
Trong thiết kế chiến lược FMZ ban đầu, nếu các hoạt động đồng thời không đồng bộ được yêu cầu, chỉexchange.Go()Hàm được sử dụng để triển khai tính đồng thời của giao diện đóng gói FMZ và một số hoạt động tùy chỉnh (hàm) không thể được thực thi đồng thời. Mặc dù thiết kế này cải thiện đáng kể hiệu quả thực hiện chương trình chính sách, nhưng nó vẫn còn rất xa lạ với những sinh viên có kinh nghiệm thiết kế đồng thời bằng ngôn ngữ lập trình gốc.
Ngay cả một số sinh viên mới làm quen với giao dịch định lượng bằng FMZ cũng không hiểuexchange.Go()Sử dụng các hàm, sử dụngexchange.Go()Có vẻ như các câu lệnh vẫn được thực thi từng câu một trong mã được thực thi tuần tự. Trong bài viết này, chúng ta hãy cùng khám phá các tính năng luồng đồng thời mới của nền tảng FMZ:__Thread()Việc sử dụng một loạt các chức năng và chiến lược chương trình thiết kế không đồng bộ.
Nếu chúng ta muốn chạy một luồng con đồng thời để thực thi một hàm tùy chỉnh mà chúng ta đã viết trong khi luồng chính chiến lược đang chạy, chúng ta có thể sử dụng thiết kế tương tự như mã sau. Tùy chỉnh một chức năng trong mã chiến lượcGetTickerAsync(), hãy viết chức năng cụ thể của hàm này. Hàm này thực hiện một vòng lặp vô hạn.whileGiao diện API FMZ được gọi liên tục trong một vòng lặp:GetTicker()Để có được dữ liệu thị trường.
Sau đó sử dụng__threadSetData(0, "ticker", t)Câu này ghi dữ liệu vào luồng chính. Tên dữ liệu làticker, giá trị dữ liệu làtNgay lập tứcGetTicker()Giá trị trả về của .
__threadSetData(0, "ticker", t)
Sau khi thiết kế hàm tùy chỉnh để thực thi đồng thời các luồng, chúng ta có thể viếtmain()Mã trong hàm làmain()Khi bắt đầu hàm, chúng ta sử dụng:
__Thread(GetTickerAsync, 0) // GetTickerAsync为需要并发执行的自定义函数,0为这个传入GetTickerAsync函数的参数
Tạo một luồng đồng thời, bắt đầu thực thiGetTickerAsync()chức năng. sau đómain()Chức năng bắt đầu thực hiệnwhileVòng lặp, nhận trong vòng lặpGetTickerAsync()Hàm này cập nhật dữ liệu và sau đó in ra:
var t = __threadGetData(0, "ticker")
Log(t)
Ví dụ mã đầy đủ:
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)
}
}
Kiểm tra hoạt động của đĩa thực tế:

Đây là thiết kế ứng dụng đơn giản nhất. Tiếp theo, chúng ta hãy xem một số thiết kế nhu cầu khác.
Có thể thiết kế một hàm để tạo ra 10 luồng cùng lúc và mỗi luồng thực thi một hàm thao tác lệnh. hiện hữumain()Thiết kế một chức năngwhileVòng lặp, hướng dẫn tương tác chiến lược phát hiện. Nhận hướng dẫn tương tác:placeMultipleOrdersChỉ cần gọi hàm thứ tự đồng thời nàytestPlaceMultipleOrders()。
if (cmd == "placeMultipleOrders") {
// ...
}
Thêm thiết kế tương tác chiến lược vào trang chỉnh sửa chiến lược và đặt nút bằng lệnh: placeMultipleOrders

Ví dụ mã đầy đủ:
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)
}
}



Yêu cầu này được đưa ra bởi một người dùng FMZ muốn có một ví dụ đơn giản để chứng minh cách sử dụng nó trong các luồng đồng thời.WebSocketKết nối và thiết kế cách truyền dữ liệu đến luồng chínhmain()chức năng.
Trên thực tế, nó rất đơn giản và tương tự như việc tạo các luồng đồng thời trong ví dụ trước. Chỉ sử dụng giao tiếp giữa các luồng__threadPeekMessage()Chức năng và__threadPostMessage()chức năng. Lấy ví dụ về lệnh gọi giao diện API WebSocket của Binance Exchange, chúng ta cũng cần chú ý đến thao tác đóng kết nối WebSocket trong thiết kế. Ví dụ sau đây cũng cho thấy cách thông báo cho một luồng đồng thời dừng nó.
Ví dụ mã đầy đủ:
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("创建WS连接:", stream)
while (true) {
var data = ws.read()
if (data) {
__threadPostMessage(0, data)
}
Log("接收到WS链接推送的数据,data:", data)
// __threadPeekMessage 超时参数设置-1,不阻塞
var msg = __threadPeekMessage(-1)
if (msg) {
if (msg == "stop") {
Log("并发线程Id:", __threadId(), "接收到stop指令")
break
}
}
}
Log("并发线程执行完毕,关闭ws连接")
ws.close()
}
function main() {
tid = __Thread(createWS)
Log("创建并发线程,线程Id:", tid)
while(true) {
// __threadPeekMessage 的超时参数设置为0,阻塞等待数据
var data = __threadPeekMessage(0)
Log("接收到并发线程", ", Id:", tid, ", 发送的数据,data:", data, "#FF0000")
var tbl = {
type : "table",
title : "<symbol>@ticker频道推送消息",
cols : ["事件类型", "事件时间", "交易对", "24小时价格变化", "24小时价格变化百分比", "平均价格", "最新成交价格", "24小时内成交量", "24小时内成交额"],
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("扫尾函数,向Id为", tid, "的并发线程发送stop指令")
__threadPostMessage(tid, "stop")
Log("等待Id为", tid, "的并发线程停止")
__threadJoin(tid)
Log("扫尾函数执行完毕")
}
Kiểm tra hoạt động của đĩa thực tế:

Bạn có thể thấymain()Chức năng này liên tục nhận dữ liệu thị trường từ kết nối WebSocket được tạo bởi luồng đồng thời.
Khi chiến lược dừng lại, chức năng quét sẽ bắt đầu hoạt động:
