
Recientemente, ha habido muchas discusiones sobre estrategias Martingala en el grupo oficial de FMZ, pero no hay muchas estrategias Martingala para contratos de moneda digital en la plataforma. Por lo tanto, aproveché esta oportunidad para diseñar una estrategia Martingala simple para futuros de criptomonedas. ¿Por qué se denomina estrategia tipo Martin? Porque los riesgos potenciales de la estrategia Martin no son pequeños, por lo que no está diseñada completamente de acuerdo con ella. Sin embargo, este tipo de estrategia aún conlleva riesgos considerables, y los parámetros de configuración de la estrategia Martingala están estrechamente relacionados con los riesgos, que no deben ignorarse.
Este artículo explica y aprende principalmente sobre el diseño de estrategias de tipo Martin. La idea de la estrategia en sí es muy clara. Como usuarios de FMZ, consideramos más el diseño de estrategias.
Al diseñar estrategias de futuros de monedas digitales, a menudo se utilizan los datos de capital total. Porque es necesario calcular los rendimientos, especialmente cuando se necesitan calcular rendimientos flotantes. Dado que las posiciones abiertas ocupan margen, las órdenes pendientes también ocupan margen. En este momento, llame a la interfaz API de la plataforma FMZexchange.GetAccount()Lo que se obtiene son los activos disponibles y los activos congelados por órdenes pendientes. De hecho, la mayoría de los mercados de futuros de monedas digitales proporcionan datos sobre el capital total, pero FMZ no encapsula uniformemente este atributo.
Por lo tanto, diseñamos funciones para obtener estos datos según diferentes intercambios:
// OKEX V5 获取总权益
function getTotalEquity_OKEX_V5() {
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 getTotalEquity_Binance() {
var totalEquity = null
var ret = exchange.GetAccount()
if (ret) {
try {
totalEquity = parseFloat(ret.Info.totalWalletBalance)
} catch(e) {
Log("获取账户总权益失败!")
return null
}
}
return totalEquity
}
En el códigototalEquityÉste es el capital total que necesitamos. Luego escribimos una función como entrada de llamada y llamamos a la función correspondiente según el nombre de intercambio.
function getTotalEquity() {
var exName = exchange.GetName()
if (exName == "Futures_OKCoin") {
return getTotalEquity_OKEX_V5()
} else if (exName == "Futures_Binance") {
return getTotalEquity_Binance()
} else {
throw "不支持该交易所"
}
}
Antes de diseñar la función principal y la lógica principal. Todavía tenemos que hacer algunos preparativos y diseñar algunas funciones auxiliares.
function cancelAll() {
while (1) {
var orders = _C(exchange.GetOrders)
if (orders.length == 0) {
break
}
for (var i = 0 ; i < orders.length ; i++) {
exchange.CancelOrder(orders[i].Id, orders[i])
Sleep(500)
}
Sleep(500)
}
}
Creo que quienes consultan con frecuencia los códigos de ejemplo de estrategia en el FMZ Strategy Square están muy familiarizados con esta función. Muchas estrategias han utilizado diseños similares. Su función es obtener la lista actual de órdenes pendientes y luego cancelarlas una por una.
function trade(distance, price, amount) {
var tradeFunc = null
if (distance == "buy") {
tradeFunc = exchange.Buy
} else if (distance == "sell") {
tradeFunc = exchange.Sell
} else if (distance == "closebuy") {
tradeFunc = exchange.Sell
} else {
tradeFunc = exchange.Buy
}
exchange.SetDirection(distance)
return tradeFunc(price, amount)
}
function openLong(price, amount) {
return trade("buy", price, amount)
}
function openShort(price, amount) {
return trade("sell", price, amount)
}
function coverLong(price, amount) {
return trade("closebuy", price, amount)
}
function coverShort(price, amount) {
return trade("closesell", price, amount)
}
Hay cuatro direcciones en el comercio de futuros: abrir una posición larga (openLong), abrir una posición corta (openShort), cerrar una posición larga (coverLong) y cerrar una posición corta (coverShort). Así que diseñamos cuatro funciones de orden para corresponder a estas operaciones. Si solo está considerando realizar un pedido, hay varios factores necesarios: dirección, precio del pedido y cantidad del pedido.
Así que también diseñamos un programa llamado:tradeLa función a manejar方向(distance)、下单价格(price)、下单量(amount)Todas las operaciones están claras.
Las llamadas de función de apertura de una posición larga (openLong), apertura de una posición corta (openShort), cierre de una posición larga (coverLong) y cierre de una posición corta (coverShort) se ejecutan en última instancia mediantetradeLa función realiza la función real, que es colocar una orden en la bolsa de futuros de acuerdo con la dirección, el precio y la cantidad establecidos.
La estrategia es muy sencilla: se utiliza el precio actual como base y se colocan órdenes de venta (posición corta) y órdenes de compra (posición larga) a una cierta distancia por encima y por debajo del precio actual. Una vez que se ejecuta un lado, todas las órdenes restantes se cancelarán y luego se colocará una nueva orden de cierre a una cierta distancia en función del precio de la posición, y se colocará una orden de aumento al precio actual actualizado, pero la orden de aumento se No duplicar la cantidad del pedido.
var buyOrderId = null
var sellOrderId = null
Luego, la opción de usar el disco de simulación OKEX_V5 está diseñada en los parámetros de la interfaz de estrategia, por lo que es necesario realizar algún procesamiento en el código:
var exName = exchange.GetName()
// 切换OKEX V5模拟盘
if (isSimulate && exName == "Futures_OKCoin") {
exchange.IO("simulate", true)
}
Los parámetros de la interfaz también incluyen una opción para restablecer toda la información, por lo que el código también debe tener el procesamiento correspondiente:
if (isReset) {
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("重置所有数据", "#FF0000")
}
Solo ejecutamos contratos perpetuos, por lo que está codificado aquí y configurado solo para contratos perpetuos.
exchange.SetContractType("swap")
Luego también debemos considerar la precisión de los precios y las cantidades de los pedidos. Si la precisión no se establece correctamente, se perderá durante el proceso de cálculo de la estrategia. Si los datos tienen muchos decimales, es fácil que el pedido se desvíe. rechazado por la interfaz de intercambio.
exchange.SetPrecision(pricePrecision, amountPrecision)
Log("设置精度", pricePrecision, amountPrecision)
Función de recuperación de datos sencilla
if (totalEq == -1 && !IsVirtual()) {
var recoverTotalEq = _G("totalEq")
if (!recoverTotalEq) {
var currTotalEq = getTotalEquity()
if (currTotalEq) {
totalEq = currTotalEq
_G("totalEq", currTotalEq)
} else {
throw "获取初始权益失败"
}
} else {
totalEq = recoverTotalEq
}
}
Si desea especificar el capital total inicial de la cuenta cuando se ejecuta la estrategia, puede configurar el parámetrototalEqSi este parámetro se establece en -1, la estrategia leerá los datos de capital total almacenados. Si no hay datos de capital total almacenados, el capital total leído actualmente se utilizará como el capital total inicial del progreso de ejecución de la estrategia. Si el total el patrimonio aumenta, es decir si ganas dinero pero tu patrimonio total es menor, significa que has perdido dinero. Si se leen los datos de capital total, continúe ejecutando usando estos datos.
while (1) { // 策略主要逻辑设计为一个死循环
var ticker = _C(exchange.GetTicker) // 首先读取当前行情信息,主要用到最新成交价
var pos = _C(exchange.GetPosition) // 读取当前持仓数据
if (pos.length > 1) { // 判断持仓数据,由于这个策略的逻辑,是不太可能同时出现多空持仓的,所以发现同时出现多空持仓就抛出错误
Log(pos)
throw "同时有多空持仓" // 抛出错误,让策略停止
}
// 根据状态而定
if (pos.length == 0) { // 根据持仓状态做出不同操作,pos.length == 0是当没有持仓时
// 未持仓了,统计一次收益
if (!IsVirtual()) {
var currTotalEq = getTotalEquity()
if (currTotalEq) {
LogProfit(currTotalEq - totalEq, "当前总权益:", currTotalEq)
}
}
buyOrderId = openLong(ticker.Last - targetProfit, amount) // 挂开多仓的买单
sellOrderId = openShort(ticker.Last + targetProfit, amount) // 挂开空仓的卖单
} else if (pos[0].Type == PD_LONG) { // 有多头持仓,挂单位置、数量有所不同
var n = 1
var price = ticker.Last
buyOrderId = openLong(price - targetProfit * n, amount)
sellOrderId = coverLong(pos[0].Price + targetProfit, pos[0].Amount)
} else if (pos[0].Type == PD_SHORT) { // 有空头持仓,挂单位置、数量有所不同
var n = 1
var price = ticker.Last
buyOrderId = coverShort(pos[0].Price - targetProfit, pos[0].Amount)
sellOrderId = openShort(price + targetProfit * n, amount)
}
if (!sellOrderId || !buyOrderId) { // 如果有一边挂单失败就取消所有挂单,重来
cancelAll()
buyOrderId = null
sellOrderId = null
continue
}
while (1) { // 挂单完成,开始监控订单
var isFindBuyId = false
var isFindSellId = false
var orders = _C(exchange.GetOrders)
for (var i = 0 ; i < orders.length ; i++) {
if (buyOrderId == orders[i].Id) {
isFindBuyId = true
}
if (sellOrderId == orders[i].Id) {
isFindSellId = true
}
}
if (!isFindSellId && !isFindBuyId) { // 检测到买卖单都成交了
cancelAll()
break
} else if (!isFindBuyId) { // 检测到买单成交
Log("买单成交")
cancelAll()
break
} else if (!isFindSellId) { // 检测到卖单成交
Log("卖单成交")
cancelAll()
break
}
LogStatus(_D())
Sleep(3000)
}
Sleep(500)
}
Se ha explicado toda la lógica y el diseño.
Dejemos que la estrategia experimente la situación del mercado el 19 de mayo.


Se puede observar que la estrategia Martingala todavía tiene ciertos riesgos.

Dirección de estrategia: https://www.fmz.com/strategy/294957
Las estrategias se utilizan principalmente para aprender, así que utilice dinero real con precaución.