Binance 多通貨自動取引戦略 API 運用ガイド

作者: リン・ハーン優しさ作成日: 2019-01-22 10:34:09,更新日:

しかし,Binance.comこの記事では,主にFMZ定量取引プラットフォームで多通貨戦略を操作する方法,そして,主に特定の知識基盤を持つユーザーのために,すべての通貨を問題なく操作するための方法について紹介します.

1. 市場 の 報じ を 入手 する

150の通貨を同時に操作したい場合,RESTプロトコルを利用して市場コートを入手するのは明らかに不適切です. 投票後,多くの時間を無駄にします. Webソケットは同時に多くの通貨に購読することはできません.Binance.com市場コートを入手するための多種戦略の問題を認識し,市場コートを総合したインターフェースを提供します.

この REST インターフェース (/api/v1/ticker/24hr) を直接使用する際には,その重量は 40 であることを注意する必要があります.これは,1 つのアクセスが通常の 40 回のアクセスに相当することを意味します.このインターフェースに 5 または 6 秒に一度アクセスしても,制限を上回ることも可能です.

このインターフェースのWebソケットバージョンにアクセスする必要があるが,膨大な量のデータにより,データは市場変化に合わせてデータをプッシュするために1秒のみ固定されていることを注意してください.数分間取引されていない一部の不人気通貨については,長い間プッシュされない場合があります.固定プッシュタイムは高周波戦略には適していませんが,一般的なマルチ通貨戦略には十分です. 特定のコードは以下のとおりです:

function main() {
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
    while (true){
        var data = client.read();
        var msg = JSON.parse(data);
        updateTicker(msg);//The updateTicker function handles market quotes and transactions, which will be introduced below.
    }
}

2. 取引 前 の 準備

Binanceには取引,最低取引額,最低取引量,価格の正確性,取引量の正確性に関する多くの制限があります. これらは事前に準備する必要があります.

Defined global variables:

var totalbtc = 0;//Total value, not necessarily btc
var baseCoin = ['BTC', 'ETH', 'BNB', 'USDT'][baseCoin_select];//Base currency selection baseCoin_select is a parameter of the drop-down box
var exceptList = Except_list_string.split(',');//Excluded currency, Except_list_string is the strategy parameter
//K line cycle selects PERIOD_M1, PERIOD_M5 is FMZ default global variable
var period = [PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H1, PERIOD_D1][period_select]
var periodSecond = [60, 300, 900, 1800, 3600, 3600*24][period_select]//The number of seconds corresponding to each cycle
var lastPeriodTime = 0;//The most recent cycle time, used to update the K line
var updateProfitTime = 0//Recently updated earnings time to update revenue
var buyList = []//buying order list
var sellList = []//selling order list
var accountInfo = {};//Used to store transaction related data lists

次のステップは accountInfo のコンテンツを改良することであり,取引対に関連するすべてのコンテンツがそこに格納されます.

if (!_G('accountInfo')){//If accountInfo is not stored in the database, reacquire the data.
    var exchangeInfo = JSON.parse(HttpQuery('https://api.binance.com/api/v1/exchangeInfo'));//Get transaction related data
    var ticker = JSON.parse(HttpQuery('https://api.binance.com/api/v1/ticker/24hr'));//First use the rest protocol to get a full amount of ticker
    var tradeSymbol = exchangeInfo.symbols.filter(function(x){return x.quoteAsset == baseCoin});//Filter the required trading pairs
    accountInfo[baseCoin] = {free:0, frozen:0, last:1, value:0};//Base currency information
    for (var i=0; i<tradeSymbol.length; i++){
        var info = tradeSymbol[i];
        if(exceptList.indexOf(info.symbol.slice(0,info.symbol.length-baseCoin.length)) >= 0){
            continue;//Filter out the currencies that was kicked out
        }
        for (var j=0; j<ticker.length; j++){
            var symbol = info.symbol.slice(0,info.symbol.length-baseCoin.length)//Currency name
            if(ticker[j].symbol.slice(ticker[j].symbol.length-baseCoin.length) == baseCoin && ticker[j].symbol == info.symbol){
                //The stored contents of the exchangeInfo and ticker
                accountInfo[symbol] = {
                    last:parseFloat(ticker[j].lastPrice), free:0, frozen:0, 
                    minQty:parseFloat(info.filters[2].minQty), minNotional:parseFloat(info.filters[3].minNotional)
                    tickerSize:parseFloat(info.filters[0].tickSize), stepSize:parseFloat(info.filters[2].stepSize),
                    ask:parseFloat(ticker[j].askPrice), bid:parseFloat(ticker[j].bidPrice), volume:parseFloat(ticker[j].quoteVolume), 
                    lowPrice:parseFloat(ticker[j].lowPrice), highPrice:parseFloat(ticker[j].highPrice),
                    priceChangePercent:parseFloat(ticker[j].priceChangePercent),
                    sellPrice:0, buyPrice:0, state:0, value:0, records:null
                }
                break;
            }
        }
    }
}else{
    accountInfo = _G('accountInfo');
}
//Automatically save accountInfo to the database when exiting
function onexit(){
    _G('accountInfo', accountInfo);
}

3. アカウントとKライン情報を更新

リアルタイム更新なしでアカウント情報機能を更新します.

function updateAccount(){
    account = exchange.GetAccount();
    if (!account){
        Log('time out');
        return;//Returning directly here is to save time, and the account information acquisition is not affected in time.
    }
    for (var i=0; i<account.Info.balances.length; i++){
        var symbol = account.Info.balances[i].asset
        //Are stored in accountInfo
        if (symbol in accountInfo){
            accountInfo[symbol].free = parseFloat(account.Info.balances[i].free);
            accountInfo[symbol].frozen = parseFloat(account.Info.balances[i].locked);
            accountInfo[symbol].value = (accountInfo[symbol].free + accountInfo[symbol].frozen)*accountInfo[symbol].last
        }
    }
}
//Update the current account total value in the selected base currency
function updateTotalBTC(){
    var btc = 0;
    for (var symbol in accountInfo){
        btc += accountInfo[symbol].value
    totalbtc = btc;
    }
}

Update the K line, the initial update can use the GetRecords function in stages, and the later update uses push data synthesis.

function initRecords(){    
    for (var symbol in accountInfo){
        if(symbol == baseCoin){continue}
        if(!accountInfo[symbol].records){
            var currency = symbol + '_' + baseCoin;
            //Switch trading pair
            exchange.IO("currency", currency)
            accountInfo[symbol].records = exchange.GetRecords(period)
            Log('Update', currency, 'K line', accountInfo[symbol].records[accountInfo[symbol].records.length-1])
            Sleep(250)//Update four per second, no limit will be reached
        }
        //Recent K-line time
        lastPeriodTime = Math.max(accountInfo[symbol].records[accountInfo[symbol].records.length-1].Time/1000, lastPeriodTime)
    }
}
//Update K line based on push ticker data
function updateRecords(msgTime){
    //If the current time is greater than the last updated cycle, it indicates that a new K line needs to be generated.
    if(parseFloat(msgTime)/1000 - lastPeriodTime > periodSecond){
        for (var symbol in accountInfo){
            if(symbol != baseCoin){
                //If the K line missing of a trading pair is too much, it will be re-acquired once, it may be that the transaction is not active, ticker did not push
                if(parseFloat(msgTime)/1000 - accountInfo[symbol].records[accountInfo[symbol].records.length-1].Time/1000 > 1.5*periodSecond){
                    var currency = symbol + '_' + baseCoin;
                    exchange.IO("currency", currency)
                    var records = exchange.GetRecords(period)
                    if(records){
                        accountInfo[symbol].records = exchange.GetRecords(period)
                    }
                    Log(symbol, 'K line is missing, regain')
                }else{
                    //Push a new K line
                    accountInfo[symbol].records.push({"Time":parseInt(lastPeriodTime + periodSecond)*1000, "Open":accountInfo[symbol].last, "High":accountInfo[symbol].last,
                    "Low":accountInfo[symbol].last, "Close":accountInfo[symbol].last, "Volume":0})
                }
            }
        }
        lastPeriodTime = lastPeriodTime + periodSecond
        Log(parseFloat(msgTime)/1000, 'Adding K line')
    }else{
        //If it is in the current K line cycle, update the current K line
        for (var symbol in accountInfo){
            if(symbol != baseCoin){
                var length = accountInfo[symbol].records.length
                accountInfo[symbol].records[length-1].Close = accountInfo[symbol].last
                accountInfo[symbol].records[length-1].Volume += accountInfo[symbol].volume
                if(accountInfo[symbol].last > accountInfo[symbol].records[length-1].High){
                    accountInfo[symbol].records[length-1].High = accountInfo[symbol].last 
                }
                else if(accountInfo[symbol].last < accountInfo[symbol].records[length-1].Low){
                    accountInfo[symbol].records[length-1].Low = accountInfo[symbol].last
                }
            }
        }
    }
}

4.Trading関連機能

//Cancel current trading pair orders
function CancelPendingOrders() {
    var orders = _C(exchange.GetOrders);
    for (var j = 0; j < orders.length; j++) {
        exchange.CancelOrder(orders[j].Id, orders[j]);
    }
}
//Cancel all trading pair orders
function cancellAll(){
    try{
        var openOrders = exchange.IO('api', 'GET', '/api/v3/openOrders');
        for (var i=0; i<openOrders.length; i++){
            var order = openOrders[i];
            var currency = order.symbol.slice(0,order.symbol.length-baseCoin.length) + '_' + baseCoin;
            exchange.IO("currency", currency);
            exchange.CancelOrder(order.orderId);
        }
    }
    catch(err){
        Log('Cancel order failed');
    }
    for (var symbol in accountInfo){
        accountInfo[symbol].state = 0;
        accountInfo[symbol].buyprice = 0;
        accountInfo[symbol].sellPrice = 0;
    }
}
//Placing the buying long order 
function toBuy(){
    //The currencies you need to buy are stored in the buyList
    if (buyList.length == 0){
        return;
    }
    for (var i=0; i<buyList.length; i++){
        var symbol =  buyList[i];
        //Slippage is the "selling price 1" plus minimum trading unit, may not be completely executed immediately, you can modify it yourself
        var buyPrice = accountInfo[symbol].ask + accountInfo[symbol].tickerSize;
        buyPrice = _N(buyPrice, parseInt((Math.log10(1.1/accountInfo[symbol].tickerSize))));//Meet price accuracy
        var currency = symbol + '_' + baseCoin;
        exchange.IO("currency", currency);//Switch trading pair
        //If you have placed an order and the price is same as this one, do not operate.
        if (accountInfo[symbol].state && accountInfo[symbol].bid == accountInfo[symbol].buyprice){
            continue;
        }else{
            //Order placed first will be cancelled first
            if (accountInfo[symbol].state == 1){
                CancelPendingOrders();
                accountInfo[symbol].state = 0;
                accountInfo[symbol].buyprice = 0;
            }
            var amount = (accountInfo[symbol].free + accountInfo[symbol].frozen)*buyPrice; //Value of existing currency
            var needBuyBTC = HoldAmount - amount;//HoldAmount is a global parameter, which require value of the hold
            var buyAmount = needBuyBTC/buyPrice;
            buyAmount = _N(scale*buyAmount, parseInt((Math.log10(1.1/accountInfo[symbol].stepSize))));//Order quantity accuracy
            //Meet minimum transaction volume and minimum transaction value requirements
            if (buyAmount > accountInfo[symbol].minQty && buyPrice*buyAmount > accountInfo[symbol].minNotional){
                if (accountInfo[baseCoin].free < buyPrice*buyAmount){return;}//Have enough base currency to buy
                var id = exchange.Buy(buyPrice, buyAmount, currency);//Final order
                if(id){
                    accountInfo[symbol].buyprice = buyPrice;
                    accountInfo[symbol].state = 1;
                }
            }
        }
        //If the buying orders are too much, it need a pause, Binance allows 10 orders every 1s maximum
        if(buyList.length > 5){
            Sleep(200)
        }
    }
}
//Placing the selling orders principles are similar to the buying orders
function toSell(){
    if (sellList.length == 0){
        return;
    }
    for (var i=0; i<sellList.length; i++){
        var currency = symbol + '_' + baseCoin;
        exchange.IO("currency", currency);
        var sellPrice = accountInfo[symbol].bid - accountInfo[symbol].tickerSize;
        sellPrice = _N(sellPrice, parseInt((Math.log10(1.1/accountInfo[symbol].tickerSize))));
        if (accountInfo[symbol].state == 1 && accountInfo[symbol].bid != accountInfo[symbol].buyprice){
            CancelPendingOrders();
            accountInfo[symbol].state = 0;
            accountInfo[symbol].sellPrice = 0;
        }
        var sellAmount = accountInfo[symbol].free;
        sellAmount = _N(Math.min(scale*sellAmount,accountInfo[symbol].free), parseInt((Math.log10(1.1/accountInfo[symbol].stepSize))));
        if (sellAmount > accountInfo[symbol].minQty && sellPrice*sellAmount > accountInfo[symbol].minNotional){
            var id = exchange.Sell(sellPrice, sellAmount, currency);
            if(id){
                accountInfo[symbol].state = 1;
                accountInfo[symbol].sellPrice = sellPrice;
            }
        }
        if(sellList.length > 5){
            Sleep(200)
        }
    }
}

5.Trading論理

取引はとても簡単です 購入・販売の通貨を buyList と sellList に押し込むだけです

function checkTrade(){
    buyList = []
    sellList = []
    for(var symbol in accountInfo){
        if(symbol == baseCoin){
            continue
        }
        var length = accountInfo[symbol].records.length
        //Simple moving average, this is a simple demonstration example, don't use it at the real market.
        var fast = TA.MA(accountInfo[symbol].records, FastPeriod)[length-1]
        var slow = TA.MA(accountInfo[symbol].records, SlowPeriod)[length-1]
        if(accountInfo[symbol].value > 2*accountInfo[symbol].minNotional && fast < 0.99*slow){
            sellList.push(symbol)
        }
        //HoldAmount strategy parameter
        if(accountInfo[symbol].value < 0.9*HoldAmount && fast > 1.01*slow){
            buyList.push(symbol)
        }
    }
}

6.ロボットのインターフェースの状態とティッカーの更新

FMZは,多くの取引通貨を表示する方法も問題である. 幸いにも,FMZ定量プラットフォームは完全なテーブル機能を提供している.また,番号で並べることも可能で,簡単で便利である.Webソケットがティカーを押すたびに,イベント駆動,取引およびさまざまな更新ロジックのために更新されます.

function updateStatus(msgTime){
    //The specific data to be displayed can be defined by itself.
    var table = {type: 'table', title: 'Position information', 
             cols: ['Currency', 'Bid', 'Ask','Last', 'Lowest price','Highest price','Price Amplitude','Volume','buying price','Selling price', 'frozen','Available','Present value'],
             rows: []};
    for (var symbol in accountInfo){
        if(symbol == baseCoin){
            var infoList = [symbol,0, 0, 1,0, 0, 0,0, 0, 0, 0, _N(accountInfo[symbol].frozen,4),_N(accountInfo[symbol].free,4), _N(accountInfo[symbol].value,5)];
        }else{
            var infoList = [symbol,accountInfo[symbol].bid, accountInfo[symbol].ask, accountInfo[symbol].last,
                        accountInfo[symbol].lowPrice, accountInfo[symbol].highPrice, accountInfo[symbol].priceChangePercent,
                        _N(accountInfo[symbol].volume,2), accountInfo[symbol].buyPrice, accountInfo[symbol].sellPrice,
                        _N(accountInfo[symbol].frozen,4),_N(accountInfo[symbol].free,4), _N(accountInfo[symbol].value,5)];
        }
        table.rows.push(infoList);
    }
    var logString = _D() + ' Net value:' + _N(totalbtc,6) + (typeof(msgTime) == 'number' ? (', Latest market time: ' + _D(msgTime)) : '') + '\n';
    logString += 'The currency to be bought:' + buyList.join(',') + ' \n';
    logString += 'The currency to be sold:' + sellList.join(',') + ' \n';
    logString += 'Currently available'+ baseCoin + ':' + _N(accountInfo[baseCoin].free,6) + ',frozen:' + _N(accountInfo[baseCoin].frozen,6)  + '\n';
    LogStatus(logString + '`' + JSON.stringify(table) + '`');//Update to robot interface
}
//Every time pushes the ticker, it is updated because of the event-driven, transactional and various update logic.
function updateTicker(msg){
    var ticker = msg;
    var msgTime = 0;
    for (var i=0; i<ticker.length; i++){
        msgTime = Math.max(msgTime, ticker[i].E);
        var symbol = ticker[i].s.slice(0,ticker[i].s.length-baseCoin.length)
        if (ticker[i].s.slice(ticker[i].s.length-baseCoin.length) == baseCoin && parseFloat(ticker[i].c) && symbol in accountInfo){
            accountInfo[symbol].last = parseFloat(ticker[i].c);
            accountInfo[symbol].volume = _N(parseFloat(ticker[i].q),1);
            accountInfo[symbol].lowPrice = parseFloat(ticker[i].l);
            accountInfo[symbol].highPrice = parseFloat(ticker[i].h);
            accountInfo[symbol].ask = parseFloat(ticker[i].a);
            accountInfo[symbol].bid = parseFloat(ticker[i].b);
            accountInfo[symbol].priceChangePercent = parseFloat(ticker[i].P);
            accountInfo[symbol].value = (accountInfo[symbol].free + accountInfo[symbol].frozen)*accountInfo[symbol].last
        }
    }
    if (Date.now() - updateProfitTime > LogProfitTime*1000){
        updateAccount();
        updateProfitTime = Date.now();//Reset revenue time
        LogProfit(totalbtc);//Update revenue
    }
    updateRecords(msgTime)//Update K line
    updateTotalBTC();//Update total market value
    updateStatus(msgTime);//Update robot status
    checkTrade()//Check which orders need to be placed
    toBuy();//placing buying order
    toSell();//placing selling order
}

7.要約

function main() {
    cancellAll();
    initRecords()
    updateAccount();
    updateTotalBTC()
    Log('Total transaction digital currency:', Object.keys(accountInfo).length-1);
    updateStatus();
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
    while (true){
        var data = client.read();
        var msg = JSON.parse(data);
        updateTicker(msg);
    }
}

8.Summary

本記事では,主に取引情報を格納する方法,ティッカーに従ってKラインを合成する方法,注文の表示方法,戦略チャート表示方法,およびティッカープッシュイベントに基づいてトレードをトリガーする方法を含む,基本的なバイナンスマルチ通貨取引フレームワークを示しています.変更およびカスタマイズできる場所がたくさんあります.全体は私の個人的な戦略から抽出されています.それはバグを意味し,特定の知識基盤を持つユーザーのみです.


もっと