
Avez-vous déjà pensé que vous pouviez facilement vous lancer dans le trading quantitatif et commencer immédiatement sans avoir à rester éveillé toute la nuit à écrire du code pour créer un framework, concevoir l’interface utilisateur et divers détails et mécanismes de conception vous-même ? Tout devient possible sur la plateforme quantitative FMZ. Vous n’avez pas besoin d’une expérience avancée en programmation, ni de vous soucier de processus de déploiement compliqués : tout ce dont vous avez besoin est un ordinateur et un compte pour démarrer votre parcours quantitatif « partout ». Cet article vous permettra de partir de zéro, de démarrer rapidement avec FMZ, de découvrir le charme du trading automatisé et d’utiliser des données et des stratégies pour maîtriser le rythme du marché. Que vous soyez un débutant ou un vétéran cherchant à améliorer son efficacité, ce voyage vaut la peine d’être essayé.
Je communique et discute souvent avec les débutants de la plateforme. Les débutants en trading quantitatif sont généralement déroutés par le processus de conception complet. Lorsque j’ai des idées de trading, je ne sais souvent pas par où commencer et je me sens dépassé.
Confus à propos de :
Résolvons ensemble la confusion ci-dessus.
Dans le monde du trading quantitatif, la conception d’une stratégie est souvent un voyage d’exploration sans fin. Vous avez peut-être essayé d’écrire des indicateurs ou de suivre aveuglément les signaux d’achat et de vente, mais ceux qui peuvent vraiment aller loin sont ces systèmes de stratégie qui peuvent être « visibles, ajustables et stables ». Grâce à la plateforme quantitative FMZ, vous pouvez vivre une expérience pratique de « respect des délais ». Construisez une stratégie simple, depuis le paramétrage, l’affichage graphique, jusqu’aux fonctions interactives et au calcul des profits et pertes, pour répondre pleinement aux exigences de conception d’une stratégie.
L’idée stratégique est une stratégie d’augmentation de position étape par étape basée sur l’ATR, une logique de création de position de grille étape par étape (bidirectionnelle longue et courte), un calcul de volatilité adaptative de l’ATR et une logique de liquidation de position (lorsque le marché s’inverse vers l’axe central).
Cette stratégie est basée sur les exigences de conception suivantes :
Mettre en place deux tableaux pour contrôler l’augmentation progressive des positions.
var arrUp = null
var arrDown = null
Chaque fois que vous ajoutez une position, les informations de position sont poussées dans le tableau, ce qui facilite le contrôle de la position et l’affichage des données sur l’interface de stratégie en temps réel.
Ouvrir et fermer des positions en fonction du niveau de rupture des prix. Par souci de simplicité, les positions d’ouverture et de fermeture utilisent des ordres de marché, qui sont simples et efficaces.
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)
}
Effacez l’inventaire et utilisez une fonction pour le gérer. Certaines structures de données doivent être réinitialisées à chaque fois que l’inventaire est effacé, la fonction d’effacement doit donc être encapsulée dans une fonction pour être réutilisée dans le module interactif.
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)
}
Il est divisé en plusieurs niveaux, et le niveau maximum est : maxRatio. Chaque niveau calcule un seuil de prix différent.
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
}
// ...
}
Concevez des fonctions interactives, effacez l’inventaire, mettez en pause, reprenez la pause, modifiez les paramètres, etc. Il est très pratique de concevoir des interactions sur FMZ, et la plate-forme fournit de nombreux contrôles interactifs. Il nous suffit d’ajouter des contrôles interactifs à la stratégie, puis d’écrire divers codes de reconnaissance et de traitement lors de la réception de messages dans le code de stratégie.
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("清仓、暂停交易")
}
}
}
Lors de l’ouverture ou de la fermeture d’une stratégie, vous pouvez facilement envoyer des messages àMail, FMZ APP, interface tierce, etc.
Log([r[r.length - 1].Time, "long", close, tradeAmount], "@") // 消息推送
Recevez des notifications push (FMZ APP et d’autres applications recevront également des notifications push) :

La fonction de calcul des profits et des pertes est appelée à chaque fois qu’une position est fermée pour calculer les profits et les pertes et générer la courbe des profits et des pertes.
function calcProfit() {
var initAcc = _G("initAcc")
var nowAcc = _C(exchange.GetAccount)
var profit = nowAcc.Equity - initAcc.Equity
return profit
}
Utiliser FMZ_G()Fonction, il est facile de concevoir un mécanisme de récupération de progression de stratégie.
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)
}
Lors de la négociation de contrats, la quantité commandée dans l’interface de commande est le nombre de contrats, les utilisateurs demandent donc souvent comment passer une commande dans le nombre de Us :
if (isAmountForUSDT) {
tradeAmount = tradeAmount * 1.05 / close
}
tradeAmount = _N(tradeAmount, amountPrecision)
C’est en fait très simple, il suffit de diviser le montant par le prix.
Si vous souhaitez toujours réserver un certain montant de fonds sur votre compte pour contrôler les risques, vous pouvez concevoir ce mécanisme simple.
var balance = acc.Balance
if (balance - initAcc.Equity * reserve < tradeAmount * close) {
continue
}
Lors de l’exécution d’un marché réel, il est absolument nécessaire d’observer la stratégie, y compris la valeur du compte, l’état de la stratégie, les positions de la stratégie, les informations sur les commandes, les graphiques du marché, etc. Ceux-ci sont conçus comme suit :
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) + "`")
Au final, plus de 200 lignes de code ont permis de mettre en œuvre une stratégie complète qui peut être backtestée et mise en œuvre dans le trading réel. Nous avons atteint notre objectif ultime : créer un système de trading quantitatif tout-en-un sur FMZ qui combine « visualisation + interaction + automatisation ».
Le backtesting est fourni à titre indicatif uniquement. Ceux qui font du trading quantitatif savent que le « backtesting » ne peut pas simuler le scénario réel à 100 %. L’objectif principal du backtesting est de tester la logique de la stratégie, de tester la robustesse de la stratégie et de tester les fonctions de base.


Conception des paramètres :

Conception d’interactions :

Code source de la stratégie :
/*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 stratégie est uniquement destinée à des fins pédagogiques. Bien qu’il puisse être utilisé dans le trading réel et qu’il soit actuellement rentable, il faudra du temps pour tester son efficacité à long terme. Il y a encore de la place pour l’optimisation dans la partie dessin de stratégie, ce qui peut éviter certaines opérations répétitives et améliorer l’efficacité du programme. La logique de la stratégie peut également être optimisée davantage.

Un résumé poétique de GPT :
Le vrai trading est un long voyage. Peu importe quand vous revenez, vous ne recherchez que la tranquillité d’esprit. Chaque fois que vous ouvrez une position, vous semez la lumière de l’espoir dans le vaste marché ; à chaque fois que vous arrêtez la perte, vous apprenez à avancer plus fermement dans le vent et la pluie. Le marché est comme la marée, et les profits et les pertes sont comme des rêves. Nous dansons sur la crête des vagues des nombres et regardons sous le phare de la stratégie. Puissions-nous, toi et moi, dans ce long voyage, ne pas nous perdre ni craindre la solitude, et enfin atteindre la lumière qui nous appartient.
Cet article présente non seulement une stratégie complète, mais plus important encore, une idée de développement de stratégie « systématique ». De la conception de la stratégie, de la gestion du statut, du contrôle des risques, de l’interaction graphique à la mise en œuvre réelle, il s’agit d’un ensemble de modèles qui peuvent être réutilisés à plusieurs reprises, et c’est également le seul moyen pour le trading quantitatif d’évoluer vers la professionnalisation.
J’espère que vous pourrez utiliser la plateforme FMZ pour créer votre propre système de trading automatisé afin de ne jamais manquer aucun signal.
Merci pour votre lecture et votre soutien. La stratégie est uniquement destinée à des fins pédagogiques. Veuillez l’utiliser avec prudence dans le trading réel.