Dans la conception initiale de la stratégie FMZ, si des opérations asynchrones simultanées sont requises, leexchange.Go()
Cette fonction ne peut être utilisée que pour obtenir l'exécution simultanée de l'interface encapsulée FMZ, et il n'est pas possible d'exécuter simultanément certaines opérations personnalisées (fonctions). Bien que cette conception améliore considérablement l'efficacité du programme de stratégie, les étudiants qui ont une expérience dans la conception simultanée dans les langages de programmation natifs se sentent souvent très mal à l'aise.
Même les nouveaux étudiants qui utilisent FMZ pour la négociation quantitative d'introduction peuvent ne pas comprendre l'utilisation de laexchange.Go()
fonction.exchange.Go()
Dans cet article, nous allons explorer l'utilisation de la fonctionnalité de thread concurrent nouvellement ajoutée dans la plateforme FMZ:__Thread()
et autres fonctions connexes, ainsi que la conception asynchrone des programmes stratégiques.
Si nous voulons que le fil principal de la stratégie s'exécute simultanément avec un sous-fil exécutant une fonction personnalisée que nous avons écrite, nous pouvons utiliser une conception similaire au code suivant.GetTickerAsync()
Cette fonction exécute une boucle infinie et appelle continuellement l'interface FMZ APIGetTicker()
pour récupérer des données de marché.
Ensuite, utilisez la déclaration__threadSetData(0, "ticker", t)
pour écrire des données dans le fil principal.ticker
et la valeur des données estt
, qui est la valeur de retour deGetTicker()
.
__threadSetData(0, "ticker", t)
Après avoir conçu la fonction personnalisée pour l'exécution simultanée de thread, nous pouvons écrire le code dans lemain()
Au début de l'annéemain()
fonction, nous utilisons:
__Thread(GetTickerAsync, 0) // GetTickerAsync is a custom function that needs to be executed concurrently, and 0 is the parameter that is passed to the GetTickerAsync function.
Créez un fil en même temps qui commence l'exécution duGetTickerAsync()
La fonctionmain()
fonction commence à exécuter sa proprewhile
La fonction de la boucle, dans laquelle il reçoit les données mises à jour par leGetTickerAsync()
fonctionne et l'imprime:
var t = __threadGetData(0, "ticker")
Log(t)
Exemple de code complet:
function GetTickerAsync(index) {
while (true) {
var t = exchanges[index].GetTicker()
__threadSetData(0, "ticker", t)
Sleep(500)
}
}
function main() {
__Thread(GetTickerAsync, 0)
while(true) {
var t = __threadGetData(0, "ticker")
Log(t)
Sleep(1000)
}
}
Test de négociation en direct:
C'est l'une des conceptions d'application les plus simples, alors regardons quelques autres conceptions d'exigences.
Nous pouvons concevoir une fonction pour créer 10 threads simultanément, chacun exécutant une fonction de placement de commande.main()
fonction, nous pouvons concevoir unwhile
la boucle pour détecter les commandes d'interaction de stratégie.placeMultipleOrders
, nous appelons la fonction de placement de commande simultanéetestPlaceMultipleOrders()
.
if (cmd == "placeMultipleOrders") {
// ...
}
Ajoutez la conception d'interaction de stratégie sur la page d'édition de stratégie en ajoutant un bouton avec la commande: placeMultipleOrders.
Exemple de code complet:
function placeOrder(exIndex, type, price, amount) {
var id = null
if (type == "Buy") {
id = exchanges[exIndex].Buy(price, amount)
} else if (type == "Sell") {
id = exchanges[exIndex].Sell(price, amount)
} else {
throw "type error! type:" + type
}
}
function testPlaceMultipleOrders(index, beginPrice, endPrice, step, type, amount) {
Log("beginPrice:", beginPrice, ", endPrice:", endPrice, ", step:", step, ", type:", type, ", amount:", amount)
var tids = []
for (var p = beginPrice; p <= endPrice; p += step) {
var tid = __Thread(placeOrder, index, type, p, amount)
tids.push(tid)
Sleep(10)
}
Sleep(1000)
for (var i = 0; i < tids.length; i++) {
__threadTerminate(tids[i])
}
}
function main() {
while(true) {
LogStatus(_D())
var cmd = GetCommand()
if (cmd) {
if (cmd == "placeMultipleOrders") {
var t = _C(exchange.GetTicker)
var beginPrice = t.Last * 0.8
var endPrice = t.Last * 0.9
var step = t.Last * 0.01
testPlaceMultipleOrders(0, beginPrice, endPrice, step, "Buy", 0.01)
var orders = exchange.GetOrders()
for (var i = 0; i < orders.length; i++) {
Log(orders[i])
}
}
}
Sleep(1000)
}
}
Après avoir cliqué sur le bouton
Cette exigence a été soulevée par un utilisateur de FMZ qui veut un exemple simple démontrant comment utiliser unWebSocketconnexion dans les threads simultanés et comment transmettre des données à lamain()
fonction dans le fil principal.
En fait, c'est assez simple et similaire à la création de threads simultanés dans les exemples précédents.__threadPeekMessage()
et__threadPostMessage()
Prenant l'appel WebSocket API pour l'échange Binance comme exemple, nous devons également gérer l'opération de fermeture de la connexion WebSocket. L'exemple suivant démontre comment notifier l'arrêt d'un thread concurrent.
Exemple de code complet:
var tid = null
function createWS() {
// wss://stream.binance.com:9443/ws/<streamName> , <symbol>@ticker
var stream = "wss://stream.binance.com:9443/ws/btcusdt@ticker"
var ws = Dial(stream)
Log("Create a WS connection:", stream)
while (true) {
var data = ws.read()
if (data) {
__threadPostMessage(0, data)
}
Log("receiving data pushed by the WS link, data:", data)
// __threadPeekMessage timeout parameter set to -1, no blocking
var msg = __threadPeekMessage(-1)
if (msg) {
if (msg == "stop") {
Log("Concurrent Thread Id:", __threadId(), "Received stop command")
break
}
}
}
Log("Concurrent threads finish execution, close ws connection")
ws.close()
}
function main() {
tid = __Thread(createWS)
Log("Create concurrent threads, thread Id:", tid)
while(true) {
// __threadPeekMessage's timeout parameter is set to 0, blocking for data
var data = __threadPeekMessage(0)
Log("Received from concurrent thread", ", Id:", tid, ", the data sent, data:", data, "#FF0000")
var tbl = {
type : "table",
title : "<symbol>@ticker channel push message",
cols : ["Event Type", "Event Time", "Trading Pairs", "24 Hour Price Change", "24 Hour Price Change %", "Average Price", "Last Traded Price", "Volume in 24 Hours", "Turnover in 24 Hours"],
rows : []
}
try {
data = JSON.parse(data)
tbl.rows.push([data.e, _D(data.E), data.s, data.p, data.P, data.w, data.c, data.v, data.q])
} catch (e) {
Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message)
}
LogStatus(_D(), "\n`" + JSON.stringify(tbl) + "`")
}
}
function onexit() {
Log("Finalize function, send a stop command to the concurrent thread with ID ", tid,"")
__threadPostMessage(tid, "stop")
Log("Wait for the concurrent thread with ID ", tid, " to stop")
__threadJoin(tid)
Log("Finalize function execution completed")
}
Au cours des tests de négociation en direct, nous pouvons voir que lemain()
La fonction reçoit continuellement des données de marché des connexions WebSocket créées par des threads concurrents.
Lors de l'arrêt de la stratégie de trading en direct, la fonction finalize commencera à fonctionner.