Ini adalah templat pasaran WebSocket yang dibangunkan secara rasmi oleh FMZ, untuk disalin dan disimpan sebagai templat yang boleh digunakan dengan memilih templat ini dalam dasar baru:https://www.fmz.com/strategy/470349
Strategi FMZ pada masa ini adalah pembungkusan REST API tradisional, yang memerlukan sambungan rangkaian untuk setiap lawatan API untuk mendapatkan data pasaran melalui cara rundingan. Kaedah ini mudah digunakan dan cukup untuk kebanyakan keperluan.
Walau bagaimanapun, protokol REST mempunyai masalah kelewatan yang melekat, masalah kelewatan akan diperbesar apabila memerlukan banyak pasangan dagangan, strategi pelbagai bursa. Walaupun fungsi Go platform boleh dilaksanakan secara serentak, masalah kelewatan masih wujud, sukar untuk memenuhi keperluan perdagangan strategi yang agak tinggi, dan terlalu banyak perdagangan, frekuensi rundingan terlalu cepat dan akan menghadapi batasan frekuensi akses platform dagangan.
Bursa masa kini juga mempunyai beban pelayan yang berat, yang semuanya menawarkan protokol WebSocket yang sempurna, dan pengguna API disyorkan untuk menggunakannya. Berbanding protokol REST, WebSocket menyediakan sambungan dua hala yang tahan lama, yang membolehkan bursa memindahkan data ke pelanggan dalam masa nyata, mengelakkan permintaan dan tindak balas yang kerap, dan mengurangkan kelewatan dengan ketara. Secara amnya, jika kelewatan akses ke API REST adalah sekitar 20ms, WebSocket memindahkan data kira-kira 2ms.
Platform dagangan kuantitatif FMZ telah menyokong protokol WebSocket sejak awal dan agak mudah untuk dipanggil, tetapi terlalu rumit untuk pengguna baru untuk memproses pelbagai langganan, langganan pelbagai pasaran bursa, dan secara efisien mudah dimasukkan ke dalam keseluruhan proses strategi. Templat WebSocket yang terbuka untuk mempercepatkan data pasaran masa nyata ini menyelesaikan masalah ini, sangat mudah digunakan, sepenuhnya serasi dengan panggilan API yang kini dibungkus, untuk kebanyakan dasar dasar REST asal, perubahan mudah digunakan secara langsung untuk mempercepatkan dasar anda.
Ciri utama:
Perhatikan 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 yang memerlukan pengendalian logik yang rumit seperti transaksi kuantitatif. Menggunakan TypeScript dapat mengurangkan kesalahan yang berpotensi, meningkatkan kebolehbacaan dan kebolehperanan kod. Oleh itu, disyorkan untuk belajar dengan mudah.
Kaedah ini juga menggunakan mekanisme asynchronous platform FMZ, yang membolehkan sub-threads menghantar mesej kepada utas utama melalui fungsi __threadPostMessage. Kaedah ini adalah asynchronous untuk memberitahu utas utama mengenai kemas kini data yang dihasilkan dalam utas anak. Data boleh dikongsi antara utas utama dan utas anak melalui fungsi __threadGetData dan __threadSetData. Kaedah ini membolehkan akses dan perubahan keadaan perkongsian utas.
Prinsip utama strategi ini adalah untuk menyambungkan pertukaran mata wang digital utama melalui WebSocket untuk menerima data pasaran secara langsung (seperti maklumat mendalam dan maklumat dagangan) untuk menyokong keputusan dagangan kuantifikasi. Prosesnya adalah sebagai berikut:
1. Pengaturan Sambungan WebSocket
setupWebsocket
Fungsi ini digunakan untuk memulakan sambungan WebSocket, menerima data pasaran. Ia menerima satu parametermain_exchanges
Pertukaran yang perlu disambungkan.
MyDial
Fungsi: Membuat sambungan WebSocket, merakam masa sambungan, dan mengeluarkan masa penutupan apabila sambungan ditutup.updateSymbols
Fungsi: Mengesan secara berkala sama ada ada permintaan langganan baru dan mengemas kini senarai pasangan transaksi semasa jika perlu.2. Pengolahan data
supports
Objek mendefinisikan bursa yang disokong dan fungsi pemprosesannya (sepertiBinance
Fungsi pemprosesan setiap bursa bertanggungjawab untuk menganalisis mesej yang diterima dan mengekstrak data yang berkaitan.
processMsg
Fungsi: memproses mesej dari bursa, mengenal pasti pelbagai jenis data (seperti kemas kini mendalam, transaksi, dan lain-lain) dan memformatnya sebagai objek peristiwa yang seragam;3. Data langganan
Pada setiap sambungan, sistem akan memaparkan saluran data pasaran yang berkaitan dengan langganan berdasarkan transaksi semasa.
getFunction
Fungsi: Mengambil fungsi pemprosesan yang sesuai mengikut nama bursa.this.wssPublic
Fungsi: Inisialisasi sambungan WebSocket dan memulakan penerimaan data.4. Pengurusan Thread
Memulakan satu utas untuk setiap bursa, menerima data secara langsung dan memproses data melalui fungsi panggilan balik.
threadMarket
Fungsi: Mengambil data dalam subthread, menguraikan dan menyimpan maklumat kedalaman dan transaksi terkini.5. Menulis semula kaedah pengambilan data
Cara untuk menulis semula maklumat kedalaman dan perdagangan untuk setiap bursa, dengan keutamaan untuk mengembalikan data yang dikemas kini secara langsung.
$.setupWebsocket()
Memulakan sambungan WebSocket untuk bursa sasaran.GetDepth()
danGetTrades()
Fungsi yang secara automatik menggunakan data WebSocket masa nyata untuk kembali kedalaman pasaran dan rekod transaksi.Jika anda memasukkan fungsi EventLoop (), ia akan berubah menjadi mekanisme pencetus, yang akan mendapatkan data secara automatik apabila terdapat kemas kini wss dan tidak menunggu data terkini.
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 strategi perdagangan multi mata wang saya yang lalu.https://www.fmz.com/digest-topic/10506Di sini anda boleh mengubahnya 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);
}
}
Jika anda mengikuti templat dasar, anda boleh meniru format di bawah dan merujuk kepada dokumen 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)
}
}
}
}