Les ressources ont été chargées... Je charge...

La transaction quantifiée dans le cercle monétaire est une nouveauté - qui vous rapproche de la quantification dans le cercle monétaire (8).

Auteur:L'inventeur de la quantification - un petit rêve, Créé: 2021-06-18 18:24:23, Mis à jour: 2024-12-04 21:16:56

img

La quantité de transaction dans le cercle monétaire est nouvelle et vous rapproche de la quantité de transaction dans le cercle monétaire (8).

Dans l'article précédent, nous avons conçu ensemble une stratégie de surveillance des différences de contrats multi-variétés, et dans cet article, nous continuons à perfectionner cette idée. Nous allons voir si cette idée est réalisable et vérifier la conception de la stratégie avec une simulation d'OKEX V5.

Le jeu est en train de s'écouler, un peu d'effervescence!

img

img

img

L'ensemble de la stratégie est conçue de la manière la plus simple possible. Bien que le traitement des détails ne soit pas trop exigeant, il est possible d'apprendre quelques petites astuces dans le code. La stratégie complète est inférieure à 400 lignes de code, il n'est pas ennuyeux de la lire et de la comprendre. Bien sûr, ce n'est qu'une démonstration de test.

Pour revenir à la conception de la stratégie, sur la base du code de l'article précédent, nous avons ajouté à la stratégie:

  • Conception de la permanence des données (enregistrer les données à l'aide de la fonction _G pour les récupérer après un redémarrage)
  • Une structure de données de grille est ajoutée pour chaque paire de différence de contrat surveillée (utilisée pour contrôler les positions de mise en place de la couverture)
  • Une simple fonction de couverture a été mise en place pour couvrir les positions ouvertes.
  • Ajout d'une fonction d'acquisition de gains et pertes pour calculer les gains et pertes flottants
  • Ajout d'une fenêtre de statut pour afficher les données de sortie.

Pour simplifier la conception, la stratégie est conçue pour ne faire que des couvertures positives (contrats à long terme vides, plus de contrats à court terme); les contrats permanents actuels (contracts à court terme) ont un taux négatif, il est préférable de faire des contrats permanents pour voir si vous pouvez gagner en augmentant le taux de points.

Laissez la stratégie fonctionner pendant un moment

Après avoir été testé pendant environ 3 jours, les fluctuations des prix sont toujours acceptables.

img

img

img

Les bénéfices de certains taux de financement sont visibles.

img

Le code source de la stratégie est partagé ici:

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

La stratégie est ouverte à:https://www.fmz.com/strategy/288559

La stratégie utilise une bibliothèque de modèles que j'ai écrite et qui n'est pas publiée car elle n'a pas été écrite.

Si vous êtes intéressé, vous pouvez installer un test OKEX V5. Oui, c'est vrai, cette stratégie ne peut pas être retestée.


Relationnée

Plus de

N95Deux ans après, DreamHost a mis en place cette stratégie pour définir différentes paires de transactions, différentes quantités d'ouvertures.

Chao est là.Il a été modifié en binance, mais il n'a pas compris comment fonctionne OK, et il n'a pas encore réussi.

Les organismes de l'orbiteComment faire pour exécuter sur une disquette d'analogie ok. Je n'arrive pas à utiliser ce plugin.

L'inventeur de la quantification - un petit rêveJe ne sais pas. // Déclaration arrCfg Var arrCfg = [] _.each ((arrNearContractType, function ((ct) { est le type de contrat de chaque fonction ArrCfg.push ((créerCfg ((formatSymbol ((ct) [0])) Je ne sais pas. Je ne sais pas. On fait la même chose avec le paramètre hedgeAmount, en le concevant comme une chaîne similaire à: 100, 200, 330; ainsi 100 correspond à la première combinaison de couverture, et on peut l'utiliser ensuite.

L'inventeur de la quantification - un petit rêveLe sous-titre peut être enveloppé par lui-même, dans l'exemple, une bibliothèque de modèles est utilisée et n'est pas publique.

L'inventeur de la quantification - un petit rêveQuel plug-in?