En el artículo anterior, implementamos una estrategia de cobertura simple juntos, y luego aprenderemos cómo actualizar la estrategia. La estrategia no presenta muchas modificaciones, pero los detalles de las modificaciones requieren atención: se han modificado las definiciones de algunos lugares del código, en comparación con las anteriores, que deben entenderse específicamente.
A->B
yB->A
, así como las líneas de disparo horizontal, podemos utilizar directamente elchart plot library
La ventaja es la simplicidad y facilidad de uso.template library
together.A continuación, vamos a realizar esas ideas de diseño una por una.
Para cambiar al modo de margen al contado, use el códigoexchanges[i].IO
, importar el parámetrotrade_normal
el cambio al margen aislado y la importacióntrade_super_margin
para cambiar al margen cruzado, que no es compatible en backtest. Eso solo se puede usar en bots.
En la preparación al comienzo de lamain
función, añadir:
// 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")
}
}
}
La estrategia aquí solo agrega el código para cambiar el modo de margen al contado de Binance Spot, por lo que la configuración del interruptor en los parámetros de la estrategia solo funciona para Binance Spot.
El uso de las plantillas de gráficos encapsulados es muy simple.chart plot Library
Puedes buscarlo directamente en la plataforma de la Plaza de FMZ.
O puedes hacer clic directamente en el enlace:https://www.fmz.com/strategy/27293para saltar a la página de copia de la plantilla.
Haga clic en el botón y puede copiar fácilmente la plantilla a su propia biblioteca de estrategias.
Luego, en la página de edición de la estrategia, puede verificar la biblioteca de plantillas a utilizar en la columna de la plantilla. Guarde la estrategia después de comprobarla, y la estrategia utilizará esta plantilla. Esta es solo una breve descripción del uso de la biblioteca de plantillas. Dado que la estrategia ya ha hecho referencia a esta plantilla, no es necesario repetir la operación. Cuando copia el código de estrategia en Square, puede ver quechart plot Library
se ha hecho referencia en la barra de plantillas de la página de edición de estrategias.
Aquí aprendemos principalmente cómo utilizar las funciones de lachart plot library
para planear.
Planeamos trazar las diferencias deA->B
yB->A
Necesitamos trazar dos curvas (actualmente, los spreads de A a B y B a A), y dos líneas horizontales (líneas de disparador de dispersión), como se muestra en la figura anterior.
Debido a que queremos diseñar un seto de un solo lado, las líneas de disparo deA->B
yB->A
será diferente, y no podemos usar el diseño en el artículo anterior.
En el artículo 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ólo hay una propagación activadatargetDiffPrice
¿ Qué pasa?
Por lo tanto, aquí tenemos que modificar el código, y tenemos que modificar los parámetros primero.
Luego, modifique el 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
}
Por lo tanto, la línea de disparo de propagación ha cambiado de la anteriortargetDiffPrice
a dos, es decir,targetDiffPriceA2B
ytargetDiffPriceB2A
¿ Qué pasa?
A continuación, puede utilizar la función de trazado de gráficos de la biblioteca de gráficos para dibujar los datos en el 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")
Cuando la estrategia se ejecuta, el gráfico se mostrará de esta manera.
A continuación, dibuje la curva de diferenciación en tiempo real; para evitar el dibujo excesivo, coloque el código que traza las curvas de diferenciación en tiempo real en la detección de saldo. el
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
}
El código de trazado solo necesita 4 líneas para permitir la estrategia con una pantalla de gráfico durante la ejecución.
Como se ha mencionado anteriormente, el número de líneas de activación del diferencial se ha cambiado a dos, que controlan respectivamente el activador de cobertura deA->B
yB->A
De esta manera, no se puede utilizar el algoritmo de precio de pedido anterior, y se utiliza en su lugar el método de agregar el precio de deslizamiento al precio de mercado.
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
}
}
Dado que los precios de compra y venta se dividen en dos piezas de datos, elhedge
La función también necesita 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()
}
También hay algunos ajustes menores basados en estas modificaciones, que no se describirán aquí.
Añadir interacción a la estrategia, para que la estrategia pueda modificar la línea de disparo de propagación en tiempo real. El diseño de la estrategia de interacción también es muy simple. Primero, añadir controles interactivos a la estrategia en la página de edición de la estrategia.
Se agregaron dos controles, uno llamado A2B y el otro llamado B2A. Después de introducir un valor en el cuadro de entrada de control, haga clic en el botón a la derecha del cuadro de entrada.123
En el cuadro de entrada, haga clic enA2B
botón, y una orden será enviada a la estrategia inmediatamente.
A2B:123
Diseñar código de detección y procesamiento interactivo en el código de estrategia.
// 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])
}
}
}
Hacer que la muestra de datos de la barra de estado sea más regulada y 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) + "`")
La prueba de retroceso es sólo una prueba de la estrategia, como una función de detección preliminar. Muchos errores pueden ser probados en la etapa de prueba de retroceso. No es necesario preocuparse demasiado por los resultados de la prueba de retroceso.
Código fuente de la estrategia:https://www.fmz.com/strategy/302834