No artigo anterior, implementamos uma estratégia de hedge simples juntos, e então vamos aprender como atualizar a estratégia. A estratégia não apresenta muitas modificações, mas os pormenores das alterações requerem atenção: as definições de alguns lugares do código foram modificadas, em comparação com as anteriores, que devem ser entendidas especificamente.
A->B
eB->A
, bem como as linhas de disparo horizontal, podemos usar diretamente ochart plot library
A vantagem é a simplicidade e a facilidade de utilização.template library
together.Em seguida, vamos realizar essas ideias de design uma a uma.
Para mudar para o modo de margem spot, use o códigoexchanges[i].IO
, importar o parâmetrotrade_normal
para mudar para margem isolada e importartrade_super_margin
para mudar para a margem cruzada, que não é suportada em backtest.
No âmbito da preparação no início domain
Função, adicionar:
// switch the margin mode
for (var i = 0 ; i < exchanges.length ; i++) { // traverse and detect all exchange objects added
if (exchanges[i].GetName() == "Binance" && marginType != 0) { // if the exchange object represented by the current index i is Binance Spot, and the parameter marginType on the strategy interface is not selected as the "common spot" option, execute the switch
if (marginType == 1) {
Log(exchanges[i].GetName(), "set to isolated margin")
exchanges[i].IO("trade_normal")
} else if (marginType == 2) {
Log(exchanges[i].GetName(), "set to cross margin")
exchanges[i].IO("trade_super_margin")
}
}
}
A estratégia aqui apenas adiciona o código para alternar o modo de margem spot do Binance Spot, então a configuração de comutação nos parâmetros da estratégia só funciona para o Binance Spot.
Usando os modelos de gráficos encapsulados é muito simples.chart plot Library
Pode procurá-lo diretamente na plataforma da Praça da FMZ.
Ou pode clicar diretamente no link:https://www.fmz.com/strategy/27293para pular para a página de cópia do modelo.
Clique no botão e você pode facilmente copiar o modelo para sua própria biblioteca de estratégia.
Em seguida, na página de edição de estratégia, você pode verificar a biblioteca de modelos a ser usada na coluna de modelo. Salve a estratégia depois de verificá-la, e a estratégia usará esse modelo. Esta é apenas uma breve descrição do uso da biblioteca de modelos. Uma vez que a estratégia já fez referência a este modelo, não há necessidade de repetir a operação. Quando você copia o código de estratégia no quadrado, você pode ver quechart plot Library
foi referenciado na barra de modelos da página de edição da estratégia.
Aqui aprendemos principalmente como utilizar as funções dochart plot library
para tramar.
Planejamos traçar os spreads deA->B
eB->A
Precisamos traçar duas curvas (atualmente, os spreads de A para B e B para A), e duas linhas horizontais (linhas de gatilho de spread), como mostrado na figura acima.
Porque queremos projetar uma cerca unilateral, as linhas de gatilho deA->B
eB->A
será diferente, e não podemos usar o desenho no artigo anterior.
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
}
Só há uma propagação desencadeada.targetDiffPrice
- Não.
Portanto, aqui precisamos modificar o código, e precisamos modificar os parâmetros primeiro.
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
}
Assim, a linha de gatilho de propagação mudou da anteriortargetDiffPrice
para dois, ou seja,targetDiffPriceA2B
etargetDiffPriceB2A
- Não.
Em seguida, você pode usar a função de gráfico gráfico da biblioteca gráfico gráfico para desenhar os dados no gráfico.
// plot
$.PlotHLine(targetDiffPriceA2B, "A->B") // the first parameter of the function is the value of the horizontal line in the Y-axis direction, and the second parameter is the display text
$.PlotHLine(targetDiffPriceB2A, "B->A")
Quando a estratégia é executada, o gráfico será exibido assim.
Em seguida, desenhe a curva de variação em tempo real; para evitar a sobrecarga, coloque o código que traça as curvas de variação em tempo real na detecção do saldo. s
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) // plot real-time spread curves
$.PlotLine("B2A", depthB.Bids[0].Price - depthA.Asks[0].Price) // the first parameter is the curve name, and the second parameter is the curve value at the current moment, that is, the value in the Y-axis direction at the current moment
}
O código de traçagem só precisa de 4 linhas para permitir a estratégia com uma exibição de gráfico durante a execução.
Como mencionado acima, o número da linha de desencadeamento do spread foi alterado para dois, que controlam, respectivamente, o desencadeador da cobertura deA->B
eB->A
. Desta forma, o algoritmo de preço da ordem anterior não pode ser utilizado, e o método de adição de preço de slide para o preço de mercado é usado em vez disso.
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPriceA2B && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A->B market condition satisfied
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("triggerA->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[1].Balance * 0.8 / priceSell, nowAccs[0].Stocks) // prompt message
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 market condition satisfied
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("triggerB->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[0].Balance * 0.8 / priceBuy, nowAccs[1].Stocks) // prompt message
hedge(exA, exB, priceBuy, priceSell, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
Uma vez que os preços de compra e venda são divididos em dois dados, os preços de venda e de compra são divididos em dois.hedge
A função também precisa de ser modificada.
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 ajustes menores baseados nestas modificações, que não serão descritos aqui.
Adicionar interação à estratégia, para que a estratégia possa modificar a linha de gatilho de propagação em tempo real. Primeiro, adicione controles interativos à estratégia na página de edição de estratégia.
Houve dois controles adicionados, um chamado A2B e o outro chamado B2A. Depois de inserir um valor na caixa de entrada de controle, clique no botão à direita da caixa de entrada. Um comando será enviado para a estratégia imediatamente, por exemplo: inserir o valor123
na caixa de entrada, clique noA2B
O botão, e um comando será enviado para a estratégia imediatamente.
A2B:123
Projetar código de detecção e processamento interativo no código de estratégia.
// interaction
var cmd = GetCommand() // every time when the loop is operated here, it will detect whether an interactive command is sent; if no, return null string
if (cmd) { // interactive command detected, such as A2B:123
Log("received command:", cmd)
var arr = cmd.split(":") // split out the interactive control name and the value in the input box; arr[0] means A2B, and arr[1] means 123
if (arr[0] == "A2B") { // judge whether the triggered interactive control is A2B
Log("modify parameterA2B,", diffAsPercentage ? "parameter of spread ratio:" : "parameter of spread:", arr[1])
if (diffAsPercentage) {
hedgeDiffPercentageB2A = parseFloat(arr[1]) // modify the spread trigger line
} else {
hedgeDiffPriceA2B = parseFloat(arr[1]) // modify the spread trigger line
}
} else if (arr[0] == "B2A") { // detected the triggered control is B2A
Log("modify parameterB2A,", diffAsPercentage ? "parameter of spread ratio:" : "parameter of spread:", arr[1])
if (diffAsPercentage) {
hedgeDiffPercentageA2B = parseFloat(arr[1])
} else {
hedgeDiffPriceB2A = parseFloat(arr[1])
}
}
}
Faça com que a exibição dos dados da barra de estado seja mais regulada e fácil de observar.
var tbl = {
"type" : "table",
"title" : "data",
"cols" : ["platform", "Currency", "frozenCurrrency", "quoteCurrency", "frozenQuoteCurrency", "triggerSpread", "currentSpread"],
"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) + "`")
O backtest é apenas um teste da estratégia, como uma função de detecção preliminar. Muitos bugs podem realmente ser testados no estágio de backtest. Não é necessário se preocupar muito com os resultados do backtest. Eventualmente, a estratégia ainda precisa ser testada no ambiente real com bots reais.
Código fonte da estratégia:https://www.fmz.com/strategy/302834