Type/to search
8
Follow
1364
Followers
Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (VIII)
Discussions
Created 2021-06-18 18:24:23  Updated 2024-12-04 21:16:56
 6
 4521

img

Débutants dans le trading quantitatif dans les cercles de crypto-monnaie, veuillez jeter un œil à ceci - Vous rapprocher du trading quantitatif dans les cercles de crypto-monnaie (VIII)

Dans l'article précédent, nous avons conçu une stratégie de surveillance de la répartition des contrats multivariétés. Dans cet article, nous allons continuer à améliorer cette idée. Voyons si cette idée est réalisable et exécutons-la sur la plateforme de simulation OKEX V5 pour vérifier la conception de la stratégie. Ces processus sont également nécessaires pour le trading programmatique de devises numériques et le trading quantitatif. J'espère que les débutants pourront accumuler une expérience précieuse.

Laissez-moi d’abord vous donner un spoiler, la stratégie fonctionne, et je suis un peu excité !

img

img

img

La conception globale de la stratégie est mise en œuvre de la manière la plus simple. Bien qu'il n'y ait pas d'exigences excessives sur les détails, vous pouvez néanmoins apprendre quelques astuces à partir du code. L'ensemble du code de stratégie fait moins de 400 lignes, il n'est donc pas ennuyeux à lire et à comprendre. Bien sûr, ce n'est qu'une démo de test, et vous devez l'exécuter pendant un certain temps pour le tester. Ce que je veux dire, c'est que la stratégie actuelle ne vise qu'à ouvrir une position avec succès, et que diverses situations telles que la fermeture d'une position doivent être réellement testées et vérifiées. Les bugs sont inévitables dans la conception d'un programme, les tests et le débogage sont donc très importants !

Revenons à la conception de la stratégie, basée sur le code de l'article précédent, la stratégie est ajoutée avec :

  • Conception de la persistance des données (utilisez la fonction _G pour enregistrer les données et restaurer les données après le redémarrage)
  • Ajout d'une structure de données de grille à chaque paire de spreads de contrats surveillée (utilisée pour contrôler l'ouverture et la fermeture de la couverture)
  • Mise en œuvre d'une fonction de couverture simple pour couvrir les positions d'ouverture et de fermeture
  • Ajout d'une fonction d'acquisition totale de capitaux propres pour calculer les profits et pertes flottants
  • Ajout de l'affichage de sortie des données de la barre d'état.

Les fonctions ci-dessus sont des fonctions ajoutées. Afin de simplifier la conception, la stratégie est uniquement conçue pour la couverture positive (contrats à terme courts, contrats à court terme longs). Actuellement, le contrat perpétuel (à court terme) a un taux négatif, c'est donc le bon moment pour investir longtemps sur le contrat perpétuel pour voir si le revenu du taux peut être augmenté.

Laissez la stratégie fonctionner pendant un moment~

Après avoir testé pendant environ 3 jours, la fluctuation de la différence de prix est en fait acceptable.

img

img

img

Vous pouvez constater que le taux de financement présente certains avantages.

img

Partageons le code source de la stratégie :

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) { // 价格不能小于等于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) // 切换为模拟环境 Log("仅支持OKEX V5 API,切换为OKEX V5 模拟盘:") } else { exchange.IO("simulate", false) // 切换为实盘 Log("仅支持OKEX V5 API,切换为OKEX V5 实盘:") } if (exchange.GetName() != "Futures_OKCoin") { throw "支持OKEX期货" } // 初始化 if (isReset) { _G(null) LogReset(1) LogProfitReset() LogVacuum() Log("重置所有数据", "#FF0000") } // 初始化标记 var isFirst = true // 收益打印周期 var preProfitPrintTS = 0 // 总权益 var totalEquity = 0 var posTbls = [] // 持仓表格数组 // 声明arrCfg var arrCfg = [] _.each(arrNearContractType, function(ct) { arrCfg.push(createCfg(formatSymbol(ct)[0])) }) var objCharts = Chart(arrCfg) objCharts.reset() // 创建对象 var exName = exchange.GetName() + "_V5" var nearConfigureFunc = $.getConfigureFunc()[exName] var farConfigureFunc = $.getConfigureFunc()[exName] var nearEx = $.createBaseEx(exchange, nearConfigureFunc) var farEx = $.createBaseEx(exchange, farConfigureFunc) // 预先写入需要订阅的合约 _.each(arrNearContractType, function(ct) { nearEx.pushSubscribeSymbol(ct) }) _.each(arrFarContractType, function(ct) { farEx.pushSubscribeSymbol(ct) }) while (true) { var ts = new Date().getTime() // 获取行情数据 nearEx.goGetTickers() farEx.goGetTickers() var nearTickers = nearEx.getTickers() var farTickers = farEx.getTickers() if (!farTickers || !nearTickers) { Sleep(2000) continue } var tbl = { type : "table", title : "远期-近期差价", cols : ["交易对", "远期", "近期", "正对冲", "反对冲"], 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]]) } } } }) }) // 初始化 if (isFirst) { isFirst = false var recoveryNets = _G("nets") var recoveryInitTotalEquity = _G("initTotalEquity") if (!recoveryNets) { // 检查持仓 _.each(subscribeFarTickers, function(farTicker) { var pos = farEx.getFuPos(farTicker.originalSymbol, ts) if (pos.length != 0) { Log(farTicker.originalSymbol, pos) throw "初始化时有持仓" } }) _.each(subscribeNearTickers, function(nearTicker) { var pos = nearEx.getFuPos(nearTicker.originalSymbol, ts) if (pos.length != 0) { Log(nearTicker.originalSymbol, pos) throw "初始化时有持仓" } }) // 构造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 "初始化获取总权益失败!" } } else { // 恢复 nets = recoveryNets initTotalEquity = recoveryInitTotalEquity } } // 检索网格,检查是否触发交易 _.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("没有查询到", obj.symbol, "的差价") return } // 检查网格,动态添加 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, }) } // 检索网格 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)) { // 正对冲开仓 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)) { // 正对冲平仓 upP.sell = false } } } obj.prePlus = currPlus // 记录本次差价,作为缓存,下次用于判断上穿下穿 // 增加其它表格输出 }) if (ts - preProfitPrintTS > 1000 * 60 * 5) { // 5分钟打印一次 var currTotalEquity = getTotalEquity() if (currTotalEquity) { totalEquity = currTotalEquity LogProfit(totalEquity - initTotalEquity, "&") // 打印动态权益收益 } // 检查持仓 posTbls = [] // 重置,更新 _.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" : ["合约代码", "数量", "价格"], "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 } // 显示网格 var netTbls = [] _.each(nets, function(obj) { var netTbl = { "type" : "table", "title" : obj.symbol, "cols" : ["网格"], "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(), "总权益:", totalEquity, "初始总权益:", initTotalEquity, " 浮动盈亏:", 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("获取账户总权益失败!") 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, "下单量计算错误:", 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("执行扫尾函数", "#FF0000") _G("nets", nets) _G("initTotalEquity", initTotalEquity) Log("保存数据:", _G("nets"), _G("initTotalEquity")) }

img

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

La stratégie utilise une bibliothèque de modèles que j'ai écrite moi-même. Comme elle n'est pas bien écrite, je ne la rendrai pas publique. Le code source de la stratégie ci-dessus peut être modifié pour ne pas utiliser ce modèle.

Si vous êtes intéressé, vous pouvez configurer un disque de simulation OKEX V5 pour les tests.
Oh! Au fait, cette stratégie ne peut pas être backtestée.

Related Recommendations
Comment
All comments (6)

    时隔两年,梦总~这个策略如何设定不同交易对,不同的开仓数量

    3 years ago
    // 声明arrCfg var arrCfg = [] _.each(arrNearContractType, function(ct) { arrCfg.push(createCfg(formatSymbol(ct)[0])) })

    把hedgeAmount 这个参数也做类似这样的处理,把这个参数设计成一个字符串类似:100,200,330 。 这样100就对应第一个对冲组合,然后具体去使用就行了。

    3 years ago

    改到了币安来,但是下单那块不懂ok怎么操作的,下单还没改成功

    5 years ago

    下单那块可以自己封装一下,例子中用到了一个模板类库,没公开。

    5 years ago

    怎么可以在ok模拟盘上跑。。。我用那个插件好像不行啊。。。

    5 years ago

    什么插件 ?

    5 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)