Pour les débutants, jetez un coup d'œil Je vous emmène à la négociation quantitative de crypto-monnaie (8)

Auteur:Je suis désolée., Créé: 2022-04-22 14:32:14, mis à jour:

Pour les débutants, jetez un coup d'œil Je vous emmène à la négociation quantitative de crypto-monnaie (8)

Dans l'article précédent, nous avons conçu une stratégie de surveillance de la propagation de contrats multi-symbole ensemble. Dans cet article, nous continuerons à améliorer cette idée. Voyons si l'idée est réalisable et l'exécuter avec le bot simulé OKEX V5 pour vérifier la conception de la stratégie. Ces processus doivent également être expérimentés dans le processus de trading programmé de crypto-monnaie et de trading quantitatif. J'espère que vous pourrez accumuler une expérience précieuse à partir de cela.

Spoiler: la stratégie est en cours, ce qui est un peu excitant.

img

img

img

La conception globale de la stratégie est mise en œuvre par l'idée la plus simple. Bien qu'il n'y ait pas d'exigence stricte pour le traitement des détails, vous pouvez toujours apprendre quelques astuces du code. L'ensemble de la stratégie est inférieur à 400 lignes, il n'est donc pas trop ennuyeux de la lire. Bien sûr, il ne s'agit que d'une démonstration pour le test, et nous devons l'exécuter pendant un certain temps pour voir le résultat. Ce que je veux dire, c'est: la stratégie actuelle n'a de succès que dans les positions d'ouverture, et il y a différentes situations, telles que les positions de fermeture, à tester et à détecter. Les bugs dans la conception du programme sont inévitables, donc les tests et le débogage sont très importants!

Pour revenir à la conception de la stratégie, basée sur le code du dernier article, j'ai ajouté:

  • Conception de la persistance des données (en utilisant la fonction _G pour stocker les données et les restaurer après redémarrage);
  • Ajout de la structure de données de la grille à chaque paire de contrats de spreads surveillée (pour contrôler la couverture pour fermer les positions);
  • Mise en œuvre d'une simple fonction de couverture pour ouvrir ou fermer des positions par couverture;
  • Ajout d'une fonction d'obtention du capital total pour calculer le bénéfice et la perte variables;
  • Ajout d'affichage des données de la barre d'état.

Pour être simple, la stratégie ne conçoit que la couverture positive (faire court pour le contrat à long terme; faire long pour le contrat à court terme).

Laissez la stratégie fonctionner pendant un moment.

Il a été testé pendant environ 3 jours, et les fluctuations de propagation sont en fait bonnes.

img

img

img

Une partie du rendement du taux de financement peut être vue sur l'image suivante.

img

Le code source de la stratégie est partagé comme suit:

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

Adresse de la stratégie:https://www.fmz.com/strategy/288559

La stratégie a utilisé l'un des modèles conçus par moi-même; le modèle n'est pas assez bon pour être montré ici, vous pouvez donc utiliser un autre modèle en modifiant un peu le code source de la stratégie.

Si vous êtes intéressé, vous pouvez exécuter la stratégie dans le bot simulé OKEX V5. Oh, bien sûr, la stratégie ne peut pas être testée!


Plus de