Binance Crypto Exchange in Max via Node for Max. Work in Progress
Hi folks,
I am pretty advanced in Max but a newbie in Javascript and Node.js. Nonetheless I decided to import as many functions as I can from the Binance API inside of Max. I consider the project pretty advanced so for sure I will learn a lot of things by doing it. But I also invite you to participate be it here on on the github page I created for the project.
I appreciate any input from advanced users. I for sure will have many questions as I go along.
Here are all the details.
This is the project: https://github.com/ygreq/n4m-binance-api. For now it does not do much.
It is based on the Binance API (https://github.com/binance-exchange/binance-official-api-docs) via this node.js project (https://www.npmjs.com/package/node-binance-api). Here you can test inside npm the specific functions: https://npm.runkit.com/node-binance-api
As I am a big fan of Trello and I see that github has a nice feature where you create a board exactly like you would do in Trello, I also created a board where I or us will track our activity better. https://github.com/ygreq/n4m-binance-api/projects/1
Installation guide
Clone or download the project. You have all that you need in this repository.
Warning
There is an option in the binance.js file to write your API keys in order to use this in more advanced ways. Like buying and selling. Please be sure not to share that file in case you input your private data there. As a feature hopefully me or someone else will put that code in another file. So you would know not to share that specific file along with the rest of the project.
Thank you all and a special thank you for all the people that already helped a lot. In the future, I will try to mention in the readme.md file on github all the people that lent a hand. :)
I will ask soon enough a question :) Don't forget I am a newbie in js and learning.
ygreq
You can get better at js while doing something. The harder the better in my opinion. ;))
Now I am trying to make the part where you can call any ticker. Max patching at this step.
I have the first problem. If I write something besides what it needs to be written, the server stops working. I sent DD instead of a normal pair like BTCUSDT and this happened:
node.script: Node child process quit, will restart
node.script: Cannot read property 'DD' of undefined
Is there a way to filter out all the extra messages?
I was also looking into finding a solution for autofill. Meaning you would have all the list of pairs and when typing, it would autofill based on your search. Like how google tries to predict what you want to search for while typing.
Datamuse is quite interesting, thank you. But I'm barely managing one API ;))
There is a function to call all the available pairs. So the list is easy to do and easily update-able. Managing is the hard part. But maybe I will delay a bit this function and get to the more interesting part. Trading ;))
I always forget about the idea of building an MVP (minimal viable product). :D. I'll take a break and afterwards try to create the code for setting a stop loss and a sell order in the same time. So if a coin drops to much or touches the target to sell. A function which is not present on binance.com.
Thank you once again for your great help!
Hi guys! I am back. And I am stuck again. I am trying to create the option to have the API keys outside of the working folder. In order to do this, I would like to call them via Max, of course.
I don't know the programming terminology, but I will try to explain. I have a coll in Max where you can input the keys from a text file. So I send the keys to Node.js (for ex: key GDYYYRIEIEI473772DDJDJ). But the Max.addHandler keeps the change within the js object. How can I make it be visible to the rest of the js code so that it can change a variable outside of Max.addHandler object (in our case, the apiKey in APIKEY: apiKey, )?
Thank you, guys!
let apiKey = '<>';
let apiSecret = '<>';
Max.addHandler("key", (keyFromMax) => {
apiKey = keyFromMax;
Max.outlet(apiKey);
});
Max.addHandler("secret", (secretFromMax) => {
apiSecret = secretFromMax;
Max.outlet(apiSecret);
});
Max.outlet("API Key is: " + apiKey);
Max.outlet("API Secret is: " + apiSecret);
const binance = require('node-binance-api')().options({
APIKEY: apiKey,
APISECRET: apiSecret,
});
Thank you, RAJA! BTW, there is a great tutorial on Youtube on javascript. I looked at the free lessons there and it is actually amazing how the guy explains. And the whole course is pretty cheap imho.
https://www.youtube.com/watch?v=W6NZfCO5SIk
Thank you, RAJA! I saved your links. They are really interesting!
I implemented the feature to have teh API keys saved on a different file for safety reasons. :D
You can find the latest version on github. Let's see what next. Will be back!
Hey YGREQ,
looking at the code snippet there are a few things to adjust to get the desired outcome. The basic concept with the variables etc is completely on point, however the concept of asynchronicity might be a bit confusing. But let's get to that one by the other...
/ Adding handlers
You are correctly using the maxApi.addHandler functionality here. But keep in mind that the closure (function) is only getting called once the message is received from Max. Basically the apiKey is only set once you send the key message from your Max Patch and therefore not available to the API creation before that.
/ Promise based API calls
Not necessarily important for the problem at hand, but most of the API Functionality is based on Promises. The idea behind this is to help you with scheduling your communication/messaging between the Max and Node Process and ensuring that things have been received. How would one use that easily? Look at the following twist to a portion of your snippet, which uses async/await
Max.addHandler("key", async (keyFromMax) => {
apiKey = keyFromMax;
await Max.outlet(apiKey);
});
You can read more about async/await online and I'm sure your preferred JS tutorial website has a bunch of info on it. These two seemed like a good introduction on a first glance:
/ Instantiating the Binance API
The main culprit of your code seems to be the fact that you create the instance of the Binance API too early, as in before the API credentials have been received from Max. Keeping your current infrastructure you'd have to wrap that into a function itself and only call it once ready. There are multiple ways of course. You could keep your two dedicated messages, turn them into one for further simplifying it or or or.
Let's take the latter approach in the following:
const Binance = require("node-binance-api");
let binanceAPI;
Max.addHandler("set_credentials", async (key, secret) => {
binanceAPI = new Binance().options({
APIKEY: key,
APISecret: secret
});
await Max.outlet(`Set API credentials to key:${key}, secret:${secret}`);
// do the rest from here on
});
This should work. However, if you'd rather avoid wapping the code with all this messaging I'd propose two different ways to solve that, which could bring you back to a bit more synchronous code in the start up phase.
/ Using DotEnv
It's common to use an .env file for setting environment/configuration variables for your project. One would basically create a .env_template file to document what needs to be set and add the .env to your .gitignore. In order to make that file easily usable you can use the dotenv package, which does most of the work for you. Imagine a setup like this:
// .env file
BINANCE_API_KEY=my_key
BINANCE_API_SECRET=my_secret
// script.js
require("dotenv").config();
if (!process.env.BINANCE_API_KEY) throw new Error("Missing Binance API Key");
if (!process.env.BINANCE_API_SECRET) throw new Error("Missing Binance API Secret");
const binance = require("node-binance-api")().options({
APIKEY: process.env.BINANCE_API_KEY,
APISECRET: process.env.BINANCE_API_SECRET
});
// do the rest from here
/ Using CLI Arguments
You were saying that part of the motivation was to have the API Credentials be part of your Max Patch. One other, classic option would be to use CLI arguments or even options to pass them to your script at startup time. You could easily append them to the script start
message in the patch. A simple way would be to rely simply on order and do something like
script start <api_key> <api_secret>
In your JS script you could then use this like the following using process.argv
const [, , apiKey, apiSecret] = process.argv;
if (!apiKey) throw new Error("Missing Binance API Key");
if (!apiSecret) throw new Error("Missing Binance API Secret");
const binance = require("node-binance-api")().options({
APIKEY: apiKey,
APISECRET: apiSecret
});
This is a basic, manual approach to CLI arguments. If you prefer fancier options one could also use packages like minimist , commander or various other ones to achieve the same but it might be a bit overkill for 2 simple arguments.
Anyhow, hope this explains the async behaviour of messaging a bit and furthermore shows a few approaches on how to streamline the configuration of your script!?
Enjoy! :)
Florian
Wow, Florin! Thank you so much for the tips!
For now I went with .env and .gitignore. So simple. I would like to try all solutions as I would like to learn them all. BTW, do you recommend one specifically more than the other?
For the beginners, I've been following this tutorial. https://medium.com/@thejasonfile/using-dotenv-package-to-create-environment-variables-33da4ac4ea8f
I don't know how I will structure the Max patch in the end so for now I have different variables for each Max.addHandler that needs it. So that I don't call something else by mistake. So here comes the next bump.
Regarding this..// These orders will be executed at current market price.
var quantity = 1;
binance.marketBuy("BNBBTC", quantity);
binance.marketSell("ETHBTC", quantity);
.. how do I send from Max a new value for the quantityForMarketOrderSell in the following example? Is it with commas in the brackets besides marketOrderSellFor ? I cannot test this unfortunately as it is sensitive. :)
Max.addHandler("setMarketOrderSellFor", (marketOrderSellFor) => {
var quantityForMarketOrderSell = 1;
binance.marketSell(marketOrderSellFor, quantityForMarketOrderSell);
});
Thank you so much!!
BTW These are the objects in Max for now related to this:
The code I uploaded is redundant. I changed it.
// Placing a SELL market order
// These orders will be executed at current market price.
Max.addHandler("setSellMarketOrder", (sellMarketOrderFor, sellMarketOrderValue) => {
binance.marketSell(sellMarketOrderFor, sellMarketOrderValue);
Max.outlet("Sold: ", sellMarketOrderFor, sellMarketOrderValue);
});
// Placing a BUY market order
// These orders will be executed at current market price.
Max.addHandler("setBuyMarketOrder", (buyMarketOrderFor, buyMarketOrderValue) => {
binance.marketBuy(buyMarketOrderFor, buyMarketOrderValue);
Max.outlet("Bought: ", buyMarketOrderFor, buyMarketOrderValue);
})
But even though I can see the balance I have (meaning the API works ok), I cannot buy . Even with the normal code like
// Call a simple buy order
Max.addHandler("setBuyMarketTest", () => {
let valueTest = 0.01;
binance.marketBuy("BTCUSDT", valueTest);
Max.outlet("Bought: ", "BTCUSDT", valueTest);
});
I get the message "Bought: BTCUST 0.01, but it does not get through because I cannot not see any difference when I check the balance. I will try to figure out what is happening.
Hey, my guess is that something is going wrong in the `marketBuy` function. From the source code, it looks like `marketBuy` takes a callback function as a third argument. So you could try supplying a callback and reading the error, if any.Max.addHandler("buyTest", () => {
let valueTest = 0.01;
binance.marketBuy("BTCUSDT", valueTest, (error) => {
if (error) {
Max.post(error);
Max.outlet("buyTest: Error, check max console");
} else {
Max.outlet("buyTest", "success", valueTest);
}
}
});
What does that print?
Hey Sam! Thank you for looking into this. I uploaded your code. And I did say success, but the balance did not change. As you can see..
node.script: loaded the binance.js script
Max.outlet: Done
Max.outlet: BTC balance: 0.00000072
Max.outlet: buyTest success 0.01
Max.outlet: BTC balance: 0.00000072
Considering I can see my balance, I should be able to trade as well, I guess. I will check the API settings on binance.com to see that maybe there are limits that are activated.
I have the Enable Trading activated. So it should work. Maybe it's just an error and the function is not actually set? I disabled it and reenabled it. No avail.
2 other options from my point of view:
- Try to create a new API key
- Try to contact the creators and the community involved to see if this function really works for them
Any other ideas?
What does this mean? " SUPPORT IS NO LONGER OFFERED BY BINANCE Please do not post an issue unless it is a feature request "
via https://github.com/jaggedsoft/node-binance-api/blob/master/ISSUE_TEMPLATE.md
I am such a sucker! ;)) I forgot to deactivate a function. Because I read about it when I copied it into the code. Jeeez. Sorry, guys, and thank you for all your help!
test: false instead of test:true to deactivate sandbox mode
Now it works!
const binance = require('node-binance-api')().options({
APIKEY: process.env.BINANCE_API_KEY,
APISECRET: process.env.BINANCE_API_SECRET,
useServerTime: true, // If you get timestamp errors, synchronize to server time at startup
test: false // If you want to use sandbox mode where orders are simulated
});
Ha! Well it happens to everyone from time to time. Excited that it works.
Hey Guys, good work!
I´m a nood in .js but i think i have something interresting for you:
https://github.com/askmike/gekko
askmike! good guy and he has also some problems with nrpm or what ever...
just to give you something, maybe nice!
Cheers