
Dans l’article précédent, nous avons mis en place une stratégie de couverture simple. Nous allons maintenant découvrir comment mettre à niveau cette stratégie. Les changements de stratégie ne seront pas majeurs, mais les détails des changements nécessitent une attention particulière. Les définitions de certaines parties du code ont changé par rapport à avant et doivent être comprises.
A交易所->B交易所,B交易所->A交易所Tracez la ligne horizontale qui déclenche la propagation. Nous utilisons directement画线类库L’avantage est qu’il est simple et facile à utiliser. Ici, nous allons également apprendre à utiliser FMZ模版类库Fonction.Ensuite, implémentons ces conceptions une par une.
En prenant comme exemple le trading spot de Binance, passez au mode effet de levier spot à l’aide du codeexchanges[i].IO, passer des paramètrestrade_normalPassez à un effet de levier position par position et transmetteztrade_super_marginPassez à la position complète à effet de levier, le backtesting n’est pas pris en charge. Ceci n’est utilisé que dans le trading réel.
existermainLa phase de préparation au début de la fonction est augmentée :
// 切换杠杆模式
for (var i = 0 ; i < exchanges.length ; i++) { // 遍历检测所有添加的交易所对象
if (exchanges[i].GetName() == "Binance" && marginType != 0) { // 如果当前i索引代表的交易所对象是币安现货,并且策略界面参数marginType选择的不是「普通币币」选项,执行切换
if (marginType == 1) {
Log(exchanges[i].GetName(), "设置为杠杆逐仓")
exchanges[i].IO("trade_normal")
} else if (marginType == 2) {
Log(exchanges[i].GetName(), "设置为杠杆全仓")
exchanges[i].IO("trade_super_margin")
}
}
}
La stratégie ici ajoute uniquement le code permettant de changer le mode de levier coin-to-coin pour Binance spot, de sorte que le commutateur défini dans les paramètres de stratégie n’est valable que pour Binance spot.
Il est très simple d’utiliser les modèles de dessin fournis. Le modèle que nous utilisons s’appelle画线类库. Vous pouvez le rechercher et l’obtenir directement sur le carré stratégique de la plateforme FMZ.

Ou cliquez directement sur le lien : https://www.fmz.com/strategy/27293 pour accéder à la page de copie de ce modèle.

Cliquez sur le bouton pour copier cette bibliothèque de modèles dans votre propre bibliothèque de stratégies.

Ensuite, sur la page d’édition des politiques, vous pouvez vérifier la bibliothèque de modèles dont vous avez besoin dans la colonne modèle. Après avoir coché la case et enregistré la politique, cette politique fera référence à ce modèle. Nous expliquons ici brièvement l’utilisation de la bibliothèque de modèles. Cette stratégie a déjà référencé ce modèle, il n’est donc pas nécessaire de répéter l’opération. Après avoir copié cette stratégie dans Strategy Square, vous pouvez la voir dans la colonne modèle de la page d’édition de stratégie.画线类库Déjà cité.
Nous apprenons principalement à utiliser画线类库fonction pour dessiner le graphique.

Nous prévoyons deA->BLa différence de prix,B->ALa différence de prix et la ligne de déclenchement de la différence de prix sont tracées. Vous devez tracer deux courbes (la différence de prix actuelle de A à B et de B à A) et deux lignes horizontales (lignes de différence de prix de déclenchement), comme indiqué dans la figure ci-dessus.
Parce que nous devons concevoir une haie unilatérale,A->BetB->ALa ligne de déclenchement est différente. Le design de l’article précédent ne peut plus être utilisé.
Dans l’article précédent :
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
Il n’y a qu’un seul spread de déclenchementtargetDiffPrice。
Donc ici, nous devons modifier le code, d’abord modifier les paramètres.

Modifiez ensuite le code :
var targetDiffPriceA2B = hedgeDiffPriceA2B
var targetDiffPriceB2A = hedgeDiffPriceB2A
if (diffAsPercentage) {
targetDiffPriceA2B = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentageA2B
targetDiffPriceB2A = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentageB2A
}
De cette façon, la ligne de déclenchement de la différence de prix change par rapport à la précédentetargetDiffPriceUn, devenu deuxtargetDiffPriceA2B、targetDiffPriceB2A。
Ensuite, vous pouvez utiliser la fonction de dessin de lignes de la bibliothèque de dessins de lignes pour dessiner ces données sur le graphique.
// 画图
$.PlotHLine(targetDiffPriceA2B, "A->B") // 该函数第一个参数是水平线在Y轴方向上的值,第二个参数是显示文本
$.PlotHLine(targetDiffPriceB2A, "B->A")
Lorsque la stratégie est en cours d’exécution, un graphique comme celui-ci apparaîtra.

Ensuite, dessinez la courbe de spread en temps réel pour éviter de trop dessiner. Placez le code permettant de tracer des courbes de données de différence de prix en temps réel dans le contrôle de solde.
if (ts - lastKeepBalanceTS > keepBalanceCyc * 1000) {
nowAccs = _C(updateAccs, exchanges)
var isBalance = keepBalance(initAccs, nowAccs, [depthA, depthB])
cancelAll()
if (isBalance) {
lastKeepBalanceTS = ts
if (isTrade) {
var nowBalance = _.reduce(nowAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
var initBalance = _.reduce(initAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
LogProfit(nowBalance - initBalance, nowBalance, initBalance, nowAccs)
isTrade = false
}
}
$.PlotLine("A2B", depthA.Bids[0].Price - depthB.Asks[0].Price) // 画实时差价曲线
$.PlotLine("B2A", depthB.Bids[0].Price - depthA.Asks[0].Price) // 第一个参数是曲线名称,第二个参数是曲线当前时刻的值,即当前时刻Y轴方向上的值
}
De cette façon, avec seulement 4 lignes de code de dessin, la stratégie peut afficher un graphique lorsqu’elle est en cours d’exécution.
Comme mentionné ci-dessus, la ligne de déclenchement de la différence de prix a été transformée en deux, contrôlantA->BLe déclencheur de haieB->ALa haie est déclenchée. De cette façon, l’algorithme du prix de l’ordre précédent ne peut pas être utilisé, et la méthode du prix du marché plus le glissement est utilisée à la place.
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPriceA2B && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B 盘口条件满足
var priceSell = depthA.Bids[0].Price - slidePrice
var priceBuy = depthB.Asks[0].Price + slidePrice
var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance * 0.8 / priceSell > minHedgeAmount) {
amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance * 0.8 / priceSell, maxHedgeAmount)
Log("触发A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[1].Balance * 0.8 / priceSell, nowAccs[0].Stocks) // 提示信息
hedge(exB, exA, priceBuy, priceSell, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
} else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPriceB2A && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A 盘口条件满足
var priceBuy = depthA.Asks[0].Price + slidePrice
var priceSell = depthB.Bids[0].Price - slidePrice
var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance * 0.8 / priceBuy > minHedgeAmount) {
amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance * 0.8 / priceBuy, maxHedgeAmount)
Log("触发B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[0].Balance * 0.8 / priceBuy, nowAccs[1].Stocks) // 提示信息
hedge(exA, exB, priceBuy, priceSell, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Étant donné que les prix d’achat et de vente sont séparés en deux données, la fonction de couverturehedgeIl faut également le modifier.
function hedge(buyEx, sellEx, priceBuy, priceSell, amount) {
var buyRoutine = buyEx.Go("Buy", priceBuy, amount)
var sellRoutine = sellEx.Go("Sell", priceSell, amount)
Sleep(500)
buyRoutine.wait()
sellRoutine.wait()
}
Il y a également quelques ajustements mineurs basés sur ces changements, que je n’aborderai pas ici. Vous pouvez consulter le code pour plus de détails.
Ajoutez de l’interactivité à la stratégie afin que celle-ci puisse modifier la ligne de déclenchement du spread en temps réel. Il s’agit d’une exigence de conception pour une stratégie semi-automatique, qui est également mise en œuvre ici comme démonstration pédagogique. La conception des interactions de stratégie est également très simple. Tout d’abord, ajoutez des contrôles interactifs à la stratégie sur la page d’édition de stratégie.

Deux contrôles sont ajoutés, l’un appelé A2B et l’autre appelé B2A. Après avoir entré une valeur dans la zone de saisie de contrôle, cliquez sur le bouton situé à droite de la zone de saisie. Une instruction sera immédiatement envoyée à la stratégie, par exemple : saisir une valeur dans la zone de saisie123, cliquezA2BCe bouton enverra immédiatement des instructions à la stratégie.
A2B:123
Concevez le code de détection et de traitement des interactions dans le code de stratégie.
// 交互
var cmd = GetCommand() // 每次循环执行到这里时,都检测有没有交互指令过来,没有则返回空字符串
if (cmd) { // 检测到有交互指令,例如:A2B:123
Log("接收到命令:", cmd)
var arr = cmd.split(":") // 拆分出交互控件名称和输入框中的值,arr[0]就是A2B,arr[1]就是123
if (arr[0] == "A2B") { // 判断触发的交互控件是不是A2B
Log("修改A2B的参数,", diffAsPercentage ? "参数为差价百分比" : "参数为差价:", arr[1])
if (diffAsPercentage) {
hedgeDiffPercentageB2A = parseFloat(arr[1]) // 修改触发差价线
} else {
hedgeDiffPriceA2B = parseFloat(arr[1]) // 修改触发差价线
}
} else if (arr[0] == "B2A") { // 检测到触发的控件是B2A
Log("修改B2A的参数,", diffAsPercentage ? "参数为差价百分比" : "参数为差价:", arr[1])
if (diffAsPercentage) {
hedgeDiffPercentageA2B = parseFloat(arr[1])
} else {
hedgeDiffPriceB2A = parseFloat(arr[1])
}
}
}
Rendez les données de la barre d’état plus organisées et plus faciles à observer.
var tbl = {
"type" : "table",
"title" : "数据",
"cols" : ["交易所", "币", "冻结币", "计价币", "冻结计价币", "触发差价", "当前差价"],
"rows" : [],
}
tbl.rows.push(["A:" + exA.GetName(), nowAccs[0].Stocks, nowAccs[0].FrozenStocks, nowAccs[0].Balance, nowAccs[0].FrozenBalance, "A->B:" + targetDiffPriceA2B, "A->B:" + (depthA.Bids[0].Price - depthB.Asks[0].Price)])
tbl.rows.push(["B:" + exB.GetName(), nowAccs[1].Stocks, nowAccs[1].FrozenStocks, nowAccs[1].Balance, nowAccs[1].FrozenBalance, "B->A:" + targetDiffPriceB2A, "B->A:" + (depthB.Bids[0].Price - depthA.Asks[0].Price)])
LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")

Le backtesting n’est qu’un test de stratégie et une détection préliminaire des fonctions. De nombreux bugs peuvent en fait être testés lors de la phase de backtesting. Il n’est pas nécessaire de prêter trop d’attention aux résultats des backtests. La stratégie finale doit encore être testée dans un environnement réel.


Code source de la stratégie : https://www.fmz.com/strategy/302834