
Ini adalah templat pasar WebSocket yang secara resmi dikembangkan oleh FMZ. Salin dan simpan sebagai templat, lalu pilih templat ini dalam strategi baru untuk menggunakannya: https://www.fmz.com/strategy/470349
Saat ini, strategi FMZ sebagian besar didasarkan pada enkapsulasi REST API tradisional. Setiap akses API memerlukan pembuatan koneksi jaringan dan perolehan data pasar melalui polling. Metode ini sederhana dan mudah digunakan, dan mencukupi untuk sebagian besar kebutuhan.
Namun, protokol REST memiliki masalah latensi bawaan, yang akan membesar ketika diperlukan beberapa pasangan perdagangan dan beberapa strategi pertukaran. Meskipun fungsi Go dari platform dapat dijalankan secara bersamaan, masalah penundaan masih ada, yang membuatnya sulit untuk memenuhi kebutuhan perdagangan strategi frekuensi yang relatif tinggi. Selain itu, jika ada terlalu banyak pasangan perdagangan dan frekuensi pemungutan suara terlalu cepat, ia akan menemui batas frekuensi akses platform perdagangan.
Saat ini, server bursa juga sedang mengalami beban berat. Mereka semua menyediakan protokol WebSocket lengkap dan merekomendasikannya kepada pengguna API. Dibandingkan dengan protokol REST, WebSocket menyediakan metode koneksi dua arah yang persisten, yang memungkinkan pertukaran untuk mendorong data ke klien secara real time, menghindari permintaan dan respons yang sering dan sangat mengurangi latensi. Secara umum, jika latensi mengakses REST API sekitar 20 ms, latensi mengirim data melalui WebSocket sekitar 2 ms. Selain itu, protokol WebSocket tidak dibatasi oleh frekuensi akses platform, dan pada dasarnya memungkinkan untuk berlangganan lusinan pasangan perdagangan sekaligus.
Platform perdagangan kuantitatif FMZ telah mendukung protokol WebSocket untuk waktu yang lama, dan relatif nyaman untuk dipanggil, tetapi bagi pengguna pemula, masih terlalu rumit untuk menangani banyak langganan, berlangganan beberapa kutipan pertukaran, dan menyematkannya secara efisien dan nyaman di keseluruhan proses strategi. Template akselerasi data pasar real-time WebSocket publik ini memecahkan masalah ini. Template ini sangat mudah digunakan dan sepenuhnya kompatibel dengan panggilan API terenkapsulasi saat ini. Untuk sebagian besar strategi REST asli, Anda cukup memodifikasinya dan menggunakannya secara langsung untuk mempercepat strategi Anda.
Fitur Utama:
Perlu diingat bahwa strategi ini menggunakan TypeScript, yang mungkin terlihat agak asing jika Anda hanya terbiasa dengan JavaScript. TypeScript memperkenalkan sistem tipe dan fitur bahasa yang lebih kaya berdasarkan JavaScript. Untuk aplikasi seperti perdagangan kuantitatif yang perlu memproses logika kompleks, penggunaan TypeScript dapat mengurangi potensi kesalahan dan meningkatkan keterbacaan dan pemeliharaan kode. Oleh karena itu, disarankan untuk mempelajarinya secara sederhana.
Selain itu, strategi ini menggunakan mekanisme asinkron dari platform FMZ, dan sub-utas mekanisme dapatFungsi threadPostMessage mengirimkan pesan ke thread utama. Metode ini tidak sinkron dan cocok untuk memberitahukan utas utama tentang pembaruan data yang dihasilkan di utas anak. Benang utama dan benang anak dapat dihubungkan melaluithreadGetData dan__Fungsi threadSetData membagikan data. Pendekatan ini memperbolehkan thread untuk mengakses dan mengubah status bersama. Jika Anda ingin mempelajari tentang multithreading, strategi ini juga merupakan contoh pembelajaran yang baik jika dipadukan dengan dokumentasi platform.
Prinsip utama strategi ini adalah terhubung ke bursa mata uang digital utama melalui WebSocket dan menerima data pasar (seperti informasi mendalam dan informasi transaksi) secara real-time untuk menyediakan dukungan data bagi keputusan perdagangan kuantitatif. Proses implementasi spesifiknya adalah sebagai berikut:
1. Pengaturan koneksi WebSocket
setupWebsocket Fungsi ini digunakan untuk menginisialisasi koneksi WebSocket dan menerima data pasar. Ini menerima satu parametermain_exchanges, yang menunjukkan pertukaran yang perlu dihubungkan.
MyDial fungsi: Buat koneksi WebSocket, catat waktu koneksi, dan keluarkan waktu tutup saat koneksi ditutup.updateSymbols fungsi: Periksa secara berkala permintaan berlangganan baru dan perbarui daftar pasangan perdagangan saat ini sesuai kebutuhan.2. Pengolahan data
supports Objek mendefinisikan pertukaran yang didukung dan fungsi pemrosesannya (sepertiBinance). Fungsi pemrosesan masing-masing bursa bertanggung jawab untuk mengurai pesan yang diterima dan mengekstrak data yang relevan.
processMsg fungsi: Memproses pesan dari pertukaran, mengidentifikasi berbagai jenis data (seperti pembaruan kedalaman, transaksi, dll.), dan memformatnya menjadi objek peristiwa terpadu.3. Data Langganan
Pada setiap koneksi, sistem akan berlangganan saluran data pasar yang relevan berdasarkan pasangan perdagangan saat ini.
getFunction fungsi: Dapatkan fungsi pemrosesan yang sesuai dengan nama pertukaran.this.wssPublic fungsi: Inisialisasi koneksi WebSocket dan mulai menerima data.4. Manajemen Thread
Mulai utas untuk setiap pertukaran, terima data secara real time, dan proses data melalui fungsi panggilan balik.
threadMarket fungsi: Menerima data pada thread anak, mengurai dan menyimpan informasi kedalaman dan transaksi terkini.5. Tulis ulang metode akuisisi data
Tulis ulang metode untuk memperoleh informasi kedalaman dan perdagangan untuk setiap bursa, dengan memberikan prioritas untuk mengembalikan data yang diperbarui secara real time.
$.setupWebsocket() Inisialisasi koneksi WebSocket ke bursa target.GetDepth() Dan GetTrades() Fungsi, secara otomatis menggunakan data waktu nyata WebSocket untuk mengembalikan kedalaman pasar dan catatan transaksi.Jika fungsi EventLoop() ditambahkan ke strategi, maka fungsi tersebut akan diubah menjadi mekanisme pemicu. Saat data wss diperbarui, data tersebut akan langsung diperoleh secara otomatis, dan jika tidak ada data terbaru, maka akan menunggu. Ini setara dengan fungsi Sleep yang cerdas. Tentu saja, Anda juga dapat menggunakan Sleep secara langsung.
function main() {
$.setupWebsocket()
while (true) {
exchanges.map(e=>{
Log(e.GetName(), e.GetDepth())
Log(e.GetName(), e.GetTrades())
})
EventLoop(100) // trigger by websocket
}
}
Lihat panduan saya sebelumnya tentang strategi perdagangan multi-mata uang https://www.fmz.com/digest-topic/10506, di mana panduan tersebut dapat dengan mudah dimodifikasi untuk mendukung 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);
}
}
Ikuti saja templat strategi, tiru format berikut, dan rujuk dokumentasi API bursa:
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)
}
}
}
}