I'm trying to build out a trading system to auto trade on Binance. There have been loads of lessons and my coding skills have improved over the last 3 months. Recently, members of this forum have been so helpful in cracking the hmac signature for example.
Feel free to follow if it is useful to you, or skip
Summary
I'm at the stage now where I can manipulate returned strings to get the data out of binance that I need. Here's an example which works. The API's are only testnet APIs, so I have left there up here so that you can all see how the code is printed exactly, since having a quote mark out of place in a string knocks out the HMAC signature etc.
Also, you'll need OpenSSL 3.0 installed and added to PATH in the command prompt to make it work. Feel free to use the code below. The API documentation is here.
_SECTION_BEGIN("Test User Get Equity");
/*
Get the time (from , apikey, the BASE_URL, the url path (endpoint) and querystring.
Wallet endpoints for the query string are found at
https://binance-docs.github.io/apidocs/spot/en/#wallet-endpoints. Note that time.txt is updated on and explore autorepeat every few seconds and writen to a file, time.txt, which is then refernced by the script when needed, as below. (Yes, I know I have taken out and then replace the same string,.... but it's working for now)
time =InternetOpenURL("https://api.binance.com/api/v3/time");
if( time )
{
while( ( str = InternetReadString( time ) ) != "" )
{
extracted = StrMid(str,14,13);
printf( "%s", extracted);
}
InternetClose( time );
}
fh = fopen( "C:\\Program Files\\AmiBroker\\Brokers\\Binance\\time.txt", "w");
if( fh )
{
fputs("timestamp="+extracted, fh );
fclose( fh );
}
*/
time = fh = fopen("C:\\Program Files\\AmiBroker\\Brokers\\Binance\\time.txt","r");
if( fh )
{
str = fgets(fh);
extracted = StrMid(str,10);
printf( "%s", extracted);
fclose(fh);
}
nameofuser = ParamStr("Name of User:","TestNet");
userpath = "C:\\Program Files\\AmiBroker\\Brokers\\Binance\\"+nameofuser;
fmkdir(userpath);
apikey = ParamStr("API Key : ","rTVOlY4UilAoQyTJxllRYliPfrdIdK80jzFxV7IqxkvUevG6XBie8ZqKtw9f4mPp");
apisecret = ParamStr("API Secret : ", "wnjAuvzTt8vMrLDulLOQdOeRTCKctNYlYf0YdrleDAv6MUcVrj8cDJEPtzZfKarF");
BASE_URL = ParamStr("BASE URL of API","https://testnet.binance.vision");
url_path = ParamStr("Endpoint (include '/' at beginning) ","/api/v3/account");
query_string = ParamStr("Query String/Parameter","&recvWindow=10000×tamp=") + extracted;
/*
Write a file "instring.txt" which contains the query string.
Note that for signed APIs, at least timestamp must always be written to file but it is good to specify a recvwindow
also, after which the request should no longer be valid (in case of a delay getting the order to the broker)
Use shellexecute to hash the input of that file with the secret key and outoput that to file "outsig.txt"
*/
fh = fopen( userpath + "\\instring.txt", "w");
if( fh )
{
fputs(query_string, fh );
fclose( fh );
}
ShellExecute("openssl", " dgst -sha256 -hmac "+ apisecret +" -out outsig.txt instring.txt", userpath, 0);
/*
Pause to allow the script to complete writing "outsig.txt"
*/
ThreadSleep(40);
/*
initialise signature variable. Define signature as the output from the file "outsig.txt".
Then remove "HMAC-SHA2-256(instring.txt)= " from that string using the StrReplace command
Define the URL by concatenating the base url, the url path (endpoint), "?", the query string, then
"&signature=" and append it with your signature. Set header using the API key, then open the url, read
and print string to output.
*/
signature = "";
fh = fopen(userpath + "\\outsig.txt", "r");
if( fh )
{
if( ! feof( fh ) )
{
signature = fgets( fh );
signature = StrReplace(signature,"HMAC-SHA2-256(instring.txt)= ","");
url = BASE_URL + url_path + "?" + query_string + "&signature=" + signature;
InternetSetHeaders("X-MBX-APIKEY: " + apikey);
io = InternetOpenURL(url);
str = InternetReadString(io);
printf( "%s", str );
InternetClose( io );
}
}
fclose(fh);
ThreadSleep(100);
fh = fopen( userpath +"\\fullstring.txt", "w");
if( fh )
{
fputs(str, fh );
}
fclose( fh );
bnb = StrExtract(str,2,'{');
btc = StrExtract(str,3,'{');
busd = StrExtract(str,4,'{');
eth = StrExtract(str,5,'{');
ltc = StrExtract(str,6,'{');
trx = StrExtract(str,7,'{');
usdt = StrExtract(str,8,'{');
xrp = StrExtract(str,9,'{');
forequity = (
bnb + "\n" +
btc + "\n" +
busd + "\n" +
eth + "\n" +
ltc + "\n" +
trx + "\n" +
usdt + "\n" +
xrp
);
fh = fopen( userpath +"\\equity.txt", "w");
if( fh )
{
fputs(forequity, fh );
}
fclose( fh );
So, where are we now?
We're at the stage where we can pull data and maniplate strings to get balances of different assets in order to be able to make some kind of calcualtion of equity. The string manipulation functions in AFL are great for what I need. I just write the outputs to files and grab what I need form those files. ( and in any case, I can't use JSON parse functions because my virtual machine won't process jscripts ,...a rabbit hole I have spent 40 hours on,..... for another day perhaps).
Now, I am trying to post buy orders and sell orders. I can write a string to a .py file then execute that, provided I have the binance connector library installed. But I am looking to try an keep it all in AFL.
My attempt
Summary
_SECTION_BEGIN("Post Buy Order");
/*
Get the time (from , apikey, the BASE_URL, the url path (endpoint) and querystring.
Wallet endpoints for the query string are found at
https://binance-docs.github.io/apidocs/spot/en/#wallet-endpoints
*/
time = fh = fopen("C:\\Program Files\\AmiBroker\\Brokers\\Binance\\time.txt","r");
if( fh )
{
str = fgets(fh);
extracted = StrMid(str,10);
printf( "%s", extracted);
fclose(fh);
}
nameofuser = ParamStr("Name of User:","TestNet");
userpath = "C:\\Program Files\\AmiBroker\\Brokers\\Binance\\"+nameofuser;
fmkdir(userpath);
apikey = ParamStr("API Key : ","rTVOlY4UilAoQyTJxllRYliPfrdIdK80jzFxV7IqxkvUevG6XBie8ZqKtw9f4mPp");
apisecret = ParamStr("API Secret : ", "wnjAuvzTt8vMrLDulLOQdOeRTCKctNYlYf0YdrleDAv6MUcVrj8cDJEPtzZfKarF");
BASE_URL = ParamStr("BASE URL of API","https://testnet.binance.vision");
url_path = ParamStr("Endpoint (include '/' at beginning) ","/api/v3/order");
query_string = ParamStr("Query String/Parameter","symbol=BTCUSDT&side=BUY&type=MARKET&quantity=0.1×tamp=") + extracted;
/*
Write a file "instring.txt" which contains the query string.
Note that for signed APIs, at least timestamp must always be written to file
Use shellexecute to hash the input of that file with the secret key and outoput that to file "outsig.txt"
*/
fh = fopen( userpath + "\\instring.txt", "w");
if( fh )
{
fputs(query_string, fh );
fclose( fh );
}
ShellExecute("openssl", " dgst -sha256 -hmac "+ apisecret +" -out outsig.txt instring.txt", userpath, 0);
/*
Pause to allow the script to complete writing "outsig.txt"
*/
ThreadSleep(40);
/*
initialise signature variable. Define signature as the output from the file "outsig.txt".
Then remove "HMAC-SHA2-256(instring.txt)= " from that string using the StrReplace command
Define the URL by concatenating the base url, the url path (endpoint), "?", the query string, then
"&signature=" and append it with your signature. Set header using the API key, then open the url and post parameters with internetpostrequest, read
and print returned string to output.
*/
signature = "";
fh = fopen(userpath + "\\outsig.txt", "r");
if( fh )
{
if( ! feof( fh ) )
{
signature = fgets( fh );
signature = StrReplace(signature,"HMAC-SHA2-256(instring.txt)= ","");
url = BASE_URL + url_path + "?" + query_string + "&signature=" + signature;
printf("%s", url);
InternetSetHeaders("X-MBX-APIKEY: " + apikey);
io = InternetPostRequest(url, query_string);
str = InternetReadString(io);
printf( "%s", str );
InternetClose( io );
}
}
fclose(fh);
ThreadSleep(100);
fh = fopen( userpath +"\\fullstring.txt", "w");
if( fh )
{
fputs(str, fh );
}
fclose( fh );
This is what I get returned
and my output window...
1664168032919TestNet
C:\Program Files\AmiBroker\Brokers\Binance\TestNet
rTVOlY4UilAoQyTJxllRYliPfrdIdK80jzFxV7IqxkvUevG6XBie8ZqKtw9f4mPp
wnjAuvzTt8vMrLDulLOQdOeRTCKctNYlYf0YdrleDAv6MUcVrj8cDJEPtzZfKarF
https://testnet.binance.vision
/api/v3/order
symbol=BTCUSDT&side=BUY&type=MARKET&quantity=0.1×tamp=1664168032919
https://testnet.binance.vision/api/v3/order?symbol=BTCUSDT&side=BUY&type=MARKET&quantity=0.1×tamp=1664168032919&signature=869994ca83835c4621fb09f24034c917ef0e2be3b351f91f23fdc7e0901671e9
The contents of instring.txt are
symbol=BTCUSDT&side=BUY&type=MARKET&quantity=0.1×tamp=1664168032919
and of outsig.txt are
HMAC-SHA2-256(instring.txt)= 869994ca83835c4621fb09f24034c917ef0e2be3b351f91f23fdc7e0901671e9
I have contacted Binance and the url and the query are allegedly constructed correctly from what they can see, but they have no experience of Amibroker so don't know how to format that into a post request from internetpostrequest(). I did think I had followed the instructions, but obviously there is something I have missed.
Any advice and lessons offered are greatly appreciated as always.
Once I have it all up and working, I'll post the whole thing here so that others can make use of it.
Thanks once again.