Interact with Binance using API PowerShell

In this article, we are going to need more functions than the one with Get-Price in the previous post blog. Therefore to interact with your Binance account, you will have to authenticate yourself to Binance, using the API with PowerShell. Which means that after you have been through it, you then should be able to place your first order with New-Order!

As explained earlier, I have decided to be as modular as possible. This is the reason why I created many functions in PowerShell to request the API. You might organize yourself differently and unify the functions following your needs. The most important is to separate the function New-Order from each others.

Prior to this, you will have to create an APIKey and an APISecret to authenticate on Binance. Follow the few steps on the official documentation.

I use New-Order as an example in this article to demonstrate how to make an API request with authentication. If you understand the process, you can apply it to other functions such as Get-Balances, Get-AllOrders.

Understand how functions work

chematic diagram of the function
  1. Firstly, the ‘main’ function, New-Order, brings you to Get-UnixTimeStamp, which will then return a timestamp within a millisecond. In the main function, we create a variable named $QueryString that includes this timestamp.

  2. Then Get-BinanceSignature is called with the $QueryString in parameter. This function creates an object System.Security.Cryptography.HMACSHA256, append the APISecret and generate a signature which is included in the URI returned.

  3. Once the Get-binanceHeader is called, an object System.Collections.Generic.Dictionary is created, then we can add the APIKey to it, and our Header is ready.

  4. Finally, we call the last function Request-API which actually does the API request with the URI and the Header. If everything’s fine, the function returns an $Object containing the requested data

Your computer needs to be perfectly on time, make sure to automatically synchronize it within the settings.

Function Get-UnixTimeStamp

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(

    )

    return $(Get-Date (Get-Date).ToUniversalTime() -UFormat %s).replace(',', '').replace('.', '').SubString(0,13)
}

About this function

  • To request the API, you create a QueryString which contains a timestamp.

Function Get-BinanceAPISignature

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 = "ASDHFASUHDFIOUSAHlUGLULUHALUHliuhalushduauauhIUH"

    $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
}

About this function

  • You need to provide your APISecret. The one here is of course a random string.
  • If you want more information about security look at the Binance documentation.

Function Get-BinanceAPIHeader

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 = "HDAUSHF3989898hiuHGhuhI987898HiuahsduhaiusduhUIH"

    $Headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
    $Headers.Add("X-MBX-APIKEY",$APIKey)

    return $Headers
}

About this function

  • You need here to provide your APIKey. Here is a sample.

Function Request-API

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 = Curl $URI -Method $Method -Headers $Headers #-Verbose
        $ObjCustomResult = $ArrayJsonResult.Content | ConvertFrom-Json
    }
    catch{
        $streamReader = [System.IO.StreamReader]::new($_.Exception.Response.GetResponseStream())
        $ErrResp = $streamReader.ReadToEnd() | ConvertFrom-Json
        $streamReader.Close()

        $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){
            ("-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 $ObjCustomResult
}

About this function

  • When I encounter a new error, I add the error code in the switch statement for a better understanding. You may want to stop running your script if you receive an indication that you’ll be banned. You can complete this according to your issues with the error codes for Binance.

Example

See the New-Order’s article to have a complete example.

Serie of post blog Crypto Binance API PowerShell

In the next post, we will see how to use the API with PowerShell and make a first order!

  1. Retrieve prices from Binance API PowerShell
  2. Get trading info from Binance API PowerShell
  3. Interact with Binance using API PowerShell
  4. New-Order API Binance PowerShell
  5. Retrieve Binance Balance API PowerShell
  6. Open Order Binance PowerShell API

3 thoughts on “Interact with Binance using API PowerShell”

  1. You’ll have the occasional error with the way you implemented Get-UnixTimeStamp.
    From time to time, you might get a result with 12 digits instead of 13, which will fail.
    You can easily fix that by adding `PadRight(13,’0′)` just before your substring to ensure that it work no matter what.

    Here is demo code about it to demonstrate (Every 50 000 loops, I get 1 or 2 error thrown where I have a substring(0,13) failure due to that)

    “`

    for ($i = 0; $i -lt 50000; $i++) {

    $b = $null
    $b = $(Get-Date (Get-Date).ToUniversalTime() -UFormat %s).replace(‘,’, ”).replace(‘.’, ”)
    [void]$b.SubString(0, 13)
    if (!$?) {
    Throw “$b (length: $($b.Length))”
    }

    }
    “`

    Reply
    • Hi IT Franck,

      Thanks a lot for your message. I directly updated with your suggestion.

      Oh, it didn’t work I expected. Don’t have the time right now. Are we supposed to use it like this->

      $(Get-Date (Get-Date).ToUniversalTime() -UFormat %s).replace(‘,’, ”).replace(‘.’, ”).SubString(0,13).PadRight(13,0)

      Yann

      Reply
  2. Hey Yann, it’s actually the other way around.
    The way padding work is that it append the specified character (0 in our case) to the string until its length is equal to 13.

    In this particular case, we pad right as the end number need to be bigger so the ms representation of the unix timestamp remains unchanged.

    $(Get-Date (Get-Date).ToUniversalTime() -UFormat %s).replace(‘,’, ”).replace(‘.’, ”).PadRight(13, 0).SubString(0, 13)

    That being said…
    I fiddled a bit with it and came up with a better (shorter & cleaner) way that do not require you to use string manipulation at all.

    [int64]([Double]$(Get-Date (Get-Date).ToUniversalTime() -UFormat %s) * 1000)

    Short explanation of what this alternative do.
    – Get the date as a string that represent a unix timestamp in seconds (I kept your initial code)
    – Convert the string to a double then multiply it by 1000 so you get a millisecond unix timestamp
    – cast the result to int64 to get rid of the remaining decimals.

    Essentially, it performs the same work of getting a ms unix timestamp but is more direct and avoid the string manipulation phase altogether.

    Reply

Leave a Comment