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

初心者向け暗号通貨量的な取引 - 暗号通貨量的な取引に近づく (8)

作者: リン・ハーンFMZ~リディア作成日:2022年8月10日 15:02:37 更新日:2023年9月19日 21:38:29

img

初心者向け暗号通貨量的な取引 - 暗号通貨量的な取引に近づく (8)

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

スポイラー警報 戦略が実行されていて ちょっと興奮してる!

img

img

img

戦略の全体的なデザインは最もシンプルな方法で実装されています.詳細はあまり要求が高くありませんが,コードからいくつかのヒントを学ぶことができます.全体的な戦略コードは400行未満なので,読み理解するのは退屈ではありません.もちろん,これはテストデモに過ぎません.テストに時間がかかります.私が言いたいのは:現在の戦略は,ポジションを開設するときにのみ成功し,ポジションを閉じるなどのさまざまな状況がテストおよび検証する必要があります.プログラム設計におけるBUGは避けられません.したがってテストとDEBUGは非常に重要です!

前回の記事のコードに基づいた戦略設計に戻り,戦略を追加します.

  • データ持続性設計 (再起動後にデータを保存し,データを復元するために _G関数を使用)
  • 監視される各CFDペアの格子データ構造を追加 (ヘッジ開始および終了ポジションを制御するために使用)
  • オープンおよび閉鎖ポジションをヘッジするための単純なヘッジ機能を実装
  • 変動利益と損失を計算するための総株式取得関数を追加
  • ステータスバーのデータ出力表示を追加

上記は追加機能である.設計を簡素化するために,戦略はポジティブなヘッジ (短期長期契約,長期短期契約) にのみ設計されている.現在,永続契約 (近期) は負の手数料率を有しており,永続契約が利率を増加させることができるかどうかを確認するために長く待つことができる.

戦略を少しだけ

約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) {  // 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 simulation environment
    	Log("Only OKEX V5 API is supported, switch to OKEX V5 simulation bot:")
    } else {
    	exchange.IO("simulate", false)  // Switch to real bot
    	Log("Only OKEX V5 API is supported, switch to OKEX V5 simulation bot:")
    }    
    if (exchange.GetName() != "Futures_OKCoin") {
    	throw "Support OKEX futures"
    }

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

    // Initialization marker
    var isFirst = true 

    // Profit print period
    var preProfitPrintTS = 0
    // Total equity
    var totalEquity = 0
    var posTbls = []   // Position table array

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

    // Pre-write the contract that require subscriptions
    _.each(arrNearContractType, function(ct) {
        nearEx.pushSubscribeSymbol(ct)
    })
    _.each(arrFarContractType, function(ct) {
        farEx.pushSubscribeSymbol(ct)
    })

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

        var tbl = {
            type : "table",
            title : "Long term-near term spread",
            cols : ["Trading pair", "long term", "near term", "positive hedging", "negative hedging"],
            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]])
                        }                        
                    }
                }
            })
        })

        // Initialization
        if (isFirst) {
            isFirst = false 
            var recoveryNets = _G("nets")
            var recoveryInitTotalEquity = _G("initTotalEquity")
            if (!recoveryNets) {
                // Check positions
                _.each(subscribeFarTickers, function(farTicker) {
                    var pos = farEx.getFuPos(farTicker.originalSymbol, ts)
                    if (pos.length != 0) {
                        Log(farTicker.originalSymbol, pos)
                        throw "Initialized with a position"
                    }
                })
                _.each(subscribeNearTickers, function(nearTicker) {
                    var pos = nearEx.getFuPos(nearTicker.originalSymbol, ts)
                    if (pos.length != 0) {
                        Log(nearTicker.originalSymbol, pos)
                        throw "Initialized with a position"
                    }
                })                
                // 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 "Initialization to obtain total equity failed!"
                }                
            } else {
                // Recovery
                nets = recoveryNets
                initTotalEquity = recoveryInitTotalEquity
            }
        }

        // Retrieve the grid and check if the trading 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 found", obj.symbol, " 's spread")
                return 
            }

            // Check grid, add dynamically
            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,
                })
            }
            
            // Search 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 hedging opening position
                        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 hedging closing position
                        upP.sell = false 
                    }
                }
            }
            obj.prePlus = currPlus  // Record the current spread as a cache, and use it to judge whether it's above the SMA or below the SMA next time
            // Add other chart outputs
        })        

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

        	// Check positions
        	posTbls = []  // Reset, 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
        }

        // Show 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 total 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("Failed 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, "Order amount calculation error:", 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 tail function", "#FF0000")
    _G("nets", nets)
    _G("initTotalEquity", initTotalEquity)
    Log("save the data:", _G("nets"), _G("initTotalEquity"))
}

img

戦略の公開演説:https://www.fmz.com/strategy/288559

戦略は,私が書いたテンプレートクラスライブラリを使用しており,それはあまり良くないため公開されていません.上記の戦略ソースコードは,このテンプレートを使用せずに変更できます.

OKEX V5シミュレーションボットを使ってテストできます この戦略は 裏付けできない


関連性

もっと