avatar of 发明者量化-小小梦 发明者量化-小小梦
konzentrieren Sie sich auf Private Nachricht
4
konzentrieren Sie sich auf
1271
Anhänger

Die quantitative Reise beginnt bei FMZ

Erstellt in: 2025-04-18 09:31:42, aktualisiert am: 2025-04-26 11:50:01
comments   0
hits   937

Die quantitative Reise beginnt bei FMZ

Einführung

Haben Sie jemals daran gedacht, dass Sie ganz einfach mit dem quantitativen Handel beginnen und sofort loslegen können, ohne die ganze Nacht wach bleiben und Code schreiben zu müssen, um ein Framework zu erstellen, die Benutzeroberfläche zu entwerfen und verschiedene Designdetails und -mechanismen selbst zu bearbeiten? Auf der quantitativen Plattform von FMZ wird alles möglich. Sie benötigen weder fortgeschrittene Programmierkenntnisse noch müssen Sie sich um komplizierte Bereitstellungsprozesse kümmern – alles, was Sie brauchen, ist ein Computer und ein Konto, um Ihre quantitative Reise zu beginnen, die Sie überallhin führt. Dieser Artikel führt Sie von Grund auf durch den schnellen Einstieg in FMZ, zeigt Ihnen den Reiz des automatisierten Handels und zeigt Ihnen, wie Sie mithilfe von Daten und Strategien den Marktrhythmus meistern. Egal, ob Sie Anfänger oder erfahrener Anwender sind und Ihre Effizienz verbessern möchten, dieser Weg ist einen Versuch wert.

Verwirrung bei Anfängern im quantitativen Handel

Ich kommuniziere und chatte oft mit Anfängern der Plattform. Anfänger im quantitativen Handel sind normalerweise durch den gesamten Designprozess verwirrt. Wenn ich Handelsideen habe, weiß ich oft nicht, wo ich anfangen soll, und fühle mich überfordert.

Verwirrt über:

  • So gestalten Sie Eröffnungs- und Schließpositionen
  • So gestalten Sie die Erlösberechnung
  • So entwickeln Sie Strategien zum Neustart und zur Fortsetzung des Handelsfortschritts
  • So gestalten Sie eine Strategiediagrammanzeige
  • Wie man strategische Interaktionskontrolle gestaltet

Lassen Sie uns die oben genannte Verwirrung gemeinsam lösen.

Designerklärung

In der Welt des quantitativen Handels ist die Strategieentwicklung oft eine endlose Entdeckungsreise. Sie haben vielleicht versucht, Indikatoren zu schreiben oder Kauf- und Verkaufssignalen blind zu folgen, aber wirklich weit kommen können Sie mit Strategiesystemen, die „sichtbar, anpassbar und stabil“ sind. Auf Basis der quantitativen Plattform von FMZ können Sie „Pünktlichkeit“ praxisnah erfahren. Erstellen Sie eine einfache Strategie, von der Parametereinstellung und Diagrammanzeige bis hin zu interaktiven Funktionen und Gewinn- und Verlustberechnung, um die Designanforderungen einer Strategie vollständig zu erfüllen.

Die Strategieidee ist eine schrittweise Strategie zur Positionserhöhung basierend auf ATR, einer schrittweisen Logik zum Aufbau von Rasterpositionen (Long und Short bidirektional), einer adaptiven ATR-Volatilitätsberechnung und einer Logik zur Positionsliquidierung (wenn der Markt zur Mittelachse umkehrt).

Diese Strategie basiert auf den folgenden Designanforderungen:

Positionen hinzufügen und Positionen schließen entsprechend Preisdurchbrüchen auf verschiedenen Ebenen

Richten Sie zwei Arrays ein, um die schrittweise Erhöhung der Positionen zu steuern.

var arrUp = null 
var arrDown = null 

Jedes Mal, wenn Sie eine Position hinzufügen, werden die Positionsinformationen in das Array übertragen, wodurch die Kontrolle der Position und die Anzeige der Daten auf der Echtzeitschnittstelle der Strategie erleichtert wird.

Öffnen und schließen Sie Positionen entsprechend dem Preisdurchbruchsniveau. Der Einfachheit halber werden sowohl zum Öffnen als auch zum Schließen von Positionen Marktaufträge verwendet, die einfach und effektiv sind.

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

Leeren Sie das Inventar und verwenden Sie eine Funktion zur Handhabung. Einige Datenstrukturen müssen bei jeder Bestandslöschung zurückgesetzt werden. Daher muss die Löschfunktion zur Wiederverwendung im interaktiven Modul in eine Funktion gekapselt werden.

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

Positionsvergabe Schritt für Schritt

Es ist in mehrere Ebenen unterteilt und die maximale Ebene ist: maxRatio. Für jede Stufe wird eine andere Preisschwelle berechnet.

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

Unterstützt dynamische Parameteranpassung, Pausenbetrieb, schnelle Freigabe und andere Interaktionen

Entwerfen Sie interaktive Funktionen, löschen Sie das Inventar, pausieren Sie, setzen Sie die Pause fort, ändern Sie Parameter usw. Das Entwerfen von Interaktionen auf FMZ ist sehr praktisch und die Plattform bietet viele interaktive Steuerelemente. Wir müssen der Strategie nur interaktive Steuerelemente hinzufügen und dann beim Empfangen von Nachrichten im Strategiecode verschiedene Erkennungs- und Verarbeitungscodes schreiben.

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

Mit Öffnungs-/Schließ-Erinnerungsmechanismus

Beim Öffnen oder Schließen einer Strategie können Sie ganz einfach Nachrichten anMail, FMZ APP, Drittanbieter-Schnittstelle usw.

Log([r[r.length - 1].Time, "long", close, tradeAmount], "@")  // 消息推送

Push-Benachrichtigungen erhalten (FMZ APP und andere Apps erhalten ebenfalls Push-Benachrichtigungen):

Die quantitative Reise beginnt bei FMZ

Echtzeitstatistiken und Anzeige von Gewinnen und Positionen

Die Funktion zur Gewinn- und Verlustberechnung wird bei jeder Schließung einer Position aufgerufen, um den Gewinn und Verlust zu berechnen und die Gewinn- und Verlustkurve auszugeben.

function calcProfit() {
    var initAcc = _G("initAcc")
    var nowAcc = _C(exchange.GetAccount)
    var profit = nowAcc.Equity - initAcc.Equity
    return profit
}

Unterstützt die Statuspersistenz (Breakpoint-Wiederherstellung)

Verwenden Sie FMZ_G()Funktion: Es ist einfach, einen Mechanismus zur Wiederherstellung des Strategiefortschritts zu entwerfen.

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

Gestaltung der Auftragsvergabe nach Betrag

Beim Handel mit Kontrakten entspricht die Bestellmenge in der Bestellschnittstelle der Anzahl der Kontrakte. Daher fragen Benutzer häufig, wie sie eine Bestellung in der Anzahl von US-Dollar aufgeben können:

            if (isAmountForUSDT) {
                tradeAmount = tradeAmount * 1.05 / close
            }
            tradeAmount = _N(tradeAmount, amountPrecision)

Es ist eigentlich ganz einfach: Teilen Sie einfach den Betrag durch den Preis.

Gestaltung des Reservesatzes

Wenn Sie zur Risikokontrolle immer einen bestimmten Betrag auf Ihrem Konto reservieren möchten, können Sie diesen einfachen Mechanismus einrichten.

            var balance = acc.Balance
            if (balance - initAcc.Equity * reserve < tradeAmount * close) {
                continue 
            }

Visualisierungsdiagramm

Beim Betreiben eines realen Marktes ist es unbedingt notwendig, die Strategie zu beachten, einschließlich Kontostand, Strategiestatus, Strategiepositionen, Auftragsinformationen, Marktcharts usw. Diese sind wie folgt aufgebaut:

        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) + "`")

Am Ende implementierten mehr als 200 Codezeilen eine vollständige Strategie, die einem Backtest unterzogen und im realen Handel umgesetzt werden kann. Wir haben unser ultimatives Ziel erreicht: die Schaffung eines umfassenden quantitativen Handelssystems auf FMZ, das „Visualisierung + Interaktion + Automatisierung“ kombiniert.

Auswirkungen der Strategieoperation und Backtesting-Ergebnisse

Backtesting dient nur als Referenz. Wer quantitativen Handel betreibt, weiß, dass „Backtesting“ das tatsächliche Szenario nicht zu 100 % simulieren kann. Der Hauptzweck des Backtestings besteht darin, die Logik der Strategie zu testen, die Robustheit der Strategie zu testen und grundlegende Funktionen zu testen.

Die quantitative Reise beginnt bei FMZ

Die quantitative Reise beginnt bei FMZ

Strategiecode, Parameterdesign

Parameterdesign:

Die quantitative Reise beginnt bei FMZ

Interaktionsdesign:

Die quantitative Reise beginnt bei FMZ

Quellcode der Strategie:

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

Die Strategie dient ausschließlich Lehrzwecken. Obwohl es im realen Handel eingesetzt werden kann und derzeit profitabel ist, wird es einige Zeit dauern, bis seine langfristige Wirksamkeit getestet wird. Im Strategiezeichnungsteil besteht noch Optimierungsbedarf, wodurch einige sich wiederholende Vorgänge vermieden und die Programmleistung verbessert werden können. Auch die Strategielogik kann noch weiter optimiert werden.

Echter Handel ist eine lange Reise

Die quantitative Reise beginnt bei FMZ

Eine poetische Zusammenfassung von GPT:

Echter Handel ist eine lange Reise. Egal, wann Sie zurückkehren, Sie suchen nur Seelenfrieden. Jedes Mal, wenn Sie eine Position eröffnen, säen Sie ein Licht der Hoffnung auf dem riesigen Markt. Jedes Mal, wenn Sie den Verlust stoppen, lernen Sie, bei Wind und Regen sicherer voranzukommen. Der Markt ist wie die Gezeiten und Gewinne und Verluste sind wie Träume. Wir tanzen auf dem Wellenkamm der Zahlen und beobachten unter dem Leuchtturm der Strategie. Mögen Sie und ich auf dieser langen Reise weder unseren Weg verlieren noch die Einsamkeit fürchten und schließlich das Licht erreichen, das uns gehört.

Zusammenfassung: Von der Strategieentwicklung zum Systemdenken

Dieser Artikel stellt nicht nur eine vollständige Strategie vor, sondern, was noch wichtiger ist, eine Idee zur „systematischen“ Strategieentwicklung. Von der Strategiegestaltung, dem Statusmanagement, der Risikokontrolle, der Diagramminteraktion bis hin zur tatsächlichen Implementierung handelt es sich hierbei um einen Satz von Vorlagen, die wiederholt wiederverwendet werden können, und es ist auch der einzige Weg für den quantitativen Handel, sich in Richtung Professionalisierung zu bewegen.

Ich hoffe, Sie können die FMZ-Plattform nutzen, um Ihr eigenes automatisiertes Handelssystem aufzubauen, sodass Sie nie ein Signal verpassen.

Vielen Dank fürs Lesen und Ihre Unterstützung. Die Strategie dient ausschließlich Lehrzwecken. Bitte verwenden Sie es im realen Handel mit Vorsicht.