avatar of 发明者量化-小小梦 发明者量化-小小梦
Suivre Messages privés
4
Suivre
1271
Abonnés

Le voyage quantitatif commence à partir de FMZ

Créé le: 2025-04-18 09:31:42, Mis à jour le: 2025-04-26 11:50:01
comments   0
hits   941

Le voyage quantitatif commence à partir de FMZ

introduction

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é.

Confusion des débutants en trading quantitatif

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 :

  • Comment concevoir des positions d’ouverture et de fermeture
  • Comment concevoir le calcul des revenus
  • Comment concevoir des stratégies pour redémarrer et poursuivre la progression du trading
  • Comment concevoir un affichage de graphique de stratégie
  • Comment concevoir des contrôles d’interaction stratégiques

Résolvons ensemble la confusion ci-dessus.

Explication de la conception

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 :

Ajoutez des positions et fermez des positions en fonction des ruptures de prix à différents niveaux

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)
}

Attribution des postes étape par étape

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 
            }
        // ...
        }

Prise en charge du réglage dynamique des paramètres, de l’opération de pause, de l’effacement rapide et d’autres interactions

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("清仓、暂停交易")
                }
            }
        }

Avec mécanisme de rappel d’ouverture/fermeture

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) :

Le voyage quantitatif commence à partir de FMZ

Statistiques en temps réel et affichage des bénéfices et des positions

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
}

Prise en charge de la persistance de l’état (récupération du point d’arrêt)

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)
    }

Conception de la passation des commandes par montant

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.

Conception du ratio de réserve

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 
            }

Graphique de visualisation

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 ».

Effet de l’opération de stratégie et résultats des backtests

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.

Le voyage quantitatif commence à partir de FMZ

Le voyage quantitatif commence à partir de FMZ

Code de stratégie, conception des paramètres

Conception des paramètres :

Le voyage quantitatif commence à partir de FMZ

Conception d’interactions :

Le voyage quantitatif commence à partir de FMZ

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.

Le vrai trading est un long voyage

Le voyage quantitatif commence à partir de FMZ

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.

Résumé : Du développement de la stratégie à la pensée systémique

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.