
¿Alguna vez has pensado que puedes comenzar fácilmente con el trading cuantitativo y comenzar de inmediato sin tener que quedarte despierto toda la noche escribiendo código para construir un marco, diseñar la interfaz de usuario y varios detalles y mecanismos de diseño tú mismo? Todo es posible en la plataforma cuantitativa FMZ. No necesita conocimientos avanzados de programación ni preocuparse por procesos de implementación complicados: todo lo que necesita es una computadora y una cuenta para comenzar su viaje cuantitativo “a cualquier parte”. Este artículo lo guiará desde cero, lo ayudará a comenzar rápidamente con FMZ, a experimentar el encanto del comercio automatizado y a utilizar datos y estrategias para dominar el ritmo del mercado. Ya sea que sea un principiante o un veterano que busca mejorar la eficiencia, vale la pena intentar este viaje.
A menudo me comunico y charlo con principiantes de la plataforma. Los principiantes en el trading cuantitativo suelen estar confundidos por el proceso de diseño completo. Cuando tengo ideas comerciales, a menudo no sé por dónde empezar y me siento abrumado.
Confundido acerca de:
Resolvamos juntos la confusión anterior.
En el mundo del trading cuantitativo, el diseño de estrategias es a menudo un viaje de exploración sin fin. Es posible que haya intentado escribir indicadores o seguir ciegamente señales de compra y venta, pero los que realmente pueden llegar lejos son aquellos sistemas de estrategia que pueden ser “visibles, ajustables y estables”. Basándose en la plataforma cuantitativa FMZ, usted podrá tener una experiencia práctica de “llegar a tiempo”. Construya una estrategia simple, desde la configuración de parámetros, la visualización de gráficos, hasta las funciones interactivas y el cálculo de ganancias y pérdidas, para cumplir completamente con los requisitos de diseño de una estrategia.
La idea de la estrategia es una estrategia de aumento de posición paso a paso basada en ATR, lógica de construcción de posición de cuadrícula paso a paso (larga y corta bidireccional), cálculo de volatilidad adaptativa de ATR y lógica de liquidación de posición (cuando el mercado revierte al eje central).
Esta estrategia se basa en los siguientes requisitos de diseño:
Configurar dos matrices para controlar el aumento gradual de posiciones.
var arrUp = null
var arrDown = null
Cada vez que se agrega una posición, la información de la posición se introduce en la matriz, lo que facilita el control de la posición y la visualización de los datos en la interfaz de estrategia en tiempo real.
Abrir y cerrar posiciones de acuerdo al nivel de ruptura del precio. Para simplificar, tanto las posiciones de apertura como las de cierre utilizan órdenes de mercado, que son simples y efectivas.
if (close > up && i >= arrUp.length && !isPaused) {
var id = exchange.CreateOrder(symbol, "sell", -1, tradeAmount)
if (!id) {
Log("下单失败")
continue
}
arrUp.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
_G("arrUp", arrUp)
arrSignal.push([r[r.length - 1].Time, "short", close, tradeAmount])
Log([r[r.length - 1].Time, "short", close, tradeAmount], "@")
} else if (close < down && i >= arrDown.length && !isPaused) {
var id = exchange.CreateOrder(symbol, "buy", -1, tradeAmount)
if (!id) {
Log("下单失败")
continue
}
arrDown.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
_G("arrDown", arrDown)
arrSignal.push([r[r.length - 1].Time, "long", close, tradeAmount])
Log([r[r.length - 1].Time, "long", close, tradeAmount], "@")
} else if (((arrUp.length > 0 && close < mid) || (arrDown.length > 0 && close > mid)) && !isPaused) {
clear(pos, r)
}
Limpia el inventario y utiliza una función para gestionarlo. Algunas estructuras de datos deben restablecerse cada vez que se borra el inventario, por lo que la función de borrado debe encapsularse en una función para su reutilización en el módulo interactivo.
function clear(positions, r) {
var close = r[r.length - 1].Close
for (var p of positions) {
if (p.Type == PD_LONG) {
var id = exchange.CreateOrder(symbol, "closebuy", -1, p.Amount)
if (!id) {
Log("下单失败")
continue
}
arrSignal.push([r[r.length - 1].Time, "closelong", close, p.Amount])
Log([r[r.length - 1].Time, "closelong", close, p.Amount], "@")
} else if (p.Type == PD_SHORT) {
var id = exchange.CreateOrder(symbol, "closesell", -1, p.Amount)
if (!id) {
Log("下单失败")
continue
}
arrSignal.push([r[r.length - 1].Time, "closeshort", close, p.Amount])
Log([r[r.length - 1].Time, "closeshort", close, p.Amount], "@")
}
}
arrUp = []
arrDown = []
_G("arrUp", arrUp)
_G("arrDown", arrDown)
var profit = calcProfit()
LogProfit(profit)
}
Se divide en varios niveles y el nivel máximo es: maxRatio. Cada nivel calcula un umbral de precio diferente.
for (var i = 0; i < maxRatio; i++) {
var up = open + atr[atr.length - 1] * (i + 1)
var mid = open
var down = open - atr[atr.length - 1] * (i + 1)
atrs.push([open, (i + 1), atr])
var tradeAmount = baseAmount * Math.pow(2, i)
if (isAmountForUSDT) {
tradeAmount = tradeAmount * 1.05 / close
}
tradeAmount = _N(tradeAmount, amountPrecision)
var balance = acc.Balance
if (balance - initAcc.Equity * reserve < tradeAmount * close) {
continue
}
// ...
}
Diseñar funciones interactivas, limpiar inventario, pausar, reanudar, modificar parámetros, etc. Es muy conveniente diseñar interacciones en FMZ, y la plataforma proporciona muchos controles interactivos. Solo necesitamos agregar controles interactivos a la estrategia y luego escribir varios códigos de reconocimiento y procesamiento al recibir mensajes en el código de la estrategia.
var cmd = GetCommand()
if (cmd) {
Log("交互指令:", cmd)
var arrCmd = cmd.split(":")
if (arrCmd.length == 2) {
var strCmd = arrCmd[0]
var param = parseFloat(arrCmd[1])
if (strCmd == "atrPeriod") {
atrPeriod = param
Log("修改ATR参数:", atrPeriod)
}
} else {
if (cmd == "isPaused" && !isPaused) {
isPaused = true
Log("暂停交易")
} else if (cmd == "isPaused" && isPaused) {
isPaused = false
Log("取消暂停交易")
} else if (cmd == "clearAndPaused") {
clear(pos, r)
isPaused = true
Log("清仓、暂停交易")
}
}
}
Al abrir o cerrar una estrategia, puedes enviar mensajes fácilmente aCorreo, APLICACIÓN FMZ, interfaz de terceros, etc.
Log([r[r.length - 1].Time, "long", close, tradeAmount], "@") // 消息推送
Recibir notificaciones push (FMZ APP y otras aplicaciones también recibirán notificaciones push):

La función para calcular ganancias y pérdidas se llama cada vez que se cierra una posición para calcular las ganancias y pérdidas y generar la curva de ganancias y pérdidas.
function calcProfit() {
var initAcc = _G("initAcc")
var nowAcc = _C(exchange.GetAccount)
var profit = nowAcc.Equity - initAcc.Equity
return profit
}
Utilice FMZ_G()Función, es fácil diseñar un mecanismo de recuperación del progreso de la estrategia.
if (isReset) {
_G(null)
LogProfitReset()
LogReset(1)
c.reset()
}
arrUp = _G("arrUp")
if (!arrUp) {
arrUp = []
_G("arrUp", arrUp)
}
arrDown = _G("arrDown")
if (!arrDown) {
arrDown = []
_G("arrDown", arrDown)
}
Al negociar contratos, la cantidad de pedidos en la interfaz de pedidos es el número de contratos, por lo que los usuarios a menudo preguntan cómo realizar un pedido en el número de Us:
if (isAmountForUSDT) {
tradeAmount = tradeAmount * 1.05 / close
}
tradeAmount = _N(tradeAmount, amountPrecision)
En realidad es muy sencillo: sólo hay que dividir la cantidad por el precio.
Si deseas reservar siempre una determinada cantidad de fondos en tu cuenta como control de riesgos, puedes diseñar este sencillo mecanismo.
var balance = acc.Balance
if (balance - initAcc.Equity * reserve < tradeAmount * close) {
continue
}
Al operar en un mercado real, es fundamental observar la estrategia, incluyendo el capital de la cuenta, el estado de la estrategia, las posiciones de la estrategia, la información de las órdenes, los gráficos del mercado, etc. Estos se estructuran de la siguiente manera:
if (isShowPlot) {
r.forEach(function(bar, index) {
c.begin(bar)
for (var i in atrs) {
var arr = atrs[i]
var up = arr[0] + arr[2][index] * arr[1]
var mid = arr[0]
var down = arr[0] - arr[2][index] * arr[1]
c.plot(up, 'up_' + (i + 1))
c.plot(mid, 'mid_' + (i + 1))
c.plot(down, 'down_' + (i + 1))
}
for (var signal of arrSignal) {
if (signal[0] == bar.Time) {
c.signal(signal[1], signal[2], signal[3])
}
}
c.close()
})
}
// ...
var orderTbl = {"type": "table", "title": "order", "cols": ["symbol", "type", "ratio", "price", "amount"], "rows": []}
for (var i = arrUp.length - 1; i >= 0; i--) {
var order = arrUp[i]
orderTbl["rows"].push([order["symbol"], "short", order["ratio"], order["price"], order["amount"]])
}
for (var i = 0; i < arrDown.length; i++) {
var order = arrDown[i]
orderTbl["rows"].push([order["symbol"], "long", order["ratio"], order["price"], order["amount"]])
}
var posTbl = {"type": "table", "title": "pos", "cols": ["symbol", "type", "price", "amount"], "rows": []}
for (var i = 0; i < pos.length; i++) {
var p = pos[i]
posTbl["rows"].push([p.Symbol, p.Type == PD_LONG ? "long" : "short", p.Price, p.Amount])
}
LogStatus(_D(), "初始权益:" + initAcc.Equity, ", 当前权益:" + acc.Equity, ", 运行状态:" + (isPaused ? "暂停交易" : "运行中"),
"\n`" + JSON.stringify(orderTbl) + "`\n", "`" + JSON.stringify(posTbl) + "`")
Al final, más de 200 líneas de código implementaron una estrategia completa que puede probarse e implementarse en operaciones reales. Hemos logrado nuestro objetivo final: crear un sistema de comercio cuantitativo todo en uno en FMZ que combina “visualización + interacción + automatización”.
Las pruebas retrospectivas son sólo para referencia. Quienes realizan trading cuantitativo saben que el “backtesting” no puede simular el escenario real al 100%. El objetivo principal del backtesting es probar la lógica de la estrategia, probar la solidez de la estrategia y probar las funciones básicas.


Diseño de parámetros:

Diseño de interacción:

Código fuente de la estrategia:
/*backtest
start: 2024-04-27 18:40:00
end: 2025-04-10 00:00:00
period: 15m
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT","balance":100}]
*/
var atrPeriod = 20
var arrUp = null
var arrDown = null
var arrSignal = []
function calcProfit() {
var initAcc = _G("initAcc")
var nowAcc = _C(exchange.GetAccount)
var profit = nowAcc.Equity - initAcc.Equity
return profit
}
function clear(positions, r) {
var close = r[r.length - 1].Close
for (var p of positions) {
if (p.Type == PD_LONG) {
var id = exchange.CreateOrder(symbol, "closebuy", -1, p.Amount)
if (!id) {
Log("下单失败")
continue
}
arrSignal.push([r[r.length - 1].Time, "closelong", close, p.Amount])
Log([r[r.length - 1].Time, "closelong", close, p.Amount], "@")
} else if (p.Type == PD_SHORT) {
var id = exchange.CreateOrder(symbol, "closesell", -1, p.Amount)
if (!id) {
Log("下单失败")
continue
}
arrSignal.push([r[r.length - 1].Time, "closeshort", close, p.Amount])
Log([r[r.length - 1].Time, "closeshort", close, p.Amount], "@")
}
}
arrUp = []
arrDown = []
_G("arrUp", arrUp)
_G("arrDown", arrDown)
var profit = calcProfit()
LogProfit(profit)
}
function main() {
var symbolInfo = symbol.split(".")
if (symbolInfo.length != 2) {
throw "error symbol:" + symbol
} else {
exchange.SetCurrency(symbolInfo[0])
exchange.SetContractType(symbolInfo[1])
}
exchange.SetPrecision(pricePrecision, amountPrecision)
let c = KLineChart({
overlay: true
})
if (isReset) {
_G(null)
LogProfitReset()
LogReset(1)
c.reset()
}
arrUp = _G("arrUp")
if (!arrUp) {
arrUp = []
_G("arrUp", arrUp)
}
arrDown = _G("arrDown")
if (!arrDown) {
arrDown = []
_G("arrDown", arrDown)
}
var initAcc = _G("initAcc")
if (!initAcc) {
initAcc = _C(exchange.GetAccount)
_G("initAcc", initAcc)
}
var isPaused = false
while (true) {
var atrs = []
var r = _C(exchange.GetRecords, symbol)
var pos = _C(exchange.GetPositions, symbol)
var acc = _C(exchange.GetAccount)
var open = r[r.length - 1].Open
var close = r[r.length - 1].Close
var atr = TA.ATR(r, atrPeriod)
for (var i = 0; i < maxRatio; i++) {
var up = open + atr[atr.length - 1] * (i + 1)
var mid = open
var down = open - atr[atr.length - 1] * (i + 1)
atrs.push([open, (i + 1), atr])
var tradeAmount = baseAmount * Math.pow(2, i)
if (isAmountForUSDT) {
tradeAmount = tradeAmount * 1.05 / close
}
tradeAmount = _N(tradeAmount, amountPrecision)
var balance = acc.Balance
if (balance - initAcc.Equity * reserve < tradeAmount * close) {
continue
}
if (close > up && i >= arrUp.length && !isPaused) {
var id = exchange.CreateOrder(symbol, "sell", -1, tradeAmount)
if (!id) {
Log("下单失败")
continue
}
arrUp.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
_G("arrUp", arrUp)
arrSignal.push([r[r.length - 1].Time, "short", close, tradeAmount])
Log([r[r.length - 1].Time, "short", close, tradeAmount], "@")
} else if (close < down && i >= arrDown.length && !isPaused) {
var id = exchange.CreateOrder(symbol, "buy", -1, tradeAmount)
if (!id) {
Log("下单失败")
continue
}
arrDown.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
_G("arrDown", arrDown)
arrSignal.push([r[r.length - 1].Time, "long", close, tradeAmount])
Log([r[r.length - 1].Time, "long", close, tradeAmount], "@")
} else if (((arrUp.length > 0 && close < mid) || (arrDown.length > 0 && close > mid)) && !isPaused) {
clear(pos, r)
}
}
if (isShowPlot) {
r.forEach(function(bar, index) {
c.begin(bar)
for (var i in atrs) {
var arr = atrs[i]
var up = arr[0] + arr[2][index] * arr[1]
var mid = arr[0]
var down = arr[0] - arr[2][index] * arr[1]
c.plot(up, 'up_' + (i + 1))
c.plot(mid, 'mid_' + (i + 1))
c.plot(down, 'down_' + (i + 1))
}
for (var signal of arrSignal) {
if (signal[0] == bar.Time) {
c.signal(signal[1], signal[2], signal[3])
}
}
c.close()
})
}
var cmd = GetCommand()
if (cmd) {
Log("交互指令:", cmd)
var arrCmd = cmd.split(":")
if (arrCmd.length == 2) {
var strCmd = arrCmd[0]
var param = parseFloat(arrCmd[1])
if (strCmd == "atrPeriod") {
atrPeriod = param
Log("修改ATR参数:", atrPeriod)
}
} else {
if (cmd == "isPaused" && !isPaused) {
isPaused = true
Log("暂停交易")
} else if (cmd == "isPaused" && isPaused) {
isPaused = false
Log("取消暂停交易")
} else if (cmd == "clearAndPaused") {
clear(pos, r)
isPaused = true
Log("清仓、暂停交易")
}
}
}
var orderTbl = {"type": "table", "title": "order", "cols": ["symbol", "type", "ratio", "price", "amount"], "rows": []}
for (var i = arrUp.length - 1; i >= 0; i--) {
var order = arrUp[i]
orderTbl["rows"].push([order["symbol"], "short", order["ratio"], order["price"], order["amount"]])
}
for (var i = 0; i < arrDown.length; i++) {
var order = arrDown[i]
orderTbl["rows"].push([order["symbol"], "long", order["ratio"], order["price"], order["amount"]])
}
var posTbl = {"type": "table", "title": "pos", "cols": ["symbol", "type", "price", "amount"], "rows": []}
for (var i = 0; i < pos.length; i++) {
var p = pos[i]
posTbl["rows"].push([p.Symbol, p.Type == PD_LONG ? "long" : "short", p.Price, p.Amount])
}
LogStatus(_D(), "初始权益:" + initAcc.Equity, ", 当前权益:" + acc.Equity, ", 运行状态:" + (isPaused ? "暂停交易" : "运行中"),
"\n`" + JSON.stringify(orderTbl) + "`\n", "`" + JSON.stringify(posTbl) + "`")
Sleep(5000)
}
}
La estrategia es únicamente con fines didácticos. Si bien se puede utilizar en el trading real y actualmente es rentable, llevará tiempo comprobar su eficacia a largo plazo. Todavía hay margen de optimización en la parte de elaboración de la estrategia, lo que puede evitar algunas operaciones repetitivas y mejorar la eficiencia del programa. La lógica de la estrategia también se puede optimizar aún más.

Un resumen poético de GPT:
El trading real es un largo viaje. No importa cuando regreses, sólo buscas paz mental. Cada vez que abres una posición, siembras la luz de la esperanza en el vasto mercado; Cada vez que detienes una pérdida, aprendes a avanzar con más firmeza ante el viento y la lluvia. El mercado es como la marea y las ganancias y las pérdidas son como los sueños. Bailamos en la cresta de las olas de los números y miramos bajo el faro de la estrategia. Que tú y yo, en este largo viaje, no perdamos el rumbo ni temamos la soledad, y alcancemos finalmente la luz que nos pertenece.
Este artículo no sólo presenta una estrategia completa, sino, lo que es más importante, una idea de desarrollo de estrategia “sistemática”. Desde el diseño de la estrategia, la gestión del estado, el control de riesgos, la interacción con los gráficos hasta la implementación real, este es un conjunto de plantillas que se pueden reutilizar repetidamente y también es la única forma de que el trading cuantitativo avance hacia la profesionalización.
Espero que puedas utilizar la plataforma FMZ para construir tu propio sistema de trading automatizado para que nunca pierdas ninguna señal.
Gracias por su lectura y apoyo. La estrategia es sólo para fines didácticos. Úselo con precaución en el comercio real.