資源の読み込みに... 荷物...

新人,チェックアウト 暗号通貨量的な取引に連れて行きます (8)

作者: リン・ハーンニナバダス作成日:2022年4月22日 14:32:14 更新日:

新人,チェックアウト 暗号通貨量的な取引に連れて行きます (8)

前回の記事では,マルチシンボルのコントラクト・スプレッドモニタリング戦略を共同で設計しました. この記事では,このアイデアをさらに改善していきます. アイデアが実行可能かどうかを確認し,戦略設計を検証するためにOKEX V5シミュレーションボットで実行してみましょう. これらのプロセスは,暗号通貨のプログラム取引および定量取引のプロセスでも経験する必要があります. そこから貴重な経験を蓄積できると信じています.

スポイラー: 戦略が実行されていますが ちょっとワクワクします

img

img

img

戦略の全体的なデザインは,最も単純なアイデアによって実装されています.詳細処理に厳格な要件はありませんが,コードからいくつかのトリックを学ぶことができます. 戦略全体が400行未満なので,読み取るのは退屈ではありません. もちろん,これはテストのためのDEMOにすぎず,結果を確認するためにしばらく実行する必要があります. 私が言いたいのは:現在の戦略は,ポジションを開く場合にのみ成功し,実際にテストおよび検出される閉じるポジションなどのさまざまな状況があります. プログラムデザインのバグは避けられません. したがって,テストとデバッグは非常に重要です!

前回の記事のコードに基づいて 戦略設計に戻ります

  • データの持続性設計 (データを保存し,再起動後にデータを復元するために _G関数を使用する)
  • 格子データ構造を監視される各契約・スプレッドペアに追加する (ポジションを閉じるためのヘッジを制御する)
  • 簡単なヘージング機能を実装し,ヘージングによってポジションを開くか閉じる.
  • 変動利益と損失を計算するために,総資本を得る関数を追加する.
  • ステータスバーデータを輸出するいくつかの表示を追加します.

上記は追加された機能です.単純に言えば,戦略はポジティブなヘッジのみを設計しました (長期契約のためにショート;短期契約のためにロング).現在,永続契約 (短期) には負の資金調達の割合があります.永続契約で,資金調達の利回りが増加できるか確認してください.

戦略を少しの間 実行させてください.

3日ほどテストされており 波動は良好です

img

img

img

資金の利回りの一部は 次の図で見ることができます

img

戦略のソースコードは以下のとおり共有されています.

var arrNearContractType = strNearContractType.split(",")
var arrFarContractType = strFarContractType.split(",")

var nets = null
var initTotalEquity = null 
var OPEN_PLUS = 1
var COVER_PLUS = 2


function createNet(begin, diff, initAvgPrice, diffUsagePercentage) {
    if (diffUsagePercentage) {
        diff = diff * initAvgPrice
    }
    var oneSideNums = 3
    var up = []
    var down = []
    for (var i = 0 ; i < oneSideNums ; i++) {
        var upObj = {
            sell : false, 
            price : begin + diff / 2 + i * diff
        }
        up.push(upObj)

        var j = (oneSideNums - 1) - i
        var downObj = {
            sell : false,
            price : begin - diff / 2 - j * diff
        }
        if (downObj.price <= 0) {  // the price cannot be less than or equal to 0 
            continue
        }
        down.push(downObj)
    }
    return down.concat(up)
}

function createCfg(symbol) {
    var cfg = {
        extension: {
            layout: 'single', 
            height: 300,
            col: 6
        },
        title: {
            text: symbol
        },
        xAxis: {
            type: 'datetime'
        },
        series: [{
            name: 'plus',
            data: []
        }]
    }
    return cfg
}

function formatSymbol(originalSymbol) {
    var arr = originalSymbol.split("-")
    return [arr[0] + "_" + arr[1], arr[0], arr[1]]
}

function main() {	
    if (isSimulate) {
    	exchange.IO("simulate", true)  // switch to the simulated environment 
    	Log("Only support OKEX V5 API, and switch to OKEX V5 simulated bot:")
    } else {
    	exchange.IO("simulate", false)  // switch to the bot
    	Log("Only support OKEX V5 API, and switch to OKEX V5 bot:")
    }    
    if (exchange.GetName() != "Futures_OKCoin") {
    	throw "support OKEX Futures"
    }

    // initialize
    if (isReset) {
        _G(null)
        LogReset(1)
        LogProfitReset()
        LogVacuum()
        Log("reset all data", "#FF0000")
    }

    // initialize the mark 
    var isFirst = true 

    // the profit prints the period 
    var preProfitPrintTS = 0
    // the total equity 
    var totalEquity = 0
    var posTbls = []   // the array of position table 

    // declare arrCfg
    var arrCfg = []
    _.each(arrNearContractType, function(ct) {
        arrCfg.push(createCfg(formatSymbol(ct)[0]))
    })
    var objCharts = Chart(arrCfg)
    objCharts.reset()
    
    // create objects 
    var exName = exchange.GetName() + "_V5"
    var nearConfigureFunc = $.getConfigureFunc()[exName]
    var farConfigureFunc = $.getConfigureFunc()[exName]
    var nearEx = $.createBaseEx(exchange, nearConfigureFunc)
    var farEx = $.createBaseEx(exchange, farConfigureFunc)

    // write the contracts to be subscribed in advance 
    _.each(arrNearContractType, function(ct) {
        nearEx.pushSubscribeSymbol(ct)
    })
    _.each(arrFarContractType, function(ct) {
        farEx.pushSubscribeSymbol(ct)
    })

    while (true) {
        var ts = new Date().getTime()
        // obtain the market quotes 
        nearEx.goGetTickers()
        farEx.goGetTickers()
        var nearTickers = nearEx.getTickers()
        var farTickers = farEx.getTickers()  
        if (!farTickers || !nearTickers) {
            Sleep(2000)
            continue
        }

        var tbl = {
            type : "table",
            title : "long-short term spread",
            cols : ["trading pair", "long term", "shaort term", "positive hedge", "negative hedge"],
            rows : []
        }        
        
        var subscribeFarTickers = []
        var subscribeNearTickers = []
        _.each(farTickers, function(farTicker) {
            _.each(arrFarContractType, function(symbol) {
                if (farTicker.originalSymbol == symbol) {
                    subscribeFarTickers.push(farTicker)
                }
            })
        })

        _.each(nearTickers, function(nearTicker) {
            _.each(arrNearContractType, function(symbol) {
                if (nearTicker.originalSymbol == symbol) {
                    subscribeNearTickers.push(nearTicker)
                }
            })
        })

        var pairs = []        
        _.each(subscribeFarTickers, function(farTicker) {
            _.each(subscribeNearTickers, function(nearTicker) {                
                if (farTicker.symbol == nearTicker.symbol) {
                    var pair = {symbol: nearTicker.symbol, nearTicker: nearTicker, farTicker: farTicker, plusDiff: farTicker.bid1 - nearTicker.ask1, minusDiff: farTicker.ask1 - nearTicker.bid1}
                    pairs.push(pair)
                    tbl.rows.push([pair.symbol, farTicker.originalSymbol, nearTicker.originalSymbol, pair.plusDiff, pair.minusDiff])
                    for (var i = 0 ; i < arrCfg.length ; i++) {
                        if (arrCfg[i].title.text == pair.symbol) {
                            objCharts.add([i, [ts, pair.plusDiff]])
                        }                        
                    }
                }
            })
        })

        // initialize 
        if (isFirst) {
            isFirst = false 
            var recoveryNets = _G("nets")
            var recoveryInitTotalEquity = _G("initTotalEquity")
            if (!recoveryNets) {
                // detect positions 
                _.each(subscribeFarTickers, function(farTicker) {
                    var pos = farEx.getFuPos(farTicker.originalSymbol, ts)
                    if (pos.length != 0) {
                        Log(farTicker.originalSymbol, pos)
                        throw "There are positions during the initialization"
                    }
                })
                _.each(subscribeNearTickers, function(nearTicker) {
                    var pos = nearEx.getFuPos(nearTicker.originalSymbol, ts)
                    if (pos.length != 0) {
                        Log(nearTicker.originalSymbol, pos)
                        throw "There are positions during the initialization"
                    }
                })                
                // construct nets
                nets = []
                _.each(pairs, function (pair) {
                    farEx.goGetAcc(pair.farTicker.originalSymbol, ts)
                    nearEx.goGetAcc(pair.nearTicker.originalSymbol, ts)
                    var obj = {
                        "symbol" : pair.symbol, 
                        "farSymbol" : pair.farTicker.originalSymbol,
                        "nearSymbol" : pair.nearTicker.originalSymbol,
                        "initPrice" : (pair.nearTicker.ask1 + pair.farTicker.bid1) / 2, 
                        "prePlus" : pair.farTicker.bid1 - pair.nearTicker.ask1,                        
                        "net" : createNet((pair.farTicker.bid1 - pair.nearTicker.ask1), diff, (pair.nearTicker.ask1 + pair.farTicker.bid1) / 2, true), 
                        "initFarAcc" : farEx.getAcc(pair.farTicker.originalSymbol, ts), 
                        "initNearAcc" : nearEx.getAcc(pair.nearTicker.originalSymbol, ts),
                        "farTicker" : pair.farTicker,
                        "nearTicker" : pair.nearTicker,
                        "farPos" : null, 
                        "nearPos" : null,
                    }
                    nets.push(obj)
                })
                var currTotalEquity = getTotalEquity()
                if (currTotalEquity) {
                	initTotalEquity = currTotalEquity
                } else {
                	throw "Fail to obtain the total equity by initialization!"
                }                
            } else {
                // recover 
                nets = recoveryNets
                initTotalEquity = recoveryInitTotalEquity
            }
        }

        // query the grid, to detect whether a trade is triggered 
        _.each(nets, function(obj) {
            var currPlus = null
            _.each(pairs, function(pair) {
                if (pair.symbol == obj.symbol) {
                    currPlus = pair.plusDiff
                    obj.farTicker = pair.farTicker
                    obj.nearTicker = pair.nearTicker
                }
            })
            if (!currPlus) {
                Log("not detected", obj.symbol, "spread")
                return 
            }

            // examine the grid; dynamically add
            while (currPlus >= obj.net[obj.net.length - 1].price) {
                obj.net.push({
                    sell : false,
                    price : obj.net[obj.net.length - 1].price + diff * obj.initPrice,
                })
            }
            while (currPlus <= obj.net[0].price) {
                var price = obj.net[0].price - diff * obj.initPrice
                if (price <= 0) {
                    break
                }
                obj.net.unshift({
                    sell : false,
                    price : price,
                })
            }
            
            // detect grid
            for (var i = 0 ; i < obj.net.length - 1 ; i++) {
                var p = obj.net[i]
                var upP = obj.net[i + 1]
                if (obj.prePlus <= p.price && currPlus > p.price && !p.sell) {
                    if (hedge(nearEx, farEx, obj.nearSymbol, obj.farSymbol, obj.nearTicker, obj.farTicker, hedgeAmount, OPEN_PLUS)) {   // positive hedge, open positions  
                        p.sell = true 
                    }
                } else if (obj.prePlus >= p.price && currPlus < p.price && upP.sell) {
                    if (hedge(nearEx, farEx, obj.nearSymbol, obj.farSymbol, obj.nearTicker, obj.farTicker, hedgeAmount, COVER_PLUS)) {   // positive hedge, close positions 
                        upP.sell = false 
                    }
                }
            }
            obj.prePlus = currPlus  // record the spread of the time, as cache, which will be used to judge upcross or downcross for the next time  
            // add other tables to export  
        })        

        if (ts - preProfitPrintTS > 1000 * 60 * 5) {   // print every 5 minutes        
        	var currTotalEquity = getTotalEquity()
        	if (currTotalEquity) {
        		totalEquity = currTotalEquity
        		LogProfit(totalEquity - initTotalEquity, "&")   // print the dynamic profit of equity 
        	}

        	// detect positions 
        	posTbls = []  // reset and update 
            _.each(nets, function(obj) {
                var currFarPos = farEx.getFuPos(obj.farSymbol)
                var currNearPos = nearEx.getFuPos(obj.nearSymbol)
                if (currFarPos && currNearPos) {
                	obj.farPos = currFarPos
                	obj.nearPos = currNearPos
                }
                var posTbl = {
                	"type" : "table", 
                	"title" : obj.symbol, 
                	"cols" : ["contract code", "amount", "price"], 
                	"rows" : [] 
                }
                _.each(obj.farPos, function(pos) {
                    posTbl.rows.push([pos.symbol, pos.amount, pos.price])
                })  
                _.each(obj.nearPos, function(pos) {
                	posTbl.rows.push([pos.symbol, pos.amount, pos.price])
                })
                posTbls.push(posTbl)
            })

            preProfitPrintTS = ts
        }

        // display the grid 
        var netTbls = []
        _.each(nets, function(obj) {
            var netTbl = {
            	"type" : "table",
            	"title" : obj.symbol,
            	"cols" : ["grid"],
            	"rows" : []
            }
            _.each(obj.net, function(p) {
            	var color = ""
            	if (p.sell) {
            		color = "#00FF00"
            	}
            	netTbl.rows.push([JSON.stringify(p) + color])
            })
            netTbl.rows.reverse()
            netTbls.push(netTbl)
        })

        LogStatus(_D(), "total equity:", totalEquity, "initial equity:", initTotalEquity, "floating profit and loss: ", totalEquity - initTotalEquity, 
        	"\n`" + JSON.stringify(tbl) + "`" + "\n`" + JSON.stringify(netTbls) + "`" + "\n`" + JSON.stringify(posTbls) + "`")
        Sleep(interval)
    }
}

function getTotalEquity() {
    var totalEquity = null 
    var ret = exchange.IO("api", "GET", "/api/v5/account/balance", "ccy=USDT")
    if (ret) {
        try {
        	totalEquity = parseFloat(ret.data[0].details[0].eq)
        } catch(e) {
        	Log("Fail to obtain the total equity of the account!")
        	return null
        }
    }
    return totalEquity
}

function hedge(nearEx, farEx, nearSymbol, farSymbol, nearTicker, farTicker, amount, tradeType) {
    var farDirection = null
    var nearDirection = null
    if (tradeType == OPEN_PLUS) {
        farDirection = farEx.OPEN_SHORT
        nearDirection = nearEx.OPEN_LONG
    } else {
        farDirection = farEx.COVER_SHORT
        nearDirection = nearEx.COVER_LONG
    }
    var nearSymbolInfo = nearEx.getSymbolInfo(nearSymbol) 
    var farSymbolInfo = farEx.getSymbolInfo(farSymbol)
    nearAmount = nearEx.calcAmount(nearSymbol, nearDirection, nearTicker.ask1, amount * nearSymbolInfo.multiplier)
    farAmount = farEx.calcAmount(farSymbol, farDirection, farTicker.bid1, amount * farSymbolInfo.multiplier)
    if (!nearAmount || !farAmount) {
        Log(nearSymbol, farSymbol, "Wrong calculation of the order amount:", nearAmount, farAmount)
        return 
    }
    nearEx.goGetTrade(nearSymbol, nearDirection, nearTicker.ask1, nearAmount[0])
    farEx.goGetTrade(farSymbol, farDirection, farTicker.bid1, farAmount[0])
    var nearIdMsg = nearEx.getTrade()
    var farIdMsg = farEx.getTrade()
    return [nearIdMsg, farIdMsg]
}

function onexit() {
	Log("execute the onexit function", "#FF0000")
    _G("nets", nets)
    _G("initTotalEquity", initTotalEquity)
    Log("Save data:", _G("nets"), _G("initTotalEquity"))
}

img

戦略アドレス:https://www.fmz.com/strategy/288559

戦略のソースコードを少し修正することで 別のテンプレートを使用できます. 戦略のソースコードを少し修正することで,

OKEX V5のシミュレーションボットで実行できます. そうだ 戦略は裏テストできない!


もっと