En el diseño inicial de la estrategia FMZ, si se requieren operaciones asíncronas simultáneas, laexchange.Go()
Aunque este diseño mejora en gran medida la eficiencia del programa de estrategia, los estudiantes que tienen experiencia en diseño concurrente en lenguajes de programación nativos a menudo se sienten muy incómodos.
Incluso los nuevos estudiantes que utilizan FMZ para la introducción al comercio cuantitativo pueden no entender el uso de laexchange.Go()
El uso deexchange.Go()
En este artículo, exploraremos el uso de la funcionalidad de hilo concurrente recientemente agregada en la plataforma FMZ:__Thread()
y otras funciones relacionadas, así como el diseño asincrónico de programas de estrategia.
Si queremos que el hilo principal de la estrategia se ejecute simultáneamente con un sub- hilo ejecutando una función personalizada que hemos escrito, podemos usar un diseño similar al siguiente código.GetTickerAsync()
Esta función ejecuta un bucle infinito y llama continuamente a la interfaz FMZ APIGetTicker()
para obtener datos de mercado.
Entonces, usa la declaración__threadSetData(0, "ticker", t)
El nombre de los datos esticker
y el valor de los datos est
, que es el valor de retorno deGetTicker()
.
__threadSetData(0, "ticker", t)
Después de diseñar la función personalizada para la ejecución simultánea de hilos, podemos escribir el código en elmain()
En el comienzo de lamain()
función, usamos:
__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.
Crear un hilo concurrente que comience a ejecutar elGetTickerAsync()
En el caso de lasmain()
La función comienza a ejecutar su propiawhile
En el circuito, en el que recibe los datos actualizadosGetTickerAsync()
Funciona y lo imprime:
var t = __threadGetData(0, "ticker")
Log(t)
Ejemplo de código completo:
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)
}
}
Prueba de negociación en vivo:
Este es uno de los diseños de aplicaciones más simples, así que vamos a ver algunos otros diseños de requisitos.
Podemos diseñar una función para crear 10 hilos simultáneamente, cada uno ejecutando una función de orden.main()
Función, podemos diseñar unwhile
cuando recibimos el comando de interacciónplaceMultipleOrders
, llamamos a la función de colocación de órdenes concurrentestestPlaceMultipleOrders()
.
if (cmd == "placeMultipleOrders") {
// ...
}
Añadir diseño de interacción de la estrategia en la página de edición de la estrategia mediante la adición de un botón con el comando: placeMultipleOrders.
Ejemplo de código completo:
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)
}
}
Después de hacer clic en el botón
Este requisito fue planteado por un usuario de FMZ que desea un ejemplo simple que demuestre cómo utilizar unWebSocket y el sistema operativoconexión en hilos concurrentes y cómo transmitir datos a lamain()
función en el hilo principal.
En realidad, es bastante simple y similar a la creación de hilos concurrentes en los ejemplos anteriores.__threadPeekMessage()
y__threadPostMessage()
Tomando la llamada WebSocket API para el intercambio Binance como ejemplo, también necesitamos manejar la operación de cierre de la conexión WebSocket. El siguiente ejemplo demuestra cómo notificar a un hilo concurrente para detenerse.
Ejemplo de código completo:
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")
}
Durante las pruebas de comercio en vivo, podemos ver que elmain()
La función recibe continuamente datos de mercado de las conexiones WebSocket creadas por hilos concurrentes.
Al detener la estrategia de negociación en vivo, la función de finalización comenzará a funcionar.