Kryptowährungsquantitativer Handel für Anfänger - Sie näher an Kryptowährungsquantitativer (8)

Schriftsteller:Lydia., Erstellt: 2022-08-10 15:02:37, Aktualisiert: 2023-09-19 21:38:29

img

Kryptowährungsquantitativer Handel für Anfänger - Sie näher an Kryptowährungsquantitativer (8)

In dem vorherigen Artikel haben wir gemeinsam eine Multi-Species-Kontrakt-Spread-Monitoring-Strategie entwickelt. In diesem Artikel werden wir diese Idee weiter verbessern. Lassen Sie uns sehen, ob diese Idee machbar ist, und führen Sie sie mit dem OKEX V5-Simulationsbot durch, um das Strategiedesign zu überprüfen. Diese Prozesse müssen auch im Prozess des programmatischen und quantitativen Handels mit Kryptowährungen erfahren werden. Ich hoffe, dass Anfänger wertvolle Erfahrungen sammeln können.

Spoiler-Alarm, die Strategie läuft, und ich bin ein wenig aufgeregt!

img

img

img

Das Gesamtdesign der Strategie wird auf die einfachste Weise umgesetzt. Obwohl die Details nicht zu anspruchsvoll sind, können Sie immer noch einige Tipps aus dem Code lernen. Der Gesamtstrategie-Code ist weniger als 400 Zeilen lang, so dass es nicht langweilig zu lesen und zu verstehen ist. Natürlich ist dies nur eine Test-DEMO, es dauert eine Weile, um es zu testen. Also was ich sagen möchte ist: Die aktuelle Strategie ist nur erfolgreich bei der Eröffnung von Positionen, und verschiedene Situationen wie das Schließen einer Position müssen getestet und verifiziert werden. BUGs im Programmdesign sind unvermeidlich, also Tests und DEBUG sind sehr wichtig!

Zurück zum Strategieentwurf, basierend auf dem Code im vorherigen Artikel, wird die Strategie hinzugefügt:

  • Design der Datenbeständigkeit (verwenden Sie die _G-Funktion, um Daten zu speichern und Daten nach dem Neustart wiederherzustellen)
  • Zusätzliche Gitterdatenstruktur für jedes überwachte CFD-Paar (verwendet zur Steuerung der Eröffnungs- und Schlusspositionen zur Absicherung)
  • Einfache Absicherungsfunktion zur Absicherung offener und geschlossener Positionen implementiert
  • Hinzugefügt wurde eine Funktion für den Erwerb von Gesamtkapital zur Berechnung des variablen Gewinns und Verlusts
  • Hinzugefügtes Statusleiste für die Anzeige der Daten.

Die Strategie ist zur Vereinfachung des Designs nur für positive Absicherung (kurze langfristige Verträge, lange kurzfristige Verträge) konzipiert. Derzeit hat der dauerhafte Vertrag (nahe Laufzeit) einen negativen Gebührensatz, der nur lange für den dauerhaften Vertrag sein kann, um zu sehen, ob er die Zinsgewinne erhöhen kann.

Lass die Strategie eine Weile laufen ~

Nach etwa 3 Tagen Tests ist die Ausbreitungsfluktuation noch möglich.

img

img

img

Hier können wir die Gewinne einiger Finanzierungsquoten sehen.

img

Teilen Sie den Quellcode der Strategie unten:

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

Öffentliche Ansprache zur Strategie:https://www.fmz.com/strategy/288559

Die Strategie verwendet eine von mir geschriebene Vorlage-Klassenbibliothek, die nicht öffentlich ist, weil sie nicht zu gut ist.

Wenn Sie interessiert sind, können Sie einen OKEX V5 Simulationsbot zum Testen verwenden. Übrigens, diese Strategie lässt sich nicht überprüfen.


Verwandt

Mehr