
No artigo anterior, implementamos uma estratégia de hedge simples. A seguir, aprenderemos como atualizar essa estratégia. As mudanças de estratégia não serão grandes, mas os detalhes das mudanças exigem atenção. As definições de algumas partes do código mudaram e precisam ser entendidas.
A交易所->B交易所,B交易所->A交易所Desenhe a linha horizontal que aciona a propagação. Nós usamos diretamente画线类库A vantagem é que é simples e fácil de usar. Aqui também aprenderemos como usar o FMZ模版类库Função.Em seguida, vamos implementar esses designs um por um.
Tomando como exemplo a negociação spot da Binance, mude para o modo de alavancagem spot usando o códigoexchanges[i].IO, passe parâmetrostrade_normalMude para posição alavancada por posição e passetrade_super_marginMude para a posição de alavancagem total, o backtesting não é suportado. Isso só é usado em negociações reais.
existirmainAdicione a seguinte fase de preparação ao início da função:
// 切换杠杆模式
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")
}
}
}
A estratégia aqui apenas adiciona o código para alternar o modo de alavancagem moeda a moeda para o Binance spot, portanto, a troca definida nos parâmetros da estratégia é válida apenas para o Binance spot.
É muito simples usar os modelos de desenho incluídos. O modelo que usamos é chamado画线类库. Você pode pesquisar e obtê-lo diretamente na plataforma de estratégia FMZ.

Ou clique diretamente no link: https://www.fmz.com/strategy/27293 para ir para a página de cópia deste modelo.

Clique no botão para copiar esta biblioteca de modelos para sua própria biblioteca de estratégias.

Em seguida, na página de edição de políticas, você pode verificar a biblioteca de modelos necessária na coluna de modelos. Depois de marcar a caixa e salvar a política, esta política fará referência a este modelo. Aqui, explicamos brevemente o uso da biblioteca de modelos. Esta estratégia já referenciou este modelo, então não há necessidade de repetir a operação. Depois de copiar esta estratégia no Strategy Square, você poderá vê-la na coluna de modelo da página de edição de estratégia.画线类库Já citado.
Aprendemos principalmente como usar画线类库função para desenhar o gráfico.

Estamos planejandoA->BA diferença de preço,B->AA diferença de preço e a linha de gatilho da diferença de preço são desenhadas. Você precisa desenhar duas curvas (a diferença de preço atual de A para B e de B para A) e duas linhas horizontais (linhas de diferença de preço de gatilho), conforme mostrado na figura acima.
Porque precisamos projetar uma cobertura unilateral,A->BeB->AA linha de gatilho é diferente. O design do artigo anterior não pode mais ser usado.
No artigo anterior:
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
Existe apenas um gatilho de propagaçãotargetDiffPrice。
Então aqui precisamos modificar o código, primeiro modificar os parâmetros.

Em seguida, modifique o código:
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
}
Desta forma, a linha de gatilho da diferença de preço muda em relação à anteriortargetDiffPriceUm, tornou-se doistargetDiffPriceA2B、targetDiffPriceB2A。
Em seguida, você pode usar a função de desenho de linha da biblioteca de desenho de linha para desenhar esses dados no gráfico.
// 画图
$.PlotHLine(targetDiffPriceA2B, "A->B") // 该函数第一个参数是水平线在Y轴方向上的值,第二个参数是显示文本
$.PlotHLine(targetDiffPriceB2A, "B->A")
Quando a estratégia estiver em execução, um gráfico como este aparecerá.

Em seguida, desenhe a curva de spread em tempo real para evitar excesso de desenho. Coloque o código para desenhar curvas de dados de diferença de preços em tempo real na verificação de saldo.
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轴方向上的值
}
Dessa forma, com apenas 4 linhas de código de desenho, a estratégia pode exibir um gráfico quando estiver em execução.
Conforme mencionado acima, a linha de gatilho da diferença de preço foi transformada em duas, controlandoA->BO gatilho da cerca vivaB->AA proteção é acionada. Dessa forma, o algoritmo de preço da ordem anterior não pode ser usado, e o método do preço de mercado mais deslizamento é usado em seu lugar.
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
}
}
Como os preços de compra e venda são separados em dois dados, a função de hedgehedgeEle também precisa ser modificado.
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()
}
Há também alguns pequenos ajustes baseados nessas mudanças, que não vou abordar aqui. Você pode olhar o código para detalhes.
Adicione interatividade à estratégia para que ela possa modificar a linha de gatilho do spread em tempo real. Este é um requisito de design para uma estratégia semiautomática, que também é implementada aqui como uma demonstração de ensino. O design de interação da estratégia também é muito simples. Primeiro, adicione controles interativos à estratégia na página de edição de estratégia.

Dois controles são adicionados, um chamado A2B e o outro chamado B2A. Depois de inserir um valor na caixa de entrada de controle, clique no botão no lado direito da caixa de entrada. Uma instrução será enviada imediatamente para a estratégia, por exemplo: insira um valor na caixa de entrada123, cliqueA2BEste botão enviará imediatamente instruções para a estratégia.
A2B:123
Projete código de detecção e processamento de interação no código de estratégia.
// 交互
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])
}
}
}
Torne os dados da barra de status mais organizados e fáceis de observar.
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) + "`")

Backtesting é apenas um teste de estratégia e uma detecção preliminar de funções. Muitos bugs podem realmente ser testados durante o estágio de backtesting. Não há necessidade de prestar muita atenção aos resultados do backtest. A estratégia final ainda precisa ser testada em um ambiente real.


Código fonte da estratégia: https://www.fmz.com/strategy/302834