Trader en PowerShell sur Binance !
Hello les techies !
Dernier article de la série sur l’utilisation de l’API Binance en PowerShell
Un petit récap de ce que l’on a fait :
Partie 1 : Récupérer le prix d’une crypto sur Binance en PowerShell
Partie 2 : Récupérer le solde d’un compte Binance en PowerShell
Et pour cette partie 3, c’est dans le titre ! on va trader 🙂 enfin on va passer des ordres d’achats et on va pouvoir choisir entre les 3 types d’ordres, Market, Limit et Stop Limit.
Comme Déjà dit dans les articles précédents, le code original n’est pas de moi, j’ai cependant fait quelques ajustements car celui-ci ne fonctionnait pas très bien.
En plus de ça, après ces 3 articles sur l’API de Binance en PowerShell, je m’attaque à la compilation de tout ça dans un module (avec quelques fonctionnalités en plus comme la prise en charge des ordres « test » pour ne pas faire de boulettes ^^)
Les fonctions
Si vous avez lus les articles précédents, vous allez voir que l’on reprend les mêmes fonctions de base comme l’authentification.
Ce qui va changer pour Trader en PowerShell c’est la fonction pour passer les ordres
On commence donc par créer un fichier New-Order.ps1 et on colle nos fonctions de base (je vous invite à consulter les articles précédent si vous souhaitez plus d’explications sur leurs rôles)
Il vous faudra bien entendu renseigner votre Clé API et votre Secret (pour récupérer ça sur votre compte Binance, j’en parle dans la Partie 2)
function Get-UnixTimeStamp {
<#
.SYNOPSIS
Return the timestamp in millisecond of the Unix Epoch
.DESCRIPTION
Unix Epoch started the Thursday, January 1, 1970 12:00:00 AM. The function return the number of second from that time.
.EXAMPLE
Get-UnixTimeStamp
#>
param(
)
$URL = "https://api.binance.com/api/v3/time"
$TimeStamp = Invoke-RestMethod -Uri $URL -Method get
return $TimeStamp.serverTime
}
function Get-BinanceAPISignature {
<#
.SYNOPSIS
Prepare the signature that will be sent with the API request
.DESCRIPTION
Endpoint requires sending a valid API-Key and signature
.PARAMETER QueryString
The queryString must contains the symobol, timestamp and a recvWindow
.PARAMETER EndPoint
The EndPoint you want to request
.EXAMPLE
$URI = Get-BinanceAPISignature -QueryString $QueryString -EndPoint "/api/v3/openOrders"
#>
param(
[Parameter(Mandatory = $true)]$QueryString,
[Parameter(Mandatory = $true)]$EndPoint
)
$APISecret = "Votre API Secret"
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($APISecret)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($QueryString))
$signature = [System.BitConverter]::ToString($signature).Replace('-', '').ToLower()
$URI = "https://api.binance.com$($EndPoint)?$QueryString&signature=$signature"
return $URI
}
function Get-BinanceAPIHeader {
<#
.SYNOPSIS
Prepare the header that will be sent with the API request
.DESCRIPTION
The header include your APIKey
.PARAMETER
#APIKey
.EXAMPLE
Get-BinanceAPIHeader
#>
param(
)
$APIKey = "Votre Clé API"
$Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$Headers.Add("X-MBX-APIKEY", $APIKey)
return $Headers
}
function Request-API {
<#
.SYNOPSIS
Run the CURL command with defined parameters
.DESCRIPTION
Call the API and error handling. Return the result of the request
.PARAMETER Method
Choose a method according to the EndPoint
.PARAMETER URI
This parameter needs to be obtained with Get-BinanceAPISignature
.PARAMETER Headers
This parameter needs to be obtained with Get-BinanceAPIHeaderx
.EXAMPLE
$ObjResults = Request-API -Method Get -URI $URI -Headers $Headers
#>
[cmdletbinding()]
param(
[Parameter(Mandatory = $true)][ValidateSet("POST", "GET", "DELETE")]$Method,
[Parameter(Mandatory = $true)]$URI,
[Parameter(Mandatory = $true)]$Headers
)
try {
$ArrayJsonResult = Invoke-RestMethod $URI -Method $Method -Headers $Headers #-Verbose
}
catch {
$LastError = $Error[0].ErrorDetails.Message | ConvertFrom-Json
<#
write-host "1: " $ErrResp -b Red
write-host "2: " $LastError.code -b Red
write-host "3: " $LastError.msg -b Red
#>
switch ($LastError.code) {
("-1021") { write-host "TimeStamp Is outside of the recvWindow" }
("-1105") { write-host "TimeStamp issue" }
("-1003") { write-host "Too much request, IP Banned"; break }
("-2010") { write-host "Stop price would trigger immediately or Account has too many open stop loss and/or take profit orders on the symbol." }
("-1013") { write-host "The amount is not enough for this currency or not following the step size rule for the symbol." }
("-1111") { write-host "Too many decimal check the precision required with Get-ExchangeInfo" }
}
}
return $ArrayJsonResult
}
Ensuite, et bien on va reprendre la fonction de la Partie 1, car pour les ordres Limit et Stop Limit il nous faut le prix actuel du token que l’on veut acheter ou vendre. plus précisément vous allez voir plus bas, c’est si on veut passer l’ordre en FIAT. c’est à dire en Euros ou en USDT par exemple.
C’est pour déterminer la quantité de token que l’on peut acheter avec nos FIAT.
function Get-Price {
[cmdletbinding()]
param(
[Parameter(Mandatory = $false)]
[string]$Symbol,
[Parameter(Mandatory = $false)]
[ValidateSet(0, 1, 2, 3, 4, 5, 6, 7, 8)]
[string]$Decimal = 3
)
BEGIN {
if ($PSBoundParameters.ContainsKey('Symbol')) {
$URL = "https://api.binance.com/api/v3/ticker/price?symbol=" + $Symbol
}
else {
$URL = "https://api.binance.com/api/v3/ticker/price"
}
}
PROCESS {
$Ticker = Invoke-RestMethod -Uri $URL -Method get
}
END {
if ($PSBoundParameters.ContainsKey('Symbol')) {
return [math]::Round($Ticker.Price, $Decimal)
}
else {
return $Ticker
}
}
}
Et enfin nous avons la fonction principale pour créer nos requêtes d’ordres Market, Limit ou Stop Limit
Explication et tests plus bas
function New-Order {
<#
.SYNOPSIS
Place an order to buy or sell crypto
.PARAMETER Symbol
The crypto you want to buy or sell
.PARAMETER Side
ValidateSet "BUY" or "SELL"
.PARAMETER OrderType
ValidateSet "OrderMarket" or "OrderLimit" or "OrderStopLimit"
.PARAMETER Quantity
Specifies the amount you want to spend (when buying) or receive (when selling)
.PARAMETER FiatAmount
specifies the amount you want to spend in USDT
.EXAMPLE
New-Order -Symbol BTCUSDT -Side BUY -OrderType OrderMarket -FiatAmount 20 -Verbose
New-Order -Symbol ETHEUR -Side BUY -OrderType OrderMarket -FiatAmount 10 -Verbose
.EXAMPLE
New-Order -Symbol BTCUSDT -Side BUY -OrderType OrderLimit -FiatAmount 1000 -Price 33000
.EXAMPLE
New-Order -Symbol BTCUSDT -Side BUY -OrderType OrderStopLimit -Quantity 0.002 -Price 33000 -StopPrice 36000
.EXAMPLE
New-Order -Symbol XRPUSDT -Side SELL -OrderType OrderLimit -Quantity 100 -Price 0.5
New-Order -Symbol EGLDUSDT -Side SELL -OrderType OrderMarket -Quantity 0.07 -Verbose
.EXAMPLE
New-Order -Symbol XRPUSDT -Side SELL -OrderType OrderStopLimit -Quantity 100 -Price 0.55 -StopPrice 0.5
.NOTES
Common error : The amount is not enough for this currency or not following the step size rule for the symbol.
If you have set an small Ammount, Example: min quantity for EUR is 10
#>
[cmdletbinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Symbol,
[Parameter(Mandatory = $true)]
[ValidateSet("BUY", "SELL")]
[string]$Side,
[Parameter(Mandatory = $true)]
[ValidateSet("OrderMarket", "OrderLimit", "OrderStopLimit")]
[string]$OrderType,
[Parameter(Mandatory = $true, ParameterSetName = 'quantity')]
[double]$Quantity,
[Parameter(Mandatory = $true, ParameterSetName = 'quantity2')]
[double]$FiatAmount
)
DynamicParam {
$paramDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
if ($OrderType -ne "OrderMarket") {
# price param
$attributes = New-Object -Type System.Management.Automation.ParameterAttribute
$attributes.Mandatory = $true
$attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attributes)
$dynParam1 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Price", [double], $attributeCollection)
$paramDictionary.Add("Price", $dynParam1)
}
if ($OrderType -eq "OrderStopLimit") {
# StopPrice param
$attributes2 = New-Object -Type System.Management.Automation.ParameterAttribute
$attributes2.Mandatory = $true
$attributeCollection2 = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection2.Add($attributes2)
$dynParam2 = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("StopPrice", [double], $attributeCollection2)
$paramDictionary.Add("StopPrice", $dynParam2)
}
return $paramDictionary
}
BEGIN {
# Check prerequisit
try {
Get-Command -Name Get-UnixTimeStamp -ErrorAction Stop | out-null
Get-Command -name Get-BinanceAPISignature -ErrorAction Stop | Out-Null
Get-Command -Name Get-BinanceAPIHeader -ErrorAction Stop | Out-Null
Get-Command -Name Request-API -ErrorAction Stop | Out-Null
}
catch {
Write-Host "Load Get-UnixTimeStamp, Get-BinanceAPISignature, Get-BinanceAPIHeader, Request-API first prior to laod the current script" -b red
Break
}
$TimeStamp = Get-UnixTimeStamp
# retrieve value from dyn param
if ($OrderType -ne "OrderMarket") {
$Price = $paramDictionary.values[0].value[0]
$StopPrice = $paramDictionary.values[0].value[1]
}
switch ($OrderType) {
"OrderMarket" {
if ($PSBoundParameters.ContainsKey('FiatAmount')) {
Write-Verbose "Order Market with FIAT"
$QueryString = "symbol=$Symbol&side=$Side&type=MARKET"eOrderQty=$FiatAmount×tamp=$TimeStamp&recvWindow=5000"
}
else {
Write-Verbose "Order Market with Quantity"
$QueryString = "symbol=$Symbol&side=$Side&type=MARKET&quantity=$Quantity×tamp=$TimeStamp&recvWindow=5000"
}
}
"OrderLimit" {
if ($PSBoundParameters.ContainsKey('FiatAmount')) {
$CurrentPrice = Get-Price -Symbol $Symbol -Decimal 8
$Quantity = [math]::Round($FiatAmount / $CurrentPrice, 6)
}
$QueryString = "symbol=$Symbol&side=$Side&type=LIMIT&price=$Price&timeInForce=GTC&quantity=$Quantity×tamp=$TimeStamp&recvWindow=5000"
}
"OrderStopLimit" {
if ($PSBoundParameters.ContainsKey('FiatAmount')) {
$CurrentPrice = Get-Price -Symbol $Symbol -Decimal 0
$Quantity = [math]::Round($FiatAmount / $CurrentPrice, 6)
}
$QueryString = "symbol=$Symbol&side=$Side&type=TAKE_PROFIT_LIMIT&stopPrice=$StopPrice&price=$Price&timeInForce=GTC&quantity=$Quantity×tamp=$TimeStamp&recvWindow=5000"
}
}
}
PROCESS {
$URI = Get-BinanceAPISignature -QueryString $QueryString -EndPoint "/api/v3/order"
$Headers = Get-BinanceAPIHeader
$ObjResults = $null # need to do this?
$ObjResults = Request-API -Method POST -URI $URI -Headers $Headers
}
END {
return $ObjResults
}
}
Les tests
Je vous explique le fonctionnement avec des exemples concret.
Premier cas, je souhaite acheter de l’Ether avec des Euros, je regarde donc, comme pour get-price le symbol et dans mon cas c’est ETHEUR
Ensuite je souhaite acheter donc la side sera BUY et pour le OrderType on va faire un ordre direct pour commencer donc OrderMarket
Sur le dernier Paramètre on a le choix entre Quantity et FiatAmount. Soit je définie une quantité d’Ether, 1 par exemple et donc il me faudra 2577 euros disponibles dans le portefeuille sinon: erreur ^^.
Ou alors je définie une quantité d’Euros, comme 50 euros par exemple et là j’obtiendrais 0,01940059 Ether
Tout ça dépend évidement du prix du marché à l’instant où vous passez l’ordre !
Pour 10 euros la commande sera :
New-Order -Symbol ETHEUR -Side BUY -OrderType OrderMarket -FiatAmount 10 -Verbose
Je ne vais pas tester tout les cas dans cet article, ce que je peux vous dire c’est que ça fonctionne 😉
Juste un dernier, pour vendre des EGOLD vers de l’USDT en utilisant la quantité, la commande sera :
New-Order -Symbol EGLDUSDT -Side SELL -OrderType OrderMarket -Quantity 0.07 -Verbose
Gestion des erreurs
La gestion des erreurs se fait via la fontion Request-API, elle gère certains cas.
Par contre d’autres erreurs peuvent être retounées si la quantité minimum n’est pas bonne par exemple.
Dans mes tests j’ai eu cette erreur :
The amount is not enough for this currency or not following the step size rule for the symbol.
En fait c’est parce que le montant de l’ordre n’est pas assez important.
Pour un achat en EUR le minimum est de 10 euros, donc si on essaye de passer un ordre avec -FiatAmount 5, cela ne fonctionne pas et pareil pour vendre.
Conclusion
Dans cette série sur l’API de Binance en PowerShell nous avons vu comment récupérer le prix d’un actif, comment récupérer son portefeuille spot et comment passer des ordres spot, tout ça directement depuis le terminal !
Pour ma part je publie bientôt un module qui regroupe ces fonctionnalités plus quelques autres et j’en profite aussi pour gérer la config (Clé API, Secret…)
Cela permet ensuite de créer un bot par exemple, attention quand même car il y des des limites en nombre de requêtes et d’ordres. la doc ici :
https://binance-docs.github.io/apidocs/futures/en/#limits
A+ les techies !
Zalman VE500
Voila un outil indispensable que tout technicien doit avoir avec lui, c’est ce boitier qui contient un HDD/SSD et avec le quel on va pouvoir tout simplement booter sur l’image disque que l’on souhaite (ISO, IMG). d’autres options sont aussi présentes comme la sélection du mode HDD, VCD ou les deux ainsi que le cryptage du disque et le verrouillage en écriture.
On en parle sur le blog !IODD ST400 Boîtier 2,5" / USB-C/Bootable Virtual ODD&HDD / Chiffrement AES256 Max jusqu'à 76 chiffres/Protection en écriture / 2541 (ST400/Type USB-C/Modèle Next Gen) Fabriqué en Corée
IODD Mini USB 3.0 256 Bits Secure Encrypted SSD Drive 512 Go