
Ini ialah templat pasaran WebSocket yang dibangunkan secara rasmi oleh FMZ dan simpan sebagai templat, kemudian pilih templat ini dalam strategi baharu untuk menggunakannya: https://www.fmz.com/strategy/470349.
Pada masa ini, strategi FMZ terutamanya berdasarkan pengkapsulan API REST tradisional Setiap akses API memerlukan sambungan rangkaian untuk mendapatkan data pasaran melalui tinjauan pendapat. Kaedah ini mudah dan mudah digunakan, dan mencukupi untuk kebanyakan keperluan.
Walau bagaimanapun, protokol REST mempunyai isu kependaman yang wujud, yang akan diperbesarkan apabila berbilang pasangan dagangan dan berbilang strategi pertukaran diperlukan. Walaupun fungsi Go pada platform boleh dilaksanakan secara serentak, masalah kelewatan masih wujud, yang menyukarkan untuk memenuhi keperluan perdagangan strategi frekuensi tinggi Di samping itu, jika terdapat terlalu banyak pasangan dagangan dan kekerapan pengundian juga pantas, ia akan menghadapi had kekerapan akses platform dagangan.
Pada masa ini, pelayan pertukaran juga berada di bawah beban berat. Mereka semua menyediakan protokol WebSocket yang lengkap dan mengesyorkannya kepada pengguna API. Berbanding dengan protokol REST, WebSocket menyediakan kaedah sambungan dua hala yang berterusan, yang membolehkan pertukaran untuk menolak data kepada pelanggan dalam masa nyata, mengelakkan permintaan dan tindak balas yang kerap dan mengurangkan kependaman. Secara umumnya, jika kependaman mengakses API REST adalah sekitar 20ms, kependaman menolak data melalui WebSocket ialah sekitar 2ms. Di samping itu, protokol WebSocket tidak dihadkan oleh kekerapan akses platform, dan pada asasnya adalah mungkin untuk melanggan berpuluh-puluh pasangan dagangan pada satu masa.
Platform dagangan kuantitatif FMZ telah menyokong protokol WebSocket untuk masa yang lama, dan ia agak mudah untuk dihubungi, tetapi bagi pengguna baru, ia masih terlalu rumit untuk mengendalikan berbilang langganan, melanggan berbilang sebut harga pertukaran, dan membenamkannya dengan cekap dan mudah ke dalam keseluruhan proses strategi. Templat pecutan data pasaran masa nyata WebSocket awam ini menyelesaikan masalah kesakitan ini. Ia sangat mudah digunakan dan serasi sepenuhnya dengan panggilan API terkapsul semasa Untuk kebanyakan strategi REST asal, anda boleh mengubah suai dan menggunakannya terus untuk mempercepatkan strategi anda.
Ciri-ciri Utama:
Ambil perhatian bahawa strategi ini menggunakan TypeScript, yang mungkin kelihatan agak asing jika anda hanya biasa dengan JavaScript. TypeScript memperkenalkan sistem jenis dan ciri bahasa yang lebih kaya berdasarkan JavaScript Untuk aplikasi seperti perdagangan kuantitatif yang perlu memproses logik yang kompleks, menggunakan TypeScript boleh mengurangkan kemungkinan ralat dan meningkatkan kebolehbacaan dan kebolehselenggaraan kod. Oleh itu, adalah disyorkan untuk mempelajarinya dengan mudah.
Di samping itu, strategi menggunakan mekanisme tak segerak platform FMZ, dan sub-benang mekanisme bolehFungsi threadPostMessage menghantar mesej ke utas utama. Kaedah ini tidak segerak dan sesuai untuk memberitahu urutan utama kemas kini data yang dijana dalam urutan anak. Benang utama dan benang kanak-kanak boleh disambungkan melaluithreadGetData dan__fungsi threadSetData berkongsi data. Pendekatan ini membenarkan urutan mengakses dan mengubah suai keadaan kongsi. Jika anda ingin belajar tentang multithreading, strategi ini juga merupakan contoh yang baik untuk belajar daripada dokumentasi platform.
Prinsip utama strategi ini adalah untuk menyambung kepada pertukaran mata wang digital arus perdana melalui WebSocket dan menerima data pasaran (seperti maklumat kedalaman dan maklumat transaksi) dalam masa nyata untuk menyediakan sokongan data untuk keputusan perdagangan kuantitatif. Proses pelaksanaan khusus adalah seperti berikut:
1. Tetapan sambungan WebSocket
setupWebsocket Fungsi ini digunakan untuk memulakan sambungan WebSocket dan menerima data pasaran. Ia menerima satu parametermain_exchanges, menunjukkan pertukaran yang perlu disambungkan.
MyDial fungsi: Buat sambungan WebSocket, rekod masa sambungan, dan keluarkan masa tutup apabila sambungan ditutup.updateSymbols fungsi: Semak secara berkala untuk permintaan langganan baharu dan kemas kini senarai pasangan dagangan semasa mengikut keperluan.2. Pemprosesan data
supports Objek mentakrifkan pertukaran yang disokong dan fungsi pemprosesannya (sepertiBinance). Setiap fungsi pemprosesan pertukaran bertanggungjawab untuk menghuraikan mesej yang diterima dan mengekstrak data yang berkaitan.
processMsg fungsi: Memproses mesej daripada pertukaran, mengenal pasti jenis data yang berbeza (seperti kemas kini mendalam, urus niaga, dsb.), dan formatkannya menjadi objek acara bersatu.3. Data Langganan
Pada setiap sambungan, sistem akan melanggan saluran data pasaran yang berkaitan berdasarkan pasangan dagangan semasa.
getFunction fungsi: Dapatkan fungsi pemprosesan yang sepadan mengikut nama pertukaran.this.wssPublic fungsi: Mulakan sambungan WebSocket dan mula menerima data.4. Pengurusan Benang
Mulakan urutan untuk setiap pertukaran, terima data dalam masa nyata dan proses data melalui fungsi panggil balik.
threadMarket fungsi: Terima data dalam utas anak, huraikan dan simpan maklumat kedalaman dan transaksi terkini.5. Tulis semula kaedah pemerolehan data
Tulis semula kaedah untuk mendapatkan maklumat kedalaman dan perdagangan untuk setiap pertukaran, memberi keutamaan kepada pemulangan data yang dikemas kini dalam masa nyata.
$.setupWebsocket() Mulakan sambungan WebSocket ke pertukaran sasaran.GetDepth() danGetTrades() Berfungsi, secara automatik menggunakan data masa nyata WebSocket untuk mengembalikan kedalaman pasaran dan rekod transaksi.Jika fungsi EventLoop() ditambahkan pada strategi, ia akan ditukar kepada mekanisme pencetus Apabila data wss dikemas kini, ia akan diperolehi secara automatik dan jika tiada data terkini, ia akan menunggu. Ia bersamaan dengan fungsi Tidur pintar Sudah tentu, anda juga boleh menggunakan Tidur 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
}
}
Rujuk panduan saya sebelum ini untuk strategi perdagangan berbilang mata wang https://www.fmz.com/digest-topic/10506, di mana ia boleh diubah suai dengan mudah untuk menyokong 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);
}
}
Cuma ikut templat strategi, tiru format berikut dan rujuk dokumentasi API pertukaran:
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)
}
}
}
}