Die Ressourcen sind geladen. Beförderung...

Eine Strategievorlage, mit der Sie die WebSocket-Branche nahtlos nutzen können

Schriftsteller:Das Gras, Erstellt: 2024-10-30 09:49:20, aktualisiert: 2024-11-05 17:45:31

img

Dies ist eine WebSocket-Marktvorlage, die von FMZ offiziell entwickelt wurde, und die als Vorlage kopiert und in der neuen Strategie ausgewählt werden kann:https://www.fmz.com/strategy/470349

Warum WebSocket benötigt wird

Die derzeitige FMZ-Strategie basiert auf der traditionellen REST-API-Umschließung, bei der bei jedem API-Zugriff eine Netzwerkverbindung hergestellt wird, um Marktdaten per Abfrage zu erhalten.

Die REST-Protokoll hat jedoch ein inhärentes Verzögerungsproblem, das vergrößert wird, wenn mehrere Handelspare oder mehrere Börsenstrategien benötigt werden. Obwohl die Go-Funktion der Plattform parallel ausgeführt werden kann, besteht das Verzögerungsproblem, dass es schwierig ist, die Bedürfnisse für relativ hohe Frequenzen für strategische Trades zu befriedigen, und dass zu viele Trades zu schnell werden und die Zugriffsfrequenz der Plattform eingeschränkt wird.

Die Serverbelastung der aktuellen Börsen ist ebenfalls sehr hoch. Sie bieten ein vollwertiges WebSocket-Protokoll und empfehlen API-Benutzern, es zu verwenden. Im Vergleich zum REST-Protokoll bietet WebSocket eine dauerhafte, zwei-Wege-Verbindung, die es den Börsen ermöglicht, Daten in Echtzeit an die Kunden zu schicken, häufige Anfragen und Antworten zu vermeiden und die Verzögerung erheblich zu reduzieren.

WebSocket-Branchenvorlage

Die FMZ-Quantitative-Trading-Plattform unterstützt seit frühem den WebSocket-Protokoll und ist relativ einfach zu bedienen, aber für Neulinge ist es zu kompliziert, mehrere Abonnements zu verarbeiten, mehrere Börsen zu abonnieren und effizient und einfach in den gesamten Strategieprozess eingebunden zu sein. Die öffentlich zugängliche WebSocket Real-Time Market Data Accelerator Template löst diesen Schmerzpunkt, ist sehr einfach zu bedienen, ist vollständig kompatibel mit den aktuell eingebundenen API-Aufrufen, und kann für die meisten ursprünglichen REST-Policies direkt verwendet werden, um Ihre Politik zu beschleunigen.

Die wichtigsten Merkmale:

  • Unterstützt von mehreren BörsenDas Programm unterstützt WebSocket-Verbindungen von mehreren Börsen wie Binance, OKX, ByBit und Bitget. Benutzer können die Verpackungsmethode dieser Vorlage nachmachen und mehr Börsen unterstützen.
  • Abonnements anpassen: Ermöglicht das Abonnieren bestimmter Marktkanäle (z. B. Tiefe, Handel usw.) und effiziente Verarbeitung der eingegangenen Daten für die sofortige Nutzung der Handelsstrategie.
  • Erweiterte Fehlerbehandlung: eingebaute Fehlerverfolgung und WebSocket-Wiederverbindungsmechanismen, um die Zuverlässigkeit und Kontinuität des Datenflusses zu gewährleisten.

Ein einfacher Überblick über die Implementierung

Beachten Sie, dass diese Strategie mit TypeScript verwendet wird, was ein wenig unbekannt erscheinen könnte, wenn Sie nur mit JavaScript vertraut sind. TypeScript führt Typensysteme und reichere Sprachfunktionen auf der Basis von JavaScript ein, die für Anwendungen wie Quantitative Transaktionen, bei denen komplexe Logiken verarbeitet werden müssen, verwendet werden.

Zusätzlich nutzt die Strategie den Asynchronisierungsmechanismus der FMZ-Plattform, mit dem der Subthread einen Nachricht über die __threadPostMessage-Funktion an den Hauptthread senden kann. Diese Methode ist asynchronisiert und eignet sich für die Benachrichtigung des Hauptthreads über die Datenaktualisierung, die in den Subthreads erzeugt werden. Daten können zwischen dem Hauptthread und den Subthreads über __threadGetData und __threadSetData geteilt werden.

Der Hauptprinzip dieser Strategie besteht darin, über WebSocket die Mainstream-Digital-Währungs-Exchanges zu verbinden und in Echtzeit Marktdaten (z. B. Tiefen- und Transaktionsdaten) zu erhalten, um Daten zur Unterstützung von quantitativen Transaktionsentscheidungen zu liefern.

1. WebSocket-Verbindungseinrichtung

setupWebsocketDie Funktion wird verwendet, um die WebSocket-Verbindung zu initialisieren und Marktdaten zu empfangen. Sie empfängt einen Parametermain_exchangesDie Anschlüsse werden von den Benutzern des Netzwerkes angezeigt.

  • MyDialFunktionen: Erstellt WebSocket-Verbindungen, zeichnet die Verbindungszeit auf und liefert die Abschaltzeit beim Abschalten der Verbindung.
  • updateSymbolsFunktionenSie überprüfen regelmäßig, ob neue Abonnementanfragen eingegangen sind, und aktualisieren die Liste der aktuellen Transaktionen, wenn nötig.

2. Datenverarbeitung

supportsObjekte definieren die unterstützten Börsen und ihre Verarbeitungsfunktionen (z. B.BinanceDie Prozessfunktionen der einzelnen Börsen analysieren die eingegangenen Nachrichten und extrahieren die entsprechenden Daten.

  • processMsgFunktionen: Verarbeiten von Messages aus den Börsen, identifizieren verschiedene Arten von Daten (z.B. tiefe Updates, Transaktionen usw.) und formatieren sie in einheitliche Ereignisobjekte;

3. Abonnementdaten

Bei jeder Verbindung wird ein Marktdatenkanal erstellt, der auf die aktuelle Transaktion basiert.

  • getFunctionFunktionen: Erhalten Sie die entsprechenden Verarbeitungsfunktionen nach dem Namen der Börse.
  • this.wssPublicFunktionen: WebSocket-Verbindung initialieren und Datenempfang starten.

4. Threadmanagement

Es wird ein Thread für jede Börse gestartet, die Daten in Echtzeit empfängt und durch Rückruffunktionen verarbeitet wird.

  • threadMarketFunktionen: Erhält Daten, analysiert und speichert die neuesten Tiefen- und Transaktionsinformationen in den Subthreads.

5. Umschreiben der Datenerfassungsmethode

Die Methode, um die Tiefe und die Transaktionsinformationen für jede Börse neu zu schreiben, wird in Echtzeit priorisiert.

Die Verwendung von Vorlagen

  1. EinführungVerwendung:$.setupWebsocket()Die WebSocket-Verbindung der Zielbörse wird initialisiert.
  2. AbonnierenDas System abonniert automatisch Channels für die Art, die Sie handeln (z. B. Tiefe, Handel usw.).
  3. DatenerfassungAufruf:GetDepth()undGetTrades()Funktion, die automatisch mit WebSocket-Realtime-Daten Markttiefe und Transaktionsprotokolle zurückgibt.
  4. Fehlbehandlung: Die Strategie beinhaltet einen Tracking-Mechanismus, der Verbindungs- und Datenfehler erfasst und automatisch erneut versucht wird, wenn die Verbindung unterbrochen wird.

Wenn Sie die EventLoop-Funktion in die Strategie einfügen, wird sie in einen Auslöser umgewandelt, der automatisch sofort abgerufen wird, wenn die WSS-Daten aktualisiert werden, und ohne aktuelle Daten wartet. Äquivalent zur intelligenten Sleep-Funktion.

function main() {
    $.setupWebsocket()
    while (true) {
        exchanges.map(e=>{
            Log(e.GetName(), e.GetDepth())
            Log(e.GetName(), e.GetTrades())
        })
        EventLoop(100) // trigger by websocket
    }
}

Hier ist mein letzter Artikel über die Strategien für den Handel mit verschiedenen Währungen.https://www.fmz.com/digest-topic/10506Hier ist eine sehr einfache Möglichkeit, um die Unterstützung für WebSockets zu ändern:

function MakeOrder() {
    for (let i in Info.trade_symbols) {
        let symbol = Info.trade_symbols[i];
        let buy_price = exchange.GetDepth(symbol + '_USDT').Asks[0].Price;
        let buy_amount = 50 / buy_price;
        if (Info.position[symbol].value < 2000){
            Trade(symbol, "buy", buy_price, buy_amount, symbol);
        }
    }
}

function OnTick() {
    try {
        UpdatePosition();
        MakeOrder();
        UpdateStatus();
    } catch (error) {
        Log("循环出错: " + error);
    }
}

function main() {
    $.setupWebsocket()
    InitInfo();
    while (true) {
        let loop_start_time = Date.now();
        if (Date.now() - Info.time.last_loop_time > Info.interval * 1000) {
            OnTick();
            Info.time.last_loop_time = Date.now();
            Info.time.loop_delay = Date.now() - loop_start_time;
        }
        Sleep(5);
    }
}

Wie kann man neue Börsen selbst hinzufügen?

Wenn Sie die Schablone der Strategie befolgen, können Sie die folgenden Formate kopieren und sich an die API-Dokumentation der Börse wenden:

    supports["Binance"] = function (ctx:ICtx) {
        let processMsg = function (obj) {
            let event = {
                ts: obj.E,
                instId: obj.s,
                depth: null,
                trades: [],
            }

            if (obj.e == "depthUpdate") {
                let depth = {
                    asks: [],
                    bids: []
                }
                obj.b.forEach(function (item) {
                    depth.bids.push({
                        price: Number(item[0]),
                        qty: Number(item[1])
                    })
                })
                obj.a.forEach(function (item) {
                    depth.asks.push({
                        price: Number(item[0]),
                        qty: Number(item[1])
                    })
                })
                event.depth = depth
            } else if (obj.e == 'bookTicker') {
                event.depth = {
                    asks: [{ price: Number(obj.a), qty: Number(obj.A) }],
                    bids: [{ price: Number(obj.b), qty: Number(obj.B) }]
                }
            } else if (obj.e == 'aggTrade') {
                event.ts = obj.E
                event.trades = [{
                    price: Number(obj.p),
                    qty: Number(obj.q),
                    ts: obj.T,
                    side: obj.m ? "sell" : "buy"
                }]
            } else if (typeof (obj.asks) !== 'undefined') {
                event.ts = obj.E || new Date().getTime()
                let depth = {
                    asks: [],
                    bids: []
                }
                obj.bids.forEach(function (item) {
                    depth.bids.push({
                        price: Number(item[0]),
                        qty: Number(item[1])
                    })
                })
                obj.asks.forEach(function (item) {
                    depth.asks.push({
                        price: Number(item[0]),
                        qty: Number(item[1])
                    })
                })
                event.depth = depth
            } else {
                return
            }
            return event
        }
        let channels = ["depth20@100ms", /*"bookTicker", */"aggTrade"]
 
        let ws = null
        let endPoint = "wss://stream.binance.com/stream"
        if (ctx.name == "Futures_Binance") {
            endPoint = "wss://fstream.binance.com/stream"
        }
        
        while (true) {
            if (!ws) {
                let subscribes = []
                ctx.symbols.forEach(function (symbol) {
                    channels.forEach(function (channel) {
                        subscribes.push(symbol.toLowerCase() + "@" + channel)
                    })
                })
                ws = MyDial(endPoint + (subscribes.length > 0 ? ("?streams=" + subscribes.join("/")) : ""))
            }
            if (!ws) {
                Sleep(1000)
                continue
            }
            updateSymbols(ctx, function(symbol:string, method:string) {
                ws.write(JSON.stringify({ 
                    "method": method.toUpperCase(), 
                    "params": channels.map(c=>symbol.toLowerCase()+'@'+c),
                    "id": 2
                }))
            })
            let msg = ws.read(1000)
            if (!msg) {
                if (msg == "") {
                    trace("websocket is closed")
                    ws.close()
                    ws = null
                }
                continue
            }
            if (msg == 'ping') {
                ws.write('pong')
            } else if (msg == 'pong') {

            } else {
                let obj = JSON.parse(msg)
                if (obj.error) {
                    trace(obj.error.msg, "#ff0000")
                    continue
                }
                if (!obj.stream) {
                    continue
                }
                if (obj.stream.indexOf("depth") != -1) {
                    if (typeof(obj.data.s) !== 'string') {
                        // patch
                        obj.data.s = obj.stream.split('@')[0].toUpperCase()
                    }
                }
                let event = processMsg(obj.data)
                if (event) {
                    ctx.callback(event)
                }
            }
        }
    }
    

Mehr