[TOC]
Với sự phát triển nhanh chóng của thị trường tài chính và sự phổ biến của giao dịch định lượng, ngày càng có nhiều nhà giao dịch bắt đầu dựa vào các chiến lược tự động để giao dịch. Trong quá trình này, giao tiếp và phối hợp giữa các chiến lược đặc biệt quan trọng.
Bài viết này sẽ khám phá giao thức giao dịch trực tiếp của các chiến lược giao dịch trong nền tảng FMZ, giới thiệu khái niệm thiết kế, tính năng chức năng và lợi thế của nó trong các ứng dụng thực tế. Thông qua phân tích trường hợp chi tiết, chúng tôi sẽ cho thấy cách sử dụng giao thức này để đạt được giao tiếp chiến lược hiệu quả và ổn định và cải thiện hiệu suất thực hiện và lợi nhuận của các chiến lược giao dịch.
Cho dù bạn là một người đam mê giao dịch định lượng chỉ mới bắt đầu với FMZ hoặc là một lập trình viên chuyên nghiệp có kinh nghiệm, bài viết này sẽ cung cấp cho bạn những hiểu biết có giá trị và hướng dẫn hoạt động thực tế.
Các kịch bản nhu cầu này chứng minh các khả năng và lợi thế khác nhau của giao thức giao dịch giao dịch trực tiếp trong các ứng dụng thực tế. Thông qua giao tiếp giữa các chiến lược hiệu quả, các nhà giao dịch có thể đối phó tốt hơn với môi trường thị trường phức tạp, tối ưu hóa các chiến lược giao dịch và cải thiện hiệu quả giao dịch và lợi nhuận.
Sau khi hiểu được các yêu cầu giao tiếp giữa các giao dịch trực tiếp, chúng ta cần xem xét cách thực hiện các yêu cầu này. Nó không gì khác ngoài giao dịch trực tiếp A hy vọng trao đổi thông tin với giao dịch trực tiếp B. Mặc dù các yêu cầu có vẻ đơn giản, nhưng có nhiều chi tiết cần phải được đồng ý khi sử dụng một bộ giao thức giao tiếp. FMZ đã kết hợp một số giao thức giao tiếp phổ biến.
mqtt / nats / amqp / kafka
Kiến trúc truyền thông là:
Khi áp dụng các giao thức này trên nền tảng FMZ, bạn có thể hiểu đơn giản rằng các giao thức mqtt / nats / amqp / kafkap được tích hợp vàoDial()
chức năng, vàDial()
function được sử dụng để xuất bản và đăng ký tin nhắn. Những tin nhắn được xuất bản này được proxy (chuyển tiếp) đến giao dịch trực tiếp được đăng ký thông qua máy chủ giao thức, vì vậy máy chủ giao thức phải được chạy trước. Để chứng minh, chúng tôi sử dụng các triển khai hình ảnh máy chủ giao thức khác nhau trong các ví dụ sau.
Chức năng quay số trong phần tài liệu API:https://www.fmz.com/syntax-guide#fun_dial
Trước khi triển khai hình ảnh docker, hãy nhớ cài đặt phần mềm docker trước.
Tiếp theo, chúng ta hãy khám phá và thực hành các ứng dụng giao thức liên lạc được hỗ trợ bởi FMZ.
MQTT (Message Queuing Telemetry Transport) là một giao thức truyền thông thông điệp nhẹ đặc biệt phù hợp với môi trường mạng băng thông thấp, độ trễ cao hoặc không đáng tin cậy. Nó được đề xuất bởi Andy Stanford-Clark và Arlen Nipper của IBM vào năm 1999 và sau đó trở thành tiêu chuẩn ISO (ISO / IEC PRF 20922).
Các tính năng chính của giao thức MQTT: chế độ xuất bản/đăng ký
Bởi vì chúng tôi sử dụng hình ảnh docker (hình ảnh muỗi nhật thực) của phần mềm hỗ trợ giao thức MQTT để triển khai máy chủ proxy MQTT, chúng tôi đã cài đặt docker trước và sẽ không đi sâu vào chi tiết sau.
Trước khi chạy lệnh để triển khai hình ảnh, chúng tôi cần phải viết một máy chủ proxy cấu hình tệpmosquitto.conf
.
# Configure port number and remote access IP
listener 1883 0.0.0.0
# Setting up anonymous access
allow_anonymous true
Sau đó thực hiện lệnh triển khai:
docker run --rm -p 1883:1883 -v ./mosquitto.conf:/mosquitto/config/mosquitto.conf eclipse-mosquitto
Sau khi hình ảnh máy chủ proxy được chạy, sau đây được hiển thị:
1723012640: mosquitto version 2.0.18 starting
1723012640: Config loaded from /mosquitto/config/mosquitto.conf.
1723012640: Opening ipv4 listen socket on port 1883.
1723012640: mosquitto version 2.0.18 running
Sau đó, chúng ta có thể kiểm tra chiến lược để thực hiện nó.
var conn = null
function main() {
LogReset(1)
var robotId = _G()
Log("Current live trading robotId:", robotId)
conn = Dial("mqtt://127.0.0.1:1883?topic=test_topic")
if (!conn) {
Log("Communication failure!")
return
}
for (var i = 0; i < 10; i++) {
// Write
var msg = "i: " + i + ", testQueue, robotA, robotId: " + robotId + ", time:" + _D()
conn.write(msg)
Log("Write a message to testQueue:", msg)
// Read
Log("read:", conn.read(1000), "#FF0000")
Sleep(1000)
}
}
function onexit() {
conn.close()
Log("close conn")
}
Việc sử dụng chính của chức năng Dial trong mã chiến lược là:
Dial("mqtt://127.0.0.1:1883?topic=test_topic")
Các tham số chuỗi của hàm Dial bắt đầu vớimqtt://
, đó là tên giao thức, theo sau là địa chỉ nghe và cổng. Biểu tượng test_topic
.
Chiến lược trên xuất bản và đăng ký một chủ đề cùng một lúc.
Chúng tôi cũng có thể sử dụng hai giao dịch trực tiếp để đăng ký với nhau và xuất bản thông tin chủ đề. Chúng tôi sử dụng một ví dụ như vậy trong phần thực hành giao thức nats, và sẽ không lặp lại phương pháp này trong các giao thức khác.
Giao thức NATS là một giao thức kiểu publish/subscribe đơn giản dựa trên văn bản. Khách hàng kết nối với gnatsd (nền chủ NATS) và giao tiếp với gnatsd. Giao tiếp dựa trên các ổ cắm TCP/IP thông thường và xác định một tập hợp rất nhỏ các hoạt động. Newline chỉ ra kết thúc. Không giống như các hệ thống truyền thông tin nhắn truyền thống sử dụng định dạng tin nhắn nhị phân, giao thức NATS dựa trên văn bản làm cho việc thực hiện khách hàng rất đơn giản và có thể dễ dàng được thực hiện trong nhiều ngôn ngữ lập trình hoặc ngôn ngữ kịch bản.
Mỗi Nghị định thư có đặc điểm riêng của nó. Bạn có thể tham khảo các tài liệu và tài liệu cụ thể, mà sẽ không được chi tiết ở đây.
Xây dựng máy chủ giao thức NATS:
docker run
name nats rm -p 4222:4222 -p 8222:8222 nats http_port 8222 auth admin
Điều này sẽ tự động tải xuống và chạy hình ảnh nats, và cổng 4222 là cổng mà khách hàng cần truy cập. Sau khi hình ảnh được triển khai, một màn hình http cũng sẽ được mở trên cổng 8222.
Listening for client connections on 0.0.0.0:4222
Server is ready
Hình ảnh máy chủ Nats bắt đầu chạy, nghe trên cổng 4222.
Chúng ta cần tạo ra hai chiến lược (giao dịch trực tiếp), hãy đặt tên cho chúng là Chiến lược A và Chiến lược B. Mã của hai chiến lược này về cơ bản là giống nhau. Chúng được viết bằng Javascript, đó là ngôn ngữ dễ sử dụng nhất trên nền tảng FMZ.
var connPub = null
var connSub = null
function main() {
var robotId = _G()
Log("Current live trading robotId:", robotId)
connPub = Dial("nats://admin@127.0.0.1:4222?topic=pubRobotA")
if (!connPub) {
Log("Communication failure!")
return
}
connSub = Dial("nats://admin@127.0.0.1:4222?topic=pubRobotB")
if (!connSub) {
Log("Communication failure!")
return
}
while (true) {
connPub.write("Message posted by robotA, robotId: " + robotId + ", time:" + _D())
var msgRead = connSub.read(10000)
if (msgRead) {
Log("msgRead:", msgRead)
}
LogStatus(_D())
Sleep(10000)
}
}
function onexit() {
connPub.close()
connSub.close()
}
var connPub = null
var connSub = null
function main() {
var robotId = _G()
Log("Current live trading robotId:", robotId)
connPub = Dial("nats://admin@127.0.0.1:4222?topic=pubRobotB")
if (!connPub) {
Log("Communication failure!")
return
}
connSub = Dial("nats://admin@127.0.0.1:4222?topic=pubRobotA")
if (!connSub) {
Log("Communication failure!")
return
}
while (true) {
connPub.write("Message posted by robotB, robotId: " + robotId + ", time:" + _D())
var msgRead = connSub.read(10000)
if (msgRead) {
Log("msgRead:", msgRead)
}
LogStatus(_D())
Sleep(10000)
}
}
function onexit() {
connPub.close()
connSub.close()
}
Hai chiến lược này gần như giống nhau, ngoại trừ việc chúng xuất bản và đăng ký với nhau, và các chủ đề đăng ký, các chủ đề được xuất bản và thông tin được xuất bản khác nhau.
Hãy lấy chiến lược B làm ví dụ:
Dial()
chức năng để tạo đối tượng máy chủ kết nối khách hàngconnPub
đối với việc xuất bản tin nhắn chủ đề:var connPub = Dial(
nats://admin@ 127.0.0.1:4222?topic=pubRobotB”)
Dòng tham số của hàm Dial bắt đầu vớinats://
chỉ ra rằng giao thức NATS được sử dụng để liên lạc.admin
là thông tin xác minh đơn giảnauth admin
được thiết lập khi triển khai hình ảnh Docker. ký tự 127.0.0.1:4222
Cuối cùng, có chủ đề xuất bản/đăng ký:topic=pubRobotB
Lưu ý rằng ký hiệu
Dial()
chức năng để tạo đối tượng máy chủ kết nối khách hàngconnSub
cho đăng ký tin nhắn chủ đề:var connSub = Dial(
nats://admin@ 127.0.0.1:4222?topic=pubRobotA”)
Sự khác biệt duy nhất làtopic=pubRobotA
, bởi vì chúng ta cần phải đăng ký chủ đềpubRobotA
nơi chiến lược A gửi thông tin.
Việc tạo và sử dụng các đối tượng kết nối đăng ký và xuất bản trong chiến lược A giống như được mô tả ở trên.
Bằng cách này, một ví dụ đơn giản về ứng dụng giao thức NATS được thực hiện trong đó giao dịch trực tiếp A và giao dịch trực tiếp B đăng ký và xuất bản tin nhắn để giao tiếp với nhau.
Trong giao tiếp không đồng bộ, thông điệp sẽ không đến được người nhận ngay lập tức, nhưng nó sẽ được lưu trữ trong một container. Khi các điều kiện nhất định được đáp ứng, thông điệp sẽ được gửi đến người nhận bởi container. Container này là hàng đợi tin nhắn. Để hoàn thành chức năng này, cả hai bên và container và các thành phần của nó phải tuân thủ các thỏa thuận và quy tắc thống nhất. AMQP là một giao thức như vậy. Cả người gửi và người nhận thông điệp đều có thể thực hiện giao tiếp không đồng bộ bằng cách tuân thủ giao thức này.
Mỗi Nghị định thư có đặc điểm riêng của nó. Bạn có thể tham khảo các tài liệu và tài liệu cụ thể, mà sẽ không được chi tiết ở đây.
triển khai máy chủ giao thức amqp:
docker run
rm hostname my-rabbit name rabbit -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=q -e RABBITMQ_DEFAULT_PASS=admin rabbitmq:3-management
Khi triển khai một hình ảnh docker, nó sẽ tải xuống và triển khai tự động, và khi hoàn thành nó sẽ hiển thị:
2024-08-06 09:02:46.248936+00:00 [info] <0.9.0> Time to start RabbitMQ: 15569 ms
Sau khi hình ảnh máy chủ được triển khai, hãy viết một ví dụ thử nghiệm:
var conn = null
function main() {
LogReset(1)
var robotId = _G()
Log("Current live trading robotId:", robotId)
conn = Dial("amqp://q:admin@127.0.0.1:5672/?queue=robotA_Queue")
if (!conn) {
Log("Communication failure!")
return
}
for (var i = 0; i < 10; i++) {
// Read
Log("read:", conn.read(1000), "#FF0000")
// Write
var msg = "i: " + i + ", testQueue, robotA, robotId: " + robotId + ", time:" + _D()
conn.write(msg)
Log("Write a message to testQueue:", msg)
Sleep(1000)
}
}
function onexit() {
conn.close()
Log("close conn")
}
Khi sử dụng hàng đợi giao thức AMQP, xin lưu ý rằng các tin nhắn được xuất bản sẽ tồn tại trong hàng đợi. Ví dụ, nếu chúng ta chạy mã ví dụ trên, 10 tin nhắn sẽ được ghi vào hàng đợi. Sau đó khi chúng ta chạy lần thứ hai, chúng ta có thể thấy rằng tin nhắn được viết lần đầu tiên sẽ được đọc lại khi đọc. Như được hiển thị trong hình:
Chúng ta có thể thấy rằng hai tin nhắn nhật ký được chỉ bởi các mũi tên màu đỏ trong ảnh chụp màn hình có thời gian không nhất quán. Lý do là vì tin nhắn màu đỏ là một trong những được đọc và viết vào hàng đợi khi mã chiến lược lần đầu tiên được chạy.
Dựa trên tính năng này, một số yêu cầu có thể được đáp ứng. ví dụ, sau khi chiến lược được khởi động lại, dữ liệu thị trường được ghi lại vẫn có thể được lấy từ hàng đợi để tính toán khởi tạo và các hoạt động khác.
Apache Kafka là một bộ lưu trữ dữ liệu phân tán được tối ưu hóa để hấp thụ và xử lý dữ liệu trực tuyến trong thời gian thực. Dữ liệu trực tuyến là dữ liệu được liên tục tạo ra bởi hàng ngàn nguồn dữ liệu, thường gửi các bản ghi dữ liệu đồng thời. Một nền tảng trực tuyến cần xử lý dòng dữ liệu liên tục này, xử lý nó theo trình tự và gia tăng.
Kafka cung cấp ba chức năng chính cho người dùng:
Kafka chủ yếu được sử dụng để xây dựng các đường ống truyền dữ liệu thời gian thực và các ứng dụng thích nghi với luồng dữ liệu. Nó kết hợp các khả năng nhắn tin, lưu trữ và xử lý luồng, và có thể lưu trữ dữ liệu lịch sử và thời gian thực.
Xây dựng hình ảnh docker của proxy Kafka:
docker run --rm --name kafka-server --hostname kafka-server -p 9092:9092 -p 9093:9093 \
-e KAFKA_CFG_NODE_ID=0 \
-e KAFKA_CFG_PROCESS_ROLES=controller,broker \
-e KAFKA_CFG_LISTENERS=PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093 \
-e KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 \
-e KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT \
-e KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka-server:9093 \
-e KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER \
bitnami/kafka:latest
Kiểm tra bằng mã kiểm tra:
var conn = null
function main() {
LogReset(1)
var robotId = _G()
Log("Current live trading robotId:", robotId)
conn = Dial("kafka://localhost:9092/test_topic")
if (!conn) {
Log("Communication failure!")
return
}
for (var i = 0; i < 10; i++) {
// Write
var msg = "i: " + i + ", testQueue, robotA, robotId: " + robotId + ", time:" + _D()
conn.write(msg)
Log("Write a message to testQueue:", msg)
// Read
Log("read:", conn.read(1000), "#FF0000")
Sleep(1000)
}
}
function onexit() {
conn.close()
Log("close conn")
}
Hãy xem làm thế nào để sử dụng giao thức Kafka để xuất bản và đăng ký tin nhắn trong chức năng Dial.
Dial("kafka://localhost:9092/test_topic")
Giống như các giao thức khác, phần đầu tiên là tên giao thức.localhost:9092
. Sau đó sử dụng ký hiệu test_topic
.
Kết quả thử nghiệm: