
Você já pensou que pode facilmente começar a negociar quantitativamente e começar imediatamente, sem precisar ficar acordado a noite toda escrevendo código para construir uma estrutura, projetando a interface do usuário e vários detalhes e mecanismos de design? Tudo se torna possível na plataforma quantitativa FMZ. Você não precisa de experiência avançada em programação, nem precisa se preocupar com processos complicados de implantação. Tudo o que você precisa é de um computador e uma conta para começar sua jornada quantitativa “vá a qualquer lugar”. Este artigo o levará do zero, lhe dará uma rápida introdução ao FMZ, experimentará o charme da negociação automatizada e usará dados e estratégias para dominar o ritmo do mercado. Seja você um iniciante ou um veterano procurando melhorar a eficiência, vale a pena tentar esta jornada.
Frequentemente me comunico e converso com iniciantes na plataforma. Iniciantes em negociação quantitativa geralmente ficam confusos com o processo de design completo. Quando tenho ideias de negociação, muitas vezes não sei por onde começar e me sinto sobrecarregado.
Confuso sobre:
Vamos resolver a confusão acima juntos.
No mundo da negociação quantitativa, o design de estratégias costuma ser uma jornada de exploração sem fim. Você pode ter tentado escrever indicadores ou tentado seguir cegamente os sinais de compra e venda, mas aqueles que realmente podem ir longe são aqueles sistemas de estratégia que podem ser “visíveis, ajustáveis e estáveis”. Com base na plataforma quantitativa FMZ, você pode ter uma experiência prática de “chegar no horário”. Crie uma estratégia simples, desde a definição de parâmetros, exibição de gráficos, funções interativas e cálculo de lucros e perdas, para atender totalmente aos requisitos de design de uma estratégia.
A ideia da estratégia é uma estratégia de aumento de posição passo a passo com base no ATR, lógica de construção de posição de grade passo a passo (bidirecional longa e curta), cálculo de volatilidade adaptativa do ATR e lógica de liquidação de posição (quando o mercado reverte para o eixo central).
Esta estratégia é baseada nos seguintes requisitos de projeto:
Configure duas matrizes para controlar o aumento gradual de posições.
var arrUp = null
var arrDown = null
Cada vez que você adiciona uma posição, as informações da posição são inseridas na matriz, o que facilita o controle da posição e a exibição dos dados na interface da estratégia em tempo real.
Abra e feche posições de acordo com o nível de rompimento do preço. Para simplificar, tanto a abertura quanto o fechamento de posições usam ordens de mercado, que são simples e eficazes.
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)
}
Limpe o inventário e use uma função para lidar com isso. Algumas estruturas de dados precisam ser redefinidas sempre que o inventário é limpo, então a função de limpeza precisa ser encapsulada em uma função para reutilização no módulo interativo.
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)
}
Ele é dividido em vários níveis, e o nível máximo é: maxRatio. Cada nível calcula um limite de preço 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
}
// ...
}
Crie funções interativas, limpe inventário, pause, retome, modifique parâmetros, etc. É muito conveniente criar interações no FMZ, e a plataforma oferece muitos controles interativos. Precisamos apenas adicionar controles interativos à estratégia e, então, escrever vários códigos de reconhecimento e processamento ao receber mensagens no código da estratégia.
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("清仓、暂停交易")
}
}
}
Ao abrir ou fechar uma estratégia, você pode facilmente enviar mensagens paraCorrespondência, APP FMZ, interface de terceiros, etc.
Log([r[r.length - 1].Time, "long", close, tradeAmount], "@") // 消息推送
Receba notificações push (o FMZ APP e outros aplicativos também receberão notificações push):

A função para calcular lucros e perdas é chamada toda vez que uma posição é fechada para calcular os lucros e perdas e gerar a curva de lucros e perdas.
function calcProfit() {
var initAcc = _G("initAcc")
var nowAcc = _C(exchange.GetAccount)
var profit = nowAcc.Equity - initAcc.Equity
return profit
}
Use FMZ_G()Função: é fácil projetar um mecanismo de recuperação do progresso da estratégia.
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)
}
Ao negociar contratos, a quantidade do pedido na interface de pedidos é o número de contratos, então os usuários frequentemente perguntam como fazer um pedido no número de Us:
if (isAmountForUSDT) {
tradeAmount = tradeAmount * 1.05 / close
}
tradeAmount = _N(tradeAmount, amountPrecision)
Na verdade é muito simples, basta dividir o valor pelo preço.
Se você quiser sempre reservar uma certa quantia de fundos em sua conta como controle de risco, você pode criar este mecanismo simples.
var balance = acc.Balance
if (balance - initAcc.Equity * reserve < tradeAmount * close) {
continue
}
Ao administrar um mercado real, é definitivamente necessário observar a estratégia, incluindo o patrimônio da conta, o status da estratégia, as posições da estratégia, as informações sobre ordens, os gráficos de mercado, etc. Eles são projetados da seguinte forma:
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) + "`")
No final, mais de 200 linhas de código implementaram uma estratégia completa que pode ser testada e implementada em negociações reais. Atingimos nosso objetivo final: criar um sistema de negociação quantitativa completo na FMZ que combina “visualização + interação + automação”.
O backtesting é apenas para referência. Aqueles que fazem negociação quantitativa sabem que o “backtest” não consegue simular o cenário real 100%. O principal objetivo do backtesting é testar a lógica da estratégia, testar a robustez da estratégia e testar funções básicas.


Projeto de parâmetros:

Design de interação:

Código fonte da estratégia:
/*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)
}
}
A estratégia é apenas para fins de ensino. Embora possa ser usado em negociações reais e seja atualmente lucrativo, levará tempo para testar sua eficácia a longo prazo. Ainda há espaço para otimização na parte de desenho de estratégias, o que pode evitar algumas operações repetitivas e melhorar a eficiência do programa. A lógica da estratégia também pode ser ainda mais otimizada.

Um resumo poético do GPT:
A verdadeira negociação é uma longa jornada. Não importa quando você retornar, você só busca paz de espírito. Cada vez que você abre uma posição, você semeia a luz da esperança no vasto mercado; cada vez que você interrompe a perda, você aprende a seguir em frente com mais firmeza no vento e na chuva. O mercado é como a maré, e lucros e perdas são como sonhos. Dançamos na crista das ondas dos números e observamos sob o farol da estratégia. Que você e eu, nesta longa jornada, não percamos o caminho nem tenhamos medo da solidão, e finalmente alcancemos a luz que nos pertence.
Este artigo não apenas apresenta uma estratégia completa, mas, mais importante, uma ideia de desenvolvimento de estratégia “sistemática”. Desde o design da estratégia, gerenciamento de status, controle de risco, interação de gráficos até a implementação real, este é um conjunto de modelos que podem ser reutilizados repetidamente e também é a única maneira de a negociação quantitativa caminhar para a profissionalização.
Espero que você possa usar a plataforma FMZ para criar seu próprio sistema de negociação automatizado para que nunca perca nenhum sinal.
Obrigado pela sua leitura e apoio. A estratégia é apenas para fins de ensino. Por favor, use-o com cautela em negociações reais.