
Il s’agit d’un modèle de marché WebSocket officiellement développé par FMZ. Copiez-le et enregistrez-le en tant que modèle, puis sélectionnez ce modèle dans la nouvelle stratégie pour l’utiliser : https://www.fmz.com/strategy/470349
Actuellement, la stratégie FMZ repose principalement sur l’encapsulation traditionnelle des API REST. Chaque accès à l’API nécessite l’établissement d’une connexion réseau pour obtenir des données de marché par sondage. Cette méthode est simple et facile à utiliser, et suffit à la plupart des besoins.
Cependant, le protocole REST présente des problèmes de latence inhérents, qui seront amplifiés lorsque plusieurs paires de trading et plusieurs stratégies d’échange seront nécessaires. Bien que les fonctions Go de la plateforme puissent être exécutées simultanément, le problème de retard existe toujours, ce qui rend difficile de répondre aux besoins de trading de stratégie à fréquence relativement élevée. De plus, s’il y a trop de paires de trading et que la fréquence d’interrogation est trop élevée, rapidement, il rencontrera la limite de fréquence d’accès de la plateforme de trading. .
Actuellement, les serveurs des plateformes d’échange sont également soumis à une forte charge. Ils fournissent tous un protocole WebSocket complet et le recommandent aux utilisateurs de l’API. Comparé au protocole REST, WebSocket fournit une méthode de connexion bidirectionnelle persistante, qui permet à l’échange de transmettre des données au client en temps réel, évitant ainsi les demandes et réponses fréquentes et réduisant considérablement la latence. D’une manière générale, si la latence d’accès à l’API REST est d’environ 20 ms, la latence de transmission des données via WebSocket est d’environ 2 ms. De plus, le protocole WebSocket n’est pas limité par la fréquence d’accès de la plateforme, et il est en principe possible de s’abonner à des dizaines de paires de trading à la fois.
La plateforme de trading quantitative FMZ prend en charge le protocole WebSocket depuis longtemps, et il est relativement facile à appeler, mais pour les utilisateurs novices, il est encore trop compliqué de gérer plusieurs abonnements, de souscrire à plusieurs cotations boursières et de les intégrer efficacement et facilement dans l’ensemble du processus stratégique. . Ce modèle d’accélération des données de marché en temps réel WebSocket public résout ce problème. Il est très facile à utiliser et entièrement compatible avec les appels d’API encapsulés actuels. Pour la plupart des stratégies REST d’origine, vous pouvez simplement les modifier et les utiliser directement pour accélérer votre stratégie.
Caractéristiques principales :
Notez que cette stratégie utilise TypeScript, ce qui peut sembler un peu inhabituel si vous n’êtes familier qu’avec JavaScript. TypeScript introduit un système de types et des fonctionnalités de langage plus riches basées sur JavaScript. Pour les applications telles que le trading quantitatif qui doivent traiter une logique complexe, l’utilisation de TypeScript peut réduire les erreurs potentielles et améliorer la lisibilité et la maintenabilité du code. Il est donc recommandé de l’apprendre simplement.
De plus, la stratégie utilise le mécanisme asynchrone de la plateforme FMZ, et le sous-thread du mécanisme peut êtreLa fonction threadPostMessage envoie un message au thread principal. Cette méthode est asynchrone et convient pour notifier au thread principal les mises à jour de données générées dans le thread enfant. Le thread principal et le thread enfant peuvent être connectés viathreadGetData et__La fonction threadSetData partage des données. Cette approche permet aux threads d’accéder et de modifier l’état partagé. Si vous souhaitez en savoir plus sur le multithreading, cette stratégie est également un bon exemple d’apprentissage en conjonction avec la documentation de la plateforme.
Le principe principal de cette stratégie est de se connecter aux principaux échanges de devises numériques via WebSocket et de recevoir des données de marché (telles que des informations approfondies et des informations sur les transactions) en temps réel pour fournir un support de données pour les décisions de trading quantitatives. Le processus spécifique de mise en œuvre est le suivant :
1. Paramètres de connexion WebSocket
setupWebsocket Cette fonction est utilisée pour initialiser une connexion WebSocket et recevoir des données de marché. Il reçoit un paramètremain_exchanges, indiquant l’échange qui doit être connecté.
MyDial fonction: Créez une connexion WebSocket, enregistrez l’heure de connexion et affichez l’heure de fermeture lorsque la connexion est fermée.updateSymbols fonction:Vérifiez régulièrement les nouvelles demandes d’abonnement et mettez à jour la liste des paires de trading actuelles si nécessaire.2. Traitement des données
supports L’objet définit les échanges pris en charge et leurs fonctions de traitement (telles queBinance). La fonction de traitement de chaque échange est chargée d’analyser les messages reçus et d’extraire les données pertinentes.
processMsg fonction: Traitez les messages des échanges, identifiez différents types de données (telles que les mises à jour de profondeur, les transactions, etc.) et formatez-les en objets d’événements unifiés.3. Données d’abonnement
À chaque connexion, le système s’abonnera aux canaux de données de marché pertinents en fonction de la paire de trading actuelle.
getFunction fonction:Obtenez la fonction de traitement correspondante en fonction du nom de l’échange.this.wssPublic fonction: Initialisez la connexion WebSocket et commencez à recevoir des données.4. Gestion des threads
Démarrez un fil pour chaque échange, recevez des données en temps réel et traitez les données via des fonctions de rappel.
threadMarket fonction:Recevez les données dans le thread enfant, analysez et stockez les dernières informations de profondeur et de transaction.5. Réécrire la méthode d’acquisition des données
Réécrire les méthodes d’obtention d’informations de profondeur et de trading pour chaque bourse, en donnant la priorité au retour de données mises à jour en temps réel.
$.setupWebsocket() Initialisez la connexion WebSocket à l’échange cible.GetDepth() et GetTrades() Fonction, utilise automatiquement les données WebSocket en temps réel pour renvoyer la profondeur du marché et les enregistrements de transactions.Si la fonction EventLoop() est ajoutée à la stratégie, elle sera transformée en mécanisme de déclenchement. Lorsque les données wss sont mises à jour, elles seront automatiquement obtenues immédiatement, et s’il n’y a pas de données récentes, elles attendront. Cela équivaut à une fonction Sleep intelligente. Bien entendu, vous pouvez également utiliser Sleep directement.
function main() {
$.setupWebsocket()
while (true) {
exchanges.map(e=>{
Log(e.GetName(), e.GetDepth())
Log(e.GetName(), e.GetTrades())
})
EventLoop(100) // trigger by websocket
}
}
Reportez-vous à mon guide précédent sur les stratégies de trading multidevises https://www.fmz.com/digest-topic/10506, où il peut être facilement modifié pour prendre en charge WebSocket :
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);
}
}
Suivez simplement le modèle de stratégie, imitez le format suivant et reportez-vous à la documentation de l’API d’échange :
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)
}
}
}
}