Sumber dimuat naik... memuat...

Satu templat dasar yang membolehkan anda menggunakan sektor WebSocket dengan lancar

Penulis:Rumput, Dicipta: 2024-10-30 09:49:20, Dikemas kini: 2024-11-05 17:45:31

img

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

Mengapa WebSocket diperlukan?

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.

Templat industri WebSocket diperkenalkan

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:

  • Sokongan pelbagai bursa: Dasar ini menyokong sambungan WebSocket untuk pelbagai pertukaran seperti Binance, OKX, ByBit, Bitget, dan pengguna boleh meniru kaedah pembungkusan templat ini untuk menyokong lebih banyak pertukaran sendiri.
  • Langganan boleh disesuaikan: Membolehkan untuk melanggan saluran pasaran tertentu (seperti kedalaman, dagangan, dan lain-lain) dan memproses data yang diterima dengan cekap untuk strategi dagangan yang segera digunakan;
  • Kesalahan yang tinggi: Pengesanan ralat terbina dalam dan mekanisme sambungan semula WebSocket untuk memastikan kebolehpercayaan dan kesinambungan aliran data.

Permulaan pelaksanaan ringkas

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

setupWebsocketFungsi ini digunakan untuk memulakan sambungan WebSocket, menerima data pasaran. Ia menerima satu parametermain_exchangesPertukaran yang perlu disambungkan.

  • MyDialFungsi: Membuat sambungan WebSocket, merakam masa sambungan, dan mengeluarkan masa penutupan apabila sambungan ditutup.
  • updateSymbolsFungsi: Mengesan secara berkala sama ada ada permintaan langganan baru dan mengemas kini senarai pasangan transaksi semasa jika perlu.

2. Pengolahan data

supportsObjek mendefinisikan bursa yang disokong dan fungsi pemprosesannya (sepertiBinanceFungsi pemprosesan setiap bursa bertanggungjawab untuk menganalisis mesej yang diterima dan mengekstrak data yang berkaitan.

  • processMsgFungsi: 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.

  • getFunctionFungsi: Mengambil fungsi pemprosesan yang sesuai mengikut nama bursa.
  • this.wssPublicFungsi: 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.

  • threadMarketFungsi: 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.

Cara menggunakan templat

  1. PermulaanPenggunaan:$.setupWebsocket()Memulakan sambungan WebSocket untuk bursa sasaran.
  2. Langganan: Sistem akan secara automatik melanggan saluran yang berkaitan dengan jenis dagangan anda (seperti kedalaman, dagangan, dan lain-lain).
  3. Pengambilan dataPerkhidmatan:GetDepth()danGetTrades()Fungsi yang secara automatik menggunakan data WebSocket masa nyata untuk kembali kedalaman pasaran dan rekod transaksi.
  4. Kesilapan: Dasar termasuk mekanisme penjejakan untuk merekod kesalahan sambungan dan data dan cuba menyambung semula secara automatik apabila sambungan terputus.

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);
    }
}

Bagaimana untuk menambah bursa baru sendiri

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)
                }
            }
        }
    }
    

Lebih lanjut