Dies ist eine offiziell von FMZ entwickelte WebSocket-Marktvorlage. Kopieren und speichern Sie sie als Vorlage. Wählen Sie diese Vorlage dann in der neuen Strategie aus, um sie zu verwenden: https://www.fmz.com/strategy/470349
Derzeit basiert die FMZ-Strategie hauptsächlich auf der traditionellen REST-API-Kapselung. Jeder API-Zugriff erfordert das Herstellen einer Netzwerkverbindung und das Abrufen von Marktdaten durch Polling. Diese Methode ist einfach und leicht anzuwenden und reicht für die meisten Anforderungen aus.
Das REST-Protokoll weist jedoch inhärente Latenzprobleme auf, die sich noch verstärken, wenn mehrere Handelspaare und mehrere Austauschstrategien erforderlich sind. Obwohl die Go-Funktionen der Plattform gleichzeitig ausgeführt werden können, besteht immer noch das Verzögerungsproblem, was es schwierig macht, die Anforderungen des relativ hochfrequenten Strategiehandels zu erfüllen. Darüber hinaus, wenn es zu viele Handelspaare gibt und die Abfragefrequenz zu hoch ist schnell, wird es an die Zugriffshäufigkeitsbeschränkung der Handelsplattform stoßen. .
Derzeit sind auch die Server der Börsen stark ausgelastet. Sie alle bieten ein vollständiges WebSocket-Protokoll an und empfehlen es API-Benutzern. Im Vergleich zum REST-Protokoll bietet WebSocket eine dauerhafte bidirektionale Verbindungsmethode, die es dem Austausch ermöglicht, Daten in Echtzeit an den Client zu übertragen, wodurch häufige Anforderungen und Antworten vermieden und die Latenz erheblich reduziert wird. Im Allgemeinen gilt: Wenn die Latenz beim Zugriff auf die REST-API etwa 20 ms beträgt, liegt die Latenz beim Pushen von Daten über WebSocket bei etwa 2 ms. Darüber hinaus ist das WebSocket-Protokoll nicht durch die Zugriffshäufigkeit der Plattform beschränkt und es ist grundsätzlich möglich, Dutzende von Handelspaaren gleichzeitig zu abonnieren.
Die quantitative Handelsplattform von FMZ unterstützt das WebSocket-Protokoll schon seit langem und ist relativ bequem aufzurufen. Für unerfahrene Benutzer ist es jedoch immer noch zu kompliziert, mehrere Abonnements zu verwalten, mehrere Börsenkurse zu abonnieren und diese effizient und bequem in die Plattform einzubetten. gesamten Strategieprozess. . Diese öffentliche WebSocket-Vorlage zur Beschleunigung von Marktdaten in Echtzeit löst dieses Problem. Sie ist sehr einfach zu verwenden und vollständig kompatibel mit den aktuellen gekapselten API-Aufrufen. Die meisten der ursprünglichen REST-Strategien können Sie einfach ändern und direkt zur Beschleunigung verwenden. Ihre Strategie.
Hauptmerkmale:
Beachten Sie, dass diese Strategie TypeScript verwendet, das Ihnen möglicherweise etwas ungewohnt vorkommt, wenn Sie nur mit JavaScript vertraut sind. TypeScript führt ein Typsystem und umfangreichere Sprachfunktionen auf Basis von JavaScript ein. Bei Anwendungen wie dem quantitativen Handel, die komplexe Logik verarbeiten müssen, kann die Verwendung von TypeScript potenzielle Fehler reduzieren und die Lesbarkeit und Wartbarkeit des Codes verbessern. Daher ist es empfehlenswert, es einfach zu lernen.
Darüber hinaus verwendet die Strategie den asynchronen Mechanismus der FMZ-Plattform, und der Mechanismus-Subthread kannDie Funktion threadPostMessage sendet eine Nachricht an den Hauptthread. Diese Methode ist asynchron und eignet sich dazu, den Hauptthread über im untergeordneten Thread generierte Datenaktualisierungen zu benachrichtigen. Der Haupt-Thread und der untergeordnete Thread können verbunden werden durchthreadGetData und__Die Funktion threadSetData gibt Daten frei. Dieser Ansatz ermöglicht es Threads, auf den gemeinsamen Status zuzugreifen und ihn zu ändern. Wenn Sie etwas über Multithreading lernen möchten, ist diese Strategie in Verbindung mit der Plattformdokumentation auch ein gutes Lernbeispiel.
Das Hauptprinzip dieser Strategie besteht darin, sich über WebSocket mit den gängigen digitalen Währungsbörsen zu verbinden und Marktdaten (wie Tiefeninformationen und Transaktionsinformationen) in Echtzeit zu empfangen, um Datenunterstützung für quantitative Handelsentscheidungen bereitzustellen. Der konkrete Implementierungsprozess läuft wie folgt ab:
1. WebSocket-Verbindungseinstellungen
setupWebsocket
Diese Funktion wird verwendet, um eine WebSocket-Verbindung zu initialisieren und Marktdaten zu empfangen. Es empfängt einen Parametermain_exchanges
, die die Vermittlungsstelle angibt, die verbunden werden muss.
MyDial
Funktion: Erstellen Sie eine WebSocket-Verbindung, zeichnen Sie die Verbindungszeit auf und geben Sie die Schließzeit aus, wenn die Verbindung geschlossen wird.updateSymbols
Funktion: Prüfen Sie regelmäßig, ob neue Abonnementanfragen vorliegen, und aktualisieren Sie die aktuelle Handelspaarliste nach Bedarf.2. Datenverarbeitung
supports
Das Objekt definiert die unterstützten Börsen und ihre Verarbeitungsfunktionen (wieBinance
). Die Verarbeitungsfunktion jeder Börse ist für das Parsen der empfangenen Nachrichten und das Extrahieren relevanter Daten verantwortlich.
processMsg
Funktion: Verarbeiten Sie Nachrichten von Börsen, identifizieren Sie verschiedene Datentypen (z. B. Tiefenaktualisierungen, Transaktionen usw.) und formatieren Sie sie in einheitliche Ereignisobjekte.3. Abonnementdaten
Bei jeder Verbindung abonniert das System die relevanten Marktdatenkanäle basierend auf dem aktuellen Handelspaar.
getFunction
Funktion: Holen Sie sich die entsprechende Verarbeitungsfunktion entsprechend dem Börsennamen.this.wssPublic
Funktion: Initialisieren Sie die WebSocket-Verbindung und beginnen Sie mit dem Empfangen von Daten.4. Thread-Verwaltung
Starten Sie für jeden Austausch einen Thread, empfangen Sie Daten in Echtzeit und verarbeiten Sie die Daten über Rückruffunktionen.
threadMarket
Funktion: Daten im untergeordneten Thread empfangen, die neuesten Tiefen- und Transaktionsinformationen analysieren und speichern.5. Schreiben Sie die Datenerfassungsmethode neu
Schreiben Sie die Methoden zum Abrufen von Tiefen- und Handelsinformationen für jede Börse neu und geben Sie dabei der Rückgabe von in Echtzeit aktualisierten Daten den Vorzug.
$.setupWebsocket()
Initialisieren Sie die WebSocket-Verbindung zum Zielaustausch.GetDepth()
Und GetTrades()
Funktion, verwendet automatisch WebSocket-Echtzeitdaten, um Markttiefe und Transaktionsaufzeichnungen zurückzugeben.Wenn der Strategie die Funktion EventLoop () hinzugefügt wird, wird sie in einen Auslösemechanismus geändert. Wenn die WSS-Daten aktualisiert werden, werden sie automatisch sofort abgerufen, und wenn keine neuesten Daten vorhanden sind, wird gewartet. Es entspricht einer intelligenten Sleep-Funktion. Natürlich kannst du Sleep auch direkt nutzen.
function main() {
$.setupWebsocket()
while (true) {
exchanges.map(e=>{
Log(e.GetName(), e.GetDepth())
Log(e.GetName(), e.GetTrades())
})
EventLoop(100) // trigger by websocket
}
}
Weitere Informationen finden Sie in meinem vorherigen Leitfaden zu Handelsstrategien für mehrere Währungen https://www.fmz.com/digest-topic/10506. Dort können Sie ihn ganz einfach zur Unterstützung von WebSocket ä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);
}
}
Folgen Sie einfach der Strategievorlage, ahmen Sie das folgende Format nach und lesen Sie die Dokumentation der Exchange-API:
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)
}
}
}
}