3
ফোকাস
1444
অনুসারী

একটি কৌশল টেমপ্লেট আপনাকে নির্বিঘ্নে WebSocket উদ্ধৃতি ব্যবহার করতে দেয়

তৈরি: 2024-10-30 09:49:20, আপডেট করা হয়েছে: 2024-11-05 17:45:31
comments   0
hits   1639

একটি কৌশল টেমপ্লেট আপনাকে নির্বিঘ্নে WebSocket উদ্ধৃতি ব্যবহার করতে দেয়

এটি একটি WebSocket মার্কেট টেমপ্লেট যা FMZ দ্বারা কপি করুন এবং একটি টেমপ্লেট হিসাবে সংরক্ষণ করুন আপনি এই টেমপ্লেটটি নতুন কৌশলে চেক করে ব্যবহার করতে পারেন: https://www.fmz.com/strategy/470349।

কেন WebSockets প্রয়োজন

বর্তমানে, FMZ কৌশলটি মূলত ঐতিহ্যগত REST API এনক্যাপসুলেশনের উপর ভিত্তি করে তৈরি করা হয়েছে API অ্যাক্সেসের প্রতিটি ধাপে একটি নেটওয়ার্ক সংযোগ স্থাপন এবং পোলিং এর মাধ্যমে বাজার ডেটা প্রাপ্ত করা প্রয়োজন। এই পদ্ধতিটি সহজ এবং ব্যবহার করা সহজ, এবং বেশিরভাগ প্রয়োজনের জন্য যথেষ্ট।

তবে, REST প্রোটোকলের অন্তর্নিহিত ল্যাটেন্সি সমস্যা রয়েছে, যা একাধিক ট্রেডিং জোড়া এবং একাধিক বিনিময় কৌশলের প্রয়োজন হলে বৃদ্ধি পাবে। যদিও প্ল্যাটফর্মের Go ফাংশনগুলি একই সাথে কার্যকর করা যেতে পারে, তবুও বিলম্বের সমস্যাটি বিদ্যমান, যা তুলনামূলকভাবে উচ্চ-ফ্রিকোয়েন্সি কৌশল ট্রেডিংয়ের চাহিদা পূরণ করা কঠিন করে তোলে। এছাড়াও, যদি অনেক বেশি ট্রেডিং জোড়া থাকে এবং পোলিং ফ্রিকোয়েন্সি খুব বেশি হয় দ্রুত, ট্রেডিং প্ল্যাটফর্মের অ্যাক্সেস ফ্রিকোয়েন্সি সীমা পূরণ হবে। ।

বর্তমানে, এক্সচেঞ্জের সার্ভারগুলিও ভারী বোঝার মধ্যে রয়েছে। তারা সকলেই একটি সম্পূর্ণ ওয়েবসকেট প্রোটোকল প্রদান করে এবং API ব্যবহারকারীদের কাছে এটি সুপারিশ করে। REST প্রোটোকলের তুলনায়, WebSocket একটি স্থায়ী দ্বিমুখী সংযোগ পদ্ধতি প্রদান করে, যা এক্সচেঞ্জকে রিয়েল টাইমে ক্লায়েন্টের কাছে ডেটা পুশ করতে সক্ষম করে, ঘন ঘন অনুরোধ এবং প্রতিক্রিয়া এড়ায় এবং বিলম্বিতা ব্যাপকভাবে হ্রাস করে। সাধারণভাবে বলতে গেলে, যদি REST API অ্যাক্সেস করার লেটেন্সি প্রায় 20 মিলিসেকেন্ড হয়, তাহলে WebSocket এর মাধ্যমে ডেটা পুশ করার লেটেন্সি প্রায় 2 মিলিসেকেন্ড। এছাড়াও, ওয়েবসকেট প্রোটোকল প্ল্যাটফর্মের অ্যাক্সেস ফ্রিকোয়েন্সি দ্বারা সীমাবদ্ধ নয়, এবং মূলত একসাথে কয়েক ডজন ট্রেডিং জোড়ায় সাবস্ক্রাইব করা সম্ভব।

WebSocket উদ্ধৃতি টেমপ্লেট ভূমিকা

FMZ পরিমাণগত ট্রেডিং প্ল্যাটফর্ম দীর্ঘদিন ধরে WebSocket প্রোটোকলকে সমর্থন করেছে, এবং এটি কল করা তুলনামূলকভাবে সুবিধাজনক তবে, নতুন ব্যবহারকারীদের জন্য, একাধিক সাবস্ক্রিপশন পরিচালনা করা, একাধিক বিনিময় উদ্ধৃতি সাবস্ক্রাইব করা এবং পুরো কৌশল প্রক্রিয়ার মধ্যে এটি এমবেড করা এখনও জটিল। দক্ষতার সাথে এবং সুবিধাজনকভাবে। এই পাবলিক WebSocket রিয়েল-টাইম মার্কেট ডেটা এক্সিলারেশন টেমপ্লেটটি ব্যবহার করা খুবই সহজ এবং বর্তমানে প্যাকেজ করা API কলগুলির সাথে সম্পূর্ণরূপে সামঞ্জস্যপূর্ণ, আপনি এটিকে পরিবর্তন করে সরাসরি ব্যবহার করতে পারেন৷ আপনার কৌশল ত্বরান্বিত.

প্রধান বৈশিষ্ট্য:

  • একাধিক বিনিময় সমর্থন: এই কৌশলটি একাধিক এক্সচেঞ্জ যেমন Binance, OKX, Bybit, Bitget, ইত্যাদি থেকে WebSocket সংযোগ সমর্থন করে৷ ব্যবহারকারীরা নিজেরাই আরও এক্সচেঞ্জ সমর্থন করতে এই টেমপ্লেটের প্যাকেজিং পদ্ধতি অনুকরণ করতে পারে৷
  • কাস্টমাইজযোগ্য সাবস্ক্রিপশন: আপনাকে নির্দিষ্ট মার্কেট চ্যানেলে সদস্যতা নিতে দেয় (যেমন গভীরতা, ট্রেডিং, ইত্যাদি) এবং কার্যকরীভাবে ট্রেডিং কৌশলগুলির মাধ্যমে তাৎক্ষণিক ব্যবহারের জন্য প্রাপ্ত ডেটা প্রক্রিয়া করে।
  • উন্নত ত্রুটি পরিচালনা: অন্তর্নির্মিত ত্রুটি ট্র্যাকিং এবং WebSocket পুনঃসংযোগ ব্যবস্থা তথ্য প্রবাহের নির্ভরযোগ্যতা এবং ধারাবাহিকতা নিশ্চিত করতে।

বাস্তবায়ন নীতির একটি সংক্ষিপ্ত ভূমিকা

মনে রাখবেন যে এই কৌশলটি TypeScript ব্যবহার করে, যা একটু অপরিচিত মনে হতে পারে যদি আপনি শুধুমাত্র জাভাস্ক্রিপ্টের সাথে পরিচিত হন। টাইপস্ক্রিপ্ট জাভাস্ক্রিপ্টের উপর ভিত্তি করে একটি টাইপ সিস্টেম এবং সমৃদ্ধ ভাষা বৈশিষ্ট্য প্রবর্তন করে যে অ্যাপ্লিকেশনগুলির জন্য পরিমাণগত ট্রেডিংয়ের মতো জটিল যুক্তির প্রয়োজন হয়, টাইপস্ক্রিপ্ট ব্যবহার করা সম্ভাব্য ত্রুটিগুলি কমাতে পারে এবং কোডের পাঠযোগ্যতা এবং রক্ষণাবেক্ষণের উন্নতি করতে পারে। অতএব, এটি সহজভাবে শিখতে সুপারিশ করা হয়।

উপরন্তু, কৌশলটি FMZ প্ল্যাটফর্মের অ্যাসিঙ্ক্রোনাস মেকানিজম ব্যবহার করে এবং মেকানিজম সাব-থ্রেডগুলি পাস করতে পারেthreadPostMessage ফাংশন প্রধান থ্রেডে একটি বার্তা পাঠায়। এই পদ্ধতিটি অ্যাসিঙ্ক্রোনাস এবং চাইল্ড থ্রেডে তৈরি হওয়া ডেটা আপডেটের প্রধান থ্রেডকে অবহিত করার জন্য উপযুক্ত। মূল থ্রেড এবং শিশু থ্রেড পাস করতে পারেনthreadGetData এবং__থ্রেডসেটডেটা ফাংশন ডেটা শেয়ার করে। এই পদ্ধতিটি থ্রেডগুলিকে ভাগ করা অবস্থা অ্যাক্সেস এবং সংশোধন করার অনুমতি দেয়। আপনি যদি প্ল্যাটফর্ম ডকুমেন্টেশনের সাথে মিলিত মাল্টি-থ্রেডিং শিখতে চান, তাহলে এই কৌশলটিও একটি ভালো শিক্ষার উদাহরণ।

এই কৌশলটির মূল নীতি হল WebSocket এর মাধ্যমে মূলধারার ডিজিটাল মুদ্রা বিনিময়ের সাথে সংযোগ করা এবং পরিমাণগত ট্রেডিং সিদ্ধান্তের জন্য ডেটা সমর্থন প্রদানের জন্য রিয়েল টাইমে বাজারের ডেটা (যেমন গভীরতার তথ্য এবং লেনদেনের তথ্য) গ্রহণ করা। নির্দিষ্ট বাস্তবায়ন প্রক্রিয়া নিম্নরূপ:

1. ওয়েবসকেট সংযোগ সেটিংস

setupWebsocket WebSocket সংযোগ শুরু করতে এবং বাজারের ডেটা গ্রহণ করতে ফাংশন ব্যবহার করা হয়। এটি একটি পরামিতি গ্রহণ করেmain_exchanges, সংযোগ করা প্রয়োজন যে বিনিময় ইঙ্গিত.

  • MyDial ফাংশন: একটি WebSocket সংযোগ তৈরি করুন, সংযোগের সময় রেকর্ড করুন এবং সংযোগ বন্ধ হয়ে গেলে বন্ধ হওয়ার সময় আউটপুট করুন।
  • updateSymbols ফাংশন: নতুন সাবস্ক্রিপশনের অনুরোধ আছে কিনা নিয়মিত পরীক্ষা করুন এবং প্রয়োজন অনুযায়ী বর্তমান ট্রেডিং পেয়ার তালিকা আপডেট করুন।

2. ডেটা প্রসেসিং

supports বস্তুটি সমর্থিত এক্সচেঞ্জ এবং তাদের প্রক্রিয়াকরণ ফাংশনগুলিকে সংজ্ঞায়িত করে (যেমনBinance) প্রতিটি এক্সচেঞ্জের প্রক্রিয়াকরণ ফাংশন প্রাপ্ত বার্তা পার্সিং এবং প্রাসঙ্গিক ডেটা বের করার জন্য দায়ী।

  • processMsg ফাংশন: এক্সচেঞ্জ থেকে বার্তাগুলি প্রক্রিয়া করুন, বিভিন্ন ধরণের ডেটা সনাক্ত করুন (যেমন গভীরতার আপডেট, লেনদেন ইত্যাদি), এবং সেগুলিকে ইউনিফাইড ইভেন্ট অবজেক্টে ফর্ম্যাট করুন৷

3. সদস্যতা ডেটা

প্রতিটি সংযোগে, সিস্টেম বর্তমান ট্রেডিং পেয়ারের উপর ভিত্তি করে প্রাসঙ্গিক বাজার ডেটা চ্যানেলে সদস্যতা নেয়।

  • getFunction ফাংশন: এক্সচেঞ্জ নামের উপর ভিত্তি করে সংশ্লিষ্ট প্রক্রিয়াকরণ ফাংশন পান।
  • this.wssPublic ফাংশন: WebSocket সংযোগ শুরু করুন এবং ডেটা গ্রহণ শুরু করুন।

4. থ্রেড ব্যবস্থাপনা

প্রতিটি এক্সচেঞ্জের জন্য একটি থ্রেড শুরু করুন, রিয়েল টাইমে ডেটা গ্রহণ করুন এবং কলব্যাক ফাংশনের মাধ্যমে ডেটা প্রক্রিয়া করুন।

  • threadMarket ফাংশন: উপ-থ্রেডগুলিতে ডেটা গ্রহণ করুন, সর্বশেষ গভীরতা এবং লেনদেনের তথ্য পার্স করুন এবং সঞ্চয় করুন।

5. ডেটা অধিগ্রহণ পদ্ধতি পুনরায় লিখুন

প্রতিটি এক্সচেঞ্জের জন্য গভীরতা এবং ট্রেডিং তথ্য পাওয়ার পদ্ধতিগুলি পুনরায় লিখুন, রিয়েল-টাইম আপডেট করা ডেটা ফেরত দেওয়ার অগ্রাধিকার দিন।

টেমপ্লেটটি কীভাবে ব্যবহার করবেন

  1. আরম্ভকরণ: ব্যবহার করুন$.setupWebsocket() টার্গেট এক্সচেঞ্জের WebSocket সংযোগ শুরু করুন।
  2. সদস্যতা: সিস্টেম স্বয়ংক্রিয়ভাবে প্রাসঙ্গিক চ্যানেলে সাবস্ক্রাইব করবে (যেমন গভীরতা, ট্রেডিং, ইত্যাদি) আপনার ট্রেড করা জাতগুলির জন্য।
  3. তথ্য অধিগ্রহণ: কল করেGetDepth() এবংGetTrades() বাজারের গভীরতা এবং লেনদেনের রেকর্ড ফেরত দিতে স্বয়ংক্রিয়ভাবে WebSocket রিয়েল-টাইম ডেটা ব্যবহার করে এমন ফাংশন।
  4. ত্রুটি হ্যান্ডলিং: নীতিতে একটি ট্র্যাকিং প্রক্রিয়া রয়েছে যা সংযোগ এবং ডেটা ত্রুটিগুলি লগ করে এবং সংযোগ বিঘ্নিত হলে স্বয়ংক্রিয়ভাবে পুনরায় সংযোগ করার চেষ্টা করে৷

যদি ইভেন্টলুপ() ফাংশনটি স্ট্র্যাটেজিতে যোগ করা হয়, এটি একটি ট্রিগার মেকানিজম এ পরিবর্তন করা হবে, যখন wss ডেটা আপডেট করা হয়, এটি স্বয়ংক্রিয়ভাবে প্রাপ্ত হবে যদি কোন সাম্প্রতিক ডেটা না থাকে, তাহলে এটি অপেক্ষা করবে। এটি অবশ্যই বুদ্ধিমান ঘুম ফাংশন সমতুল্য, এছাড়াও সরাসরি ব্যবহার করা যেতে পারে.

function main() {
    $.setupWebsocket()
    while (true) {
        exchanges.map(e=>{
            Log(e.GetName(), e.GetDepth())
            Log(e.GetName(), e.GetTrades())
        })
        EventLoop(100) // trigger by websocket
    }
}

আমার পূর্ববর্তী মাল্টি-কারেন্সি ট্রেডিং কৌশল নির্দেশিকা https://www.fmz.com/digest-topic/10506 দেখুন, যেখানে এটি সহজেই 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);
    }
}

কিভাবে নিজেকে একটি নতুন বিনিময় যোগ করুন

শুধু কৌশল টেমপ্লেট অনুসরণ করুন, নিম্নলিখিত বিন্যাস অনুকরণ করুন, এবং বিনিময় API ডকুমেন্টেশন পড়ুন:

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