রিসোর্স লোড হচ্ছে... লোডিং...

FMZ কোয়ান্ট ট্রেডিং প্ল্যাটফর্ম কাস্টম প্রোটোকল অ্যাক্সেস গাইড

লেখক:এফএমজেড-লিডিয়া, সৃষ্টিঃ ২০২৪-১১-০৮ ১৬ঃ৩৭ঃ২৫, আপডেটঃ ২০২৪-১১-০৮ ১৬ঃ৩৮ঃ৫২

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

এই নিবন্ধেবিশ্রামএফএমজেড কোয়ান্ট ট্রেডিং প্ল্যাটফর্মের কাস্টম প্রোটোকলটি কীভাবে OKX এক্সচেঞ্জের এপিআই ক্যাপসুল এবং অ্যাক্সেস করতে ব্যবহার করবেন তা ব্যাখ্যা করার উদাহরণ হিসাবে প্রোটোকল অ্যাক্সেস করুন। অন্যথায় নির্দিষ্ট না হলে, এই নিবন্ধটি REST কাস্টম প্রোটোকলকে বোঝায়।

  • কাস্টম প্রোটোকলের ওয়ার্কফ্লো হলঃ অনুরোধ প্রক্রিয়াঃ স্ট্র্যাটেজি ইনস্ট্যান্স ডকার চালানো -> কাস্টম প্রোটোকল প্রোগ্রাম -> এক্সচেঞ্জ এপিআই প্রতিক্রিয়া প্রক্রিয়াঃ এক্সচেঞ্জ এপিআই -> কাস্টম প্রোটোকল প্রোগ্রাম -> ডকার চালানো কৌশল দৃষ্টান্ত

১. এক্সচেঞ্জ কনফিগার করুন

এফএমজেড কোয়ান্ট ট্রেডিং প্ল্যাটফর্মে এক্সচেঞ্জ কনফিগার করার জন্য পৃষ্ঠাঃ

https://www.fmz.com/m/platforms/add

img

  • প্রোটোকল নির্বাচন করুনঃ Custom Protocol নির্বাচন করুন।
  • সার্ভিস ঠিকানাঃ কাস্টম প্রোটোকল প্রোগ্রামটি মূলত একটি RPC পরিষেবা। অতএব, এক্সচেঞ্জ কনফিগার করার সময় পরিষেবা ঠিকানা এবং পোর্ট স্পষ্টভাবে নির্দিষ্ট করা প্রয়োজন। এইভাবে, ডকার প্রক্রিয়া চলাকালীন কাস্টম প্রোটোকল প্রোগ্রাম অ্যাক্সেস করতে জানেStrategy instance running on the docker -> Custom protocol program. উদাহরণস্বরূপঃhttp://127.0.0.1:6666/OKX, কাস্টম প্রোটোকল প্রোগ্রাম এবং ডকার সাধারণত একই ডিভাইসে (সার্ভার) চালিত হয়, তাই পরিষেবা ঠিকানাটি স্থানীয় মেশিন (স্থানীয় হোস্ট) হিসাবে লেখা হয় এবং পোর্টটি এমন একটি পোর্ট হিসাবে ব্যবহার করা যেতে পারে যা সিস্টেম দ্বারা দখল করা হয় না।
  • অ্যাক্সেস কীঃ প্রক্রিয়া চলাকালীন বিনিময় কনফিগারেশন তথ্য পাসStrategy instance running on the docker -> Custom protocol program.
  • গোপন চাবি: প্রক্রিয়া চলাকালীন বিনিময় কনফিগারেশন তথ্য পাসStrategy instance running on the docker -> Custom protocol program.
  • ট্যাগঃ FMZ Quant Trading প্ল্যাটফর্মে এক্সচেঞ্জ অবজেক্টের ট্যাগ, যা একটি নির্দিষ্ট এক্সচেঞ্জ অবজেক্টকে চিহ্নিত করতে ব্যবহৃত হয়।

নিবন্ধে প্রকাশিত OKX প্লাগ-ইন কনফিগারেশনের স্ক্রিনশট নিম্নরূপঃ

img

OKX এক্সচেঞ্জ গোপন কী কনফিগারেশন তথ্যঃ

accessKey:  accesskey123    // accesskey123, these are not actual keys, just for demonstration
secretKey:  secretkey123
passphrase: passphrase123

2. ডকার এবং কাস্টম প্রোটোকল প্রোগ্রাম (প্লাগইন) এর বাস্তবায়ন

    1. ডকার এফএমজেড কোয়ান্ট ট্রেডিং প্ল্যাটফর্মে যে কোনও কৌশল চালানোর জন্য একটি ডকার স্থাপন করা আবশ্যক। একটি ডকার স্থাপন করার বিষয়ে বিস্তারিত জানার জন্য, প্ল্যাটফর্ম টিউটোরিয়ালটি দেখুন। এটি এখানে বিস্তারিতভাবে প্রবেশ করবে না।
    1. কাস্টম প্রোটোকল প্রোগ্রাম (প্লাগইন) ডকার এবং কাস্টম প্রোটোকল সাধারণত একই ডিভাইসে স্থাপন করা হয়। কাস্টম প্রোটোকল (পরিষেবা) প্রোগ্রামটি যে কোনও ভাষায় লেখা যেতে পারে। এই নিবন্ধটি পাইথন 3 এ লেখা হয়েছে। যে কোনও পাইথন প্রোগ্রাম চালানোর মতোই, আপনি এটি সরাসরি চালাতে পারেন (পাইথন পরিবেশের বিভিন্ন কনফিগারেশন আগাম তৈরি করুন) । অবশ্যই, এফএমজেড পাইথন প্রোগ্রাম চালাতেও সমর্থন করে, এবং আপনি এই কাস্টম প্রোটোকলটি লাইভ ট্রেডিং হিসাবে চালাতে পারেন যাতে এফএমজেড কোয়ান্ট ট্রেডিং প্ল্যাটফর্মটি আনপ্যাকড এক্সচেঞ্জ এপিআই অ্যাক্সেসের জন্য সমর্থন সরবরাহ করে। কাস্টম প্রোটোকল প্রোগ্রাম চালানোর পরে, এটি শুনতে শুরু করেঃhttp://127.0.0.1:6666. কাস্টম প্রোটোকল প্রোগ্রাম নির্দিষ্ট পাথ প্রক্রিয়া করতে পারেন, যেমন/OKX.

3. কৌশল দৃষ্টান্ত FMZ API ফাংশন অনুরোধ

যখন (FMZ) প্ল্যাটফর্ম এপিআই ফাংশনটি কৌশলটিতে কল করা হয়, কাস্টম প্রোটোকল প্রোগ্রামটি ডকার থেকে একটি অনুরোধ গ্রহণ করবে। আপনি এটি প্ল্যাটফর্মের ডিবাগিং সরঞ্জামগুলি ব্যবহার করেও পরীক্ষা করতে পারেন, উদাহরণস্বরূপঃ

ডিবাগিং সরঞ্জাম পাতাঃ

https://www.fmz.com/m/debug

function main() {
    return exchange.GetTicker("LTC_USDT")
}

ফাংশন কল করা হচ্ছেexchange.GetTicker(), কাস্টম প্রোটোকল প্রোগ্রাম অনুরোধ পায়ঃ

POST /OKX HTTP/1.1 
{
    "access_key":"xxx",
    "method":"ticker",
    "nonce":1730275031047002000,
    "params":{"symbol":"LTC_USDT"},
    "secret_key":"xxx"
}
  • অ্যাক্সেস_কী: উপরে প্ল্যাটফর্মে কনফিগার করা এক্সচেঞ্জ কী
  • secret_key: উপরে Configure Exchange প্ল্যাটফর্মে কনফিগার করা এক্সচেঞ্জ কী
  • পদ্ধতিঃ কৌশল মধ্যে কলিং ইন্টারফেস সম্পর্কিত, যখন কলexchange.GetTicker(), methodহয়ticker.
  • nonce: যখন অনুরোধটি ঘটেছিল তখন সময় স্ট্যাম্প।
  • প্যারামিটারঃ এই উদাহরণে, কৌশলটিতে ইন্টারফেস কলের সাথে সম্পর্কিত প্যারামিটারগুলি, যখন কল করা হয়exchange.GetTicker(), এর সাথে সম্পর্কিত পরামিতিগুলি হলঃ{"symbol":"LTC_USDT"}.

4. এক্সচেঞ্জ ইন্টারফেসের কাস্টম প্রোটোকল প্রোগ্রাম অ্যাক্সেস

যখন কাস্টম প্রোটোকল প্রোগ্রামটি ডকার থেকে একটি অনুরোধ গ্রহণ করে, তখন এটি অনুরোধে থাকা তথ্যের ভিত্তিতে কৌশল দ্বারা অনুরোধ করা প্ল্যাটফর্ম এপিআই ফাংশন (প্যারামিটার তথ্য সহ), এক্সচেঞ্জ কী ইত্যাদির মতো তথ্য পেতে পারে।

এই তথ্যের উপর ভিত্তি করে, কাস্টম প্রোটোকল প্রোগ্রামটি প্রয়োজনীয় ডেটা পেতে বা নির্দিষ্ট ক্রিয়াকলাপ সম্পাদন করতে এক্সচেঞ্জ ইন্টারফেসে অ্যাক্সেস করতে পারে।

সাধারণত এক্সচেঞ্জ ইন্টারফেসে GET/POST/PUT/DELETE এর মত পদ্ধতি থাকে, যা পাবলিক ইন্টারফেস এবং প্রাইভেট ইন্টারফেস-এ বিভক্ত।

  • পাবলিক ইন্টারফেসঃ এমন একটি ইন্টারফেস যা স্বাক্ষর যাচাইয়ের প্রয়োজন হয় না এবং সরাসরি কাস্টম প্রোটোকল প্রোগ্রামে অনুরোধ করা হয়।
  • ব্যক্তিগত ইন্টারফেসঃ একটি ইন্টারফেস যা স্বাক্ষর যাচাইয়ের প্রয়োজন। এই এক্সচেঞ্জগুলির এপিআই ইন্টারফেস অনুরোধ করার জন্য স্বাক্ষরটি কাস্টম প্রোটোকল প্রোগ্রামে বাস্তবায়ন করা দরকার।

কাস্টম প্রোটোকল প্রোগ্রাম এক্সচেঞ্জ ইন্টারফেস থেকে প্রতিক্রিয়া ডেটা গ্রহণ করে এবং ডকার দ্বারা প্রত্যাশিত ডেটা তৈরি করতে এটি আরও প্রক্রিয়া করে (নীচে বর্ণিত) ।GetTicker, GetAccountএবং Python কাস্টম প্রোটোকলের উদাহরণে CustomProtocolOKX ক্লাস বাস্তবায়নে অন্যান্য ফাংশন।

৫. কাস্টম প্রোটোকল প্রোগ্রাম ডাটা ডকারকে সাড়া দেয়

যখন কাস্টম প্রোটোকল প্রোগ্রামটি এক্সচেঞ্জের এপিআই ইন্টারফেসে অ্যাক্সেস করে, নির্দিষ্ট ক্রিয়াকলাপ সম্পাদন করে বা নির্দিষ্ট ডেটা অর্জন করে, তখন এটি ডকারকে ফলাফল ফিড করতে হবে।

ডকারকে ফিড করা ডেটা কৌশল দ্বারা কল করা ইন্টারফেস অনুযায়ী পরিবর্তিত হয় এবং প্রথমে দুটি বিভাগে বিভক্ত হয়ঃ

  • কাস্টম প্রোটোকল প্রোগ্রাম সফলভাবে এক্সচেঞ্জ ইন্টারফেস কল করেঃ
{
    "data": null,  // "data" can be of any type 
    "raw": null    // "raw" can be of any type 
}

তথ্যঃ এই ক্ষেত্রের নির্দিষ্ট কাঠামোটিmethodকাস্টম প্রোটোকল প্রোগ্রাম দ্বারা প্রাপ্ত অনুরোধে, এবং FMZ প্ল্যাটফর্ম API ফাংশন দ্বারা অবশেষে ফিরে আসা ডেটা কাঠামো নির্মাণের জন্য ব্যবহৃত হয়। সমস্ত ইন্টারফেস নীচে তালিকাভুক্ত করা হবে। raw: এই ক্ষেত্রটি এক্সচেঞ্জ এপিআই ইন্টারফেস প্রতিক্রিয়ার কাঁচা ডেটা পাস করতে ব্যবহার করা যেতে পারে, যেমন টিকার কাঠামো দ্বারা ফিরে আসাexchange.GetTicker()টিকার কাঠামোর তথ্য ক্ষেত্রটিrawক্ষেত্র এবংdataকিছু প্ল্যাটফর্মের এপিআই ফাংশনের এই ডেটার প্রয়োজন নেই।

  • কাস্টম প্রোটোকল প্রোগ্রাম এক্সচেঞ্জ ইন্টারফেস কল করতে ব্যর্থ হয়েছে (ব্যবসায়িক ত্রুটি, নেটওয়ার্ক ত্রুটি, ইত্যাদি)
{
    "error": ""    // "error" contains an error message as a string
}

error: error information, which will be displayed in the error log in the log area of the (FMZ) platform live trading, debugging tool and other pages. ত্রুটি সংক্রান্ত তথ্য যা (FMZ) প্ল্যাটফর্মের লাইভ ট্রেডিং, ডিবাগিং টুল এবং অন্যান্য পৃষ্ঠাগুলির লগ এলাকায় ত্রুটি লগ প্রদর্শিত হবে।

কৌশল প্রোগ্রাম দ্বারা প্রাপ্ত কাস্টম প্রোটোকল প্রতিক্রিয়া তথ্য প্রদর্শন করেঃ

// Tested in the debugging tool of the FMZ platform
function main() {
    Log(exchange.GetTicker("USDT"))       // The trading pair is incomplete, the BaseCurrency part is missing, and the custom protocol plug-in is required to return an error message: {"error": "..."}
    Log(exchange.GetTicker("LTC_USDT"))
}

img

৬. কাস্টম প্রোটোকলে ডেটা স্ট্রাকচার চুক্তি

উপরে কাস্টম প্রোটোকল প্রোগ্রাম (FMZ unpackaged) এক্সচেঞ্জ এপিআই অ্যাক্সেসে অংশগ্রহণের একটি সংক্ষিপ্ত প্রক্রিয়া। এই প্রক্রিয়া শুধুমাত্র প্রক্রিয়া ব্যাখ্যা যখন কলexchange.GetTicker()(FMZ) প্ল্যাটফর্ম ডিবাগিং টুলের মধ্যে ফাংশন। নিম্নলিখিত প্ল্যাটফর্ম API ফাংশনগুলির ইন্টারঅ্যাকশন বিবরণ বিস্তারিতভাবে ব্যাখ্যা করবে।

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

POST /OKX HTTP/1.1 
{
    "access_key": "xxx",
    "method": "ticker",
    "nonce": 1730275031047002000,
    "params": {"symbol":"LTC_USDT"},
    "secret_key": "xxx"
}

কৌশল (যেমন GetTicker) মধ্যে বিভিন্ন FMZ প্ল্যাটফর্ম encapsulated API ফাংশন কল করার সময়, কাস্টম প্রোটোকলে ডকার দ্বারা প্রেরিত অনুরোধ বিন্যাস এছাড়াও ভিন্ন হবে। শরীরের তথ্য (JSON) শুধুমাত্র ভিন্নmethodএবংparams. একটি কাস্টম প্রোটোকল ডিজাইন করার সময়, পদ্ধতির বিষয়বস্তু অনুযায়ী নির্দিষ্ট অপারেশন সম্পাদন করুন। নিম্নলিখিত সমস্ত ইন্টারফেসের জন্য অনুরোধ-প্রতিক্রিয়া দৃশ্যকল্প।

স্পট এক্সচেঞ্জ

উদাহরণস্বরূপ, বর্তমান ট্রেডিং জুটি হলঃETH_USDTডকার কাস্টম প্রোটোকলের প্রতিক্রিয়া জানাতে আশা করে এমন ডেটা মূলত ডেটা ফিল্ডে লেখা হয় এবং এক্সচেঞ্জ ইন্টারফেসের মূল ডেটা রেকর্ড করতে একটি কাঁচা ক্ষেত্রও যুক্ত করা যেতে পারে।

  • GetTicker

পদ্ধতি ক্ষেত্রঃ ticker প্যারামাস ক্ষেত্রঃ

{"symbol":"ETH_USDT"}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{
    "data": {
        "symbol": "ETH_USDT",      // Corresponds to the Symbol field in the Ticker structure returned by the GetTicker function
        "buy": "2922.18",          // ...corresponds to the Buy field
        "sell": "2922.19", 
        "high": "2955", 
        "low": "2775.15", 
        "open": "2787.72", 
        "last": "2922.18", 
        "vol": "249400.888156", 
        "time": "1731028903911"
    },
    "raw": {}                      // A raw field can be added to record the raw data of the exchange API interface response
}
  • গভীরতা পান

পদ্ধতি ক্ষেত্রঃ গভীরতা প্যারামাস ক্ষেত্রঃ

{"limit":"30","symbol":"ETH_USDT"}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{
    "data" : {
        "time" : 1500793319499,
        "asks" : [
            [1000, 0.5], [1001, 0.23], [1004, 2.1]
            // ... 
        ],
        "bids" : [
            [999, 0.25], [998, 0.8], [995, 1.4]
            // ... 
        ]
    }
}
  • GetTrades

পদ্ধতি ক্ষেত্রঃ ট্রেডস প্যারামাস ক্ষেত্রঃ

{"symbol":"eth_usdt"}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{ 
    "data": [
        {
            "id": 12232153,
            "time" : 1529919412968,
            "price": 1000,
            "amount": 0.5,
            "type": "buy",             // "buy"、"sell"、"bid"、"ask"
        }, {
            "id": 12545664,
            "time" : 1529919412900,
            "price": 1001,
            "amount": 1,
            "type": "sell",
        }
        // ...
    ]
}
  • GetRecords

পদ্ধতি ক্ষেত্রঃ রেকর্ডস প্যারামাস ক্ষেত্রঃ

{
    "limit":"500",
    "period":"60",          // 60 minutes
    "symbol":"ETH_USDT"
}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{
    "data": [
            // "Time":1500793319000,"Open":1.1,"High":2.2,"Low":3.3,"Close":4.4,"Volume":5.5
            [1500793319, 1.1, 2.2, 3.3, 4.4, 5.5],
            [1500793259, 1.01, 2.02, 3.03, 4.04, 5.05],
            // ...
    ]
}
  • GetMarketsবাস্তবায়ন

পদ্ধতি ক্ষেত্রঃ প্যারামাস ক্ষেত্রঃ

{}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{}
  • GetTickersবাস্তবায়ন

পদ্ধতি ক্ষেত্রঃ প্যারামাস ক্ষেত্রঃ

{}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{}
  • অ্যাকাউন্ট পান

পদ্ধতি ক্ষেত্রঃ অ্যাকাউন্টস প্যারামাস ক্ষেত্রঃ

{}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{
    "data": [
        {"currency": "TUSD", "free": "3000", "frozen": "0"}, 
        {"currency": "BTC", "free": "0.2482982056277609", "frozen": "0"}, 
        // ...
    ]
}
  • GetAssets

পদ্ধতি ক্ষেত্রঃ সম্পদ প্যারামাস ক্ষেত্রঃ

{}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{
    "data": [
        {"currency": "TUSD", "free": "3000", "frozen": "0"},
        {"currency": "BTC", "free": "0.2482982056277609", "frozen": "0"}, 
        // ...
    ]
}
  • ক্রিয়েটঅর্ডার / কিনুন / বিক্রি করুন

পদ্ধতি ক্ষেত্রঃ trade প্যারামাস ক্ষেত্রঃ

{"amount":"0.1","price":"1000","symbol":"BTC_USDT","type":"buy"}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{
    "data": {
        "id": "BTC-USDT,123456"
    }
}
  • অর্ডার পান

পদ্ধতি ক্ষেত্রঃ অর্ডার প্যারামাস ক্ষেত্রঃ

{"symbol":"ETH_USDT"}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{
    "data": [
        {
            "id": "ETH-USDT,123456",
            "symbol": "ETH_USDT",
            "amount": 0.25,
            "price": 1005,
            "deal_amount": 0,
            "avg_price": "1000",
            "type": "buy",         // "buy"、"sell"
            "status": "pending",   // "pending", "pre-submitted", "submitting", "submitted", "partial-filled"
        }, 
        // ...
    ]
}
  • অর্ডার পান

পদ্ধতি ক্ষেত্রঃ অর্ডার প্যারামাস ক্ষেত্রঃ

{
    "id":"ETH-USDT,123456",       // Calling in the strategy: exchange.GetOrder("ETH-USDT,123456")
    "symbol":"ETH_USDT"
}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{ 
    "data": {
        "id": "ETH-USDT,123456",
        "symbol": "ETH_USDT"
        "amount": 0.15,
        "price": 1002,
        "status": "pending",    // "pending", "pre-submitted", "submitting", "submitted", "partial-filled", "filled", "closed", "finished", "partial-canceled", "canceled"
        "deal_amount": 0,
        "type": "buy",          // "buy"、"sell"
        "avg_price": 0,         // If the exchange does not provide it, it can be assigned a value of 0 during processing.
    }
}
  • GetHistoryঅর্ডার

পদ্ধতি ক্ষেত্রঃ অর্ডার ইতিহাস প্যারামাস ক্ষেত্রঃ

{"limit":0,"since":0,"symbol":"ETH_USDT"}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{
    "data": [
        {
            "id": "ETH-USDT,123456",
            "symbol": "ETH_USDT",
            "amount": 0.25,
            "price": 1005,
            "deal_amount": 0,
            "avg_price": 1000,
            "type": "buy",       // "buy"、"sell"
            "status": "filled",  // "filled"
        }, 
        // ...
    ]
}
  • অর্ডার বাতিল করুন

পদ্ধতি ক্ষেত্রঃ বাতিল প্যারামাস ক্ষেত্রঃ

{"id":"ETH-USDT,123456","symbol":"ETH_USDT"}

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{
    "data": true    // As long as there is no error field in the JSON, the order cancellation is considered successful by default.
}
  • আইও

দ্যexchange.IOএক্সচেঞ্জ ইন্টারফেস সরাসরি অ্যাক্সেস করতে ফাংশন ব্যবহার করা হয়. উদাহরণস্বরূপ, আসুন গ্রহণGET /api/v5/trade/orders-pending, parameters: instType=SPOT, instId=ETH-USDTউদাহরণস্বরূপ।

// Called in the strategy instance
exchange.IO("api", "GET", "/api/v5/trade/orders-pending", "instType=SPOT&instId=ETH-USDT")

পদ্ধতি ক্ষেত্রঃ"__api_/api/v5/trade/orders-pending", পদ্ধতি ক্ষেত্রটি _ দিয়ে শুরু হয়এপিআই, যা ইঙ্গিত করে যে এটিexchange.IOকৌশল দৃষ্টান্তে ফাংশন কল। প্যারামাস ক্ষেত্রঃ

{"instId":"ETH-USDT","instType":"SPOT"}   // instType=SPOT&instId=ETH-USDT encoded parameters will be restored to JSON

ডাটা যে ডকার কাস্টম প্রোটোকল প্রতিক্রিয়া আশা করেঃ

{
    "data": {"code": "0", "data": [], "msg": ""}    // The data attribute value is the data of the exchange API: GET /api/v5/trade/orders-pending response
}
  • অন্যান্য কৌশল উদাহরণে ব্যবহৃত অন্যান্য FMZ প্ল্যাটফর্ম API ফাংশন, যেমনঃexchange.Go(), exchange.GetRawJSON()এবং অন্যান্য ফাংশনগুলিকে ক্যাপসুল করা প্রয়োজন হয় না, এবং কলিং পদ্ধতি এবং ফাংশন অপরিবর্তিত থাকে।

ফিউচার এক্সচেঞ্জ

স্পট এক্সচেঞ্জের সমস্ত ফাংশন সমর্থন করার পাশাপাশি, ফিউচার এক্সচেঞ্জগুলিতে কিছু এপিআই ফাংশন রয়েছে যা ফিউচার এক্সচেঞ্জগুলির জন্য অনন্য।

বাস্তবায়ন

  • পজিশন পান
  • মার্জিন লেভেল সেট করুন
  • GetFundings

পাইথন সংস্করণে কাস্টম প্রোটোকল উদাহরণ

REST কাস্টম প্রোটোকল - একটি স্পট এক্সচেঞ্জ অবজেক্ট হিসাবে ক্যাপসুল করা OKX এক্সচেঞ্জ REST API ইন্টারফেসের অ্যাক্সেস। একটি পাবলিক ইন্টারফেস অনুরোধ এবং প্রতিক্রিয়া তথ্য encapsulation বাস্তবায়ন. একটি ব্যক্তিগত ইন্টারফেস স্বাক্ষর, অনুরোধ এবং প্রতিক্রিয়া তথ্য encapsulation বাস্তবায়ন। এই উদাহরণটি মূলত পরীক্ষা এবং শেখার জন্য। অন্যান্য ইন্টারফেসগুলি পরীক্ষার জন্য ডকারকে সরাসরি প্রতিক্রিয়া জানাতে সিমুলেটেড ডেটা ব্যবহার করে।

import http.server
import socketserver
import json
import urllib.request
import urllib.error
import argparse
import ssl
import hmac
import hashlib
import base64

from datetime import datetime

ssl._create_default_https_context = ssl._create_unverified_context

class BaseProtocol:
    ERR_NOT_SUPPORT = {"error": "not support"}

    def __init__(self, apiBase, accessKey, secretKey):
        self._apiBase = apiBase
        self._accessKey = accessKey
        self._secretKey = secretKey


    def _httpRequest(self, method, path, query="", params={}, addHeaders={}):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6', 
            'Content-Type': 'application/json; charset=UTF-8'
        }

        # add headers
        for key in addHeaders:
            headers[key] = addHeaders[key]

        if method == "GET":
            url = f"{self._apiBase}{path}?{query}" if query != "" else f"{self._apiBase}{path}"
            req = urllib.request.Request(url, method=method, headers=headers)
        else:
            url = f"{self._apiBase}{path}"
            req = urllib.request.Request(url, json.dumps(params, separators=(',', ':')).encode('utf-8'), method=method, headers=headers)
        
        print(f'send request by protocol: {self.exName}, req:', req.method, req.full_url, req.headers, req.data, "\n")

        try:
            with urllib.request.urlopen(req) as resp:
                data = json.loads(resp.read())
        except json.JSONDecodeError:
            data = {"error": "Invalid JSON response"}
        except urllib.error.HTTPError as e:
            data = {"error": f"HTTP error: {e.code}"}
        except urllib.error.URLError as e:
            data = {"error": f"URL error: {e.reason}"}
        except Exception as e:
            data = {"error": f"Exception occurred: {str(e)}"}

        print(f'protocol response received: {self.exName}, resp:', data, "\n")

        return data
    

    def GetTickers(self):
        return self.ERR_NOT_SUPPORT


    def GetMarkets(self):
        return self.ERR_NOT_SUPPORT


    def GetTicker(self, symbol):
        return self.ERR_NOT_SUPPORT


    def GetDepth(self, symbol=""):
        return self.ERR_NOT_SUPPORT


    def GetTrades(self, symbol=""):
        return self.ERR_NOT_SUPPORT


    def GetRecords(self, symbol, period, limit):
        return self.ERR_NOT_SUPPORT


    def GetAssets(self):
        return self.ERR_NOT_SUPPORT


    def GetAccount(self):
        return self.ERR_NOT_SUPPORT


    def CreateOrder(self, symbol, side, price, amount):
        return self.ERR_NOT_SUPPORT


    def GetOrders(self, symbol=""):
        return self.ERR_NOT_SUPPORT


    def GetOrder(self, orderId):
        return self.ERR_NOT_SUPPORT


    def CancelOrder(self, orderId):
        return self.ERR_NOT_SUPPORT


    def GetHistoryOrders(self, symbol, since, limit):
        return self.ERR_NOT_SUPPORT


    def GetPostions(self, symbol=""):
        return self.ERR_NOT_SUPPORT


    def SetMarginLevel(self, symbol, marginLevel):
        return self.ERR_NOT_SUPPORT


    def GetFundings(self, symbol=""):
        return self.ERR_NOT_SUPPORT


    def IO(self, params):
        return self.ERR_NOT_SUPPORT


class ProtocolFactory:
    @staticmethod
    def createExWrapper(apiBase, accessKey, secretKey, exName) -> BaseProtocol:
        if exName == "OKX":
            return CustomProtocolOKX(apiBase, accessKey, secretKey, exName)
        else:
            raise ValueError(f'Unknown exName: {exName}')


class CustomProtocolOKX(BaseProtocol):
    """
    CustomProtocolOKX - OKX API Wrapper

    # TODO: add information.
    """

    def __init__(self, apiBase, accessKey, secretKey, exName):
        secretKeyList = secretKey.split(",")
        self.exName = exName
        self._x_simulated_trading = 0
        if len(secretKeyList) > 1:
            self._passphrase = secretKeyList[1]
            if len(secretKeyList) > 2:
                if secretKeyList[2] == "simulate":
                    self._x_simulated_trading = 1
        else:
            raise ValueError(f"{self.exName}: invalid secretKey format.")
        super().__init__(apiBase, accessKey, secretKeyList[0])


    def getCurrencys(self, symbol):
        baseCurrency, quoteCurrency = "", ""
        arrCurrency = symbol.split("_")
        if len(arrCurrency) == 2:
            baseCurrency = arrCurrency[0]
            quoteCurrency = arrCurrency[1]
        return baseCurrency, quoteCurrency


    def getSymbol(self, instrument):
        arrCurrency = instrument.split("-")
        if len(arrCurrency) == 2:
            baseCurrency = arrCurrency[0]
            quoteCurrency = arrCurrency[1]
        else:
            raise ValueError(f"{self.exName}: invalid instrument: {instrument}")
        return f'{baseCurrency}_{quoteCurrency}'


    def callUnsignedAPI(self, httpMethod, path, query="", params={}):
        return self._httpRequest(httpMethod, path, query, params)


    def callSignedAPI(self, httpMethod, path, query="", params={}):
        strTime = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
        jsonStr = json.dumps(params, separators=(',', ':')) if len(params) > 0 else ""
        message = f'{strTime}{httpMethod}{path}{jsonStr}'
        if httpMethod == "GET" and query != "":
            message = f'{strTime}{httpMethod}{path}?{query}{jsonStr}'
        mac = hmac.new(bytes(self._secretKey, encoding='utf8'), bytes(message, encoding='utf-8'), digestmod='sha256')
        signature = base64.b64encode(mac.digest())
        
        headers = {}
        if self._x_simulated_trading == 1:
            headers["x-simulated-trading"] = str(self._x_simulated_trading)
        headers["OK-ACCESS-KEY"] = self._accessKey
        headers["OK-ACCESS-PASSPHRASE"] = self._passphrase
        headers["OK-ACCESS-TIMESTAMP"] = strTime        
        headers["OK-ACCESS-SIGN"] = signature
        return self._httpRequest(httpMethod, path, query, params, headers)

    
    # Encapsulates requests to the exchange API.
    def GetTicker(self, symbol):
        """
        GET /api/v5/market/ticker , param: instId 
        """

        baseCurrency, quoteCurrency = self.getCurrencys(symbol)
        if baseCurrency == "" or quoteCurrency == "":
            return {"error": "invalid symbol"}

        path = "/api/v5/market/ticker"
        query = f'instId={baseCurrency}-{quoteCurrency}'
        data = self.callUnsignedAPI("GET", path, query=query)
        if "error" in data.keys() and "data" not in data.keys():
            return data

        ret_data = {}
        if data["code"] != "0" or not isinstance(data["data"], list):
            return {"error": json.dumps(data, ensure_ascii=False)}
        for tick in data["data"]:
            if not all(k in tick for k in ("instId", "bidPx", "askPx", "high24h", "low24h", "vol24h", "ts")):
                return {"error": json.dumps(data, ensure_ascii=False)}

            ret_data["symbol"] = self.getSymbol(tick["instId"])
            ret_data["buy"] = tick["bidPx"]
            ret_data["sell"] = tick["askPx"]
            ret_data["high"] = tick["high24h"]
            ret_data["low"] = tick["low24h"]
            ret_data["open"] = tick["open24h"]
            ret_data["last"] = tick["last"]
            ret_data["vol"] = tick["vol24h"]
            ret_data["time"] = tick["ts"]

        return {"data": ret_data, "raw": data}


    def GetDepth(self, symbol):
        """
        TODO: Implementation code
        """
        
        # Mock data for testing.
        ret_data = {            
            "time" : 1500793319499,
            "asks" : [
                [1000, 0.5], [1001, 0.23], [1004, 2.1]
            ],
            "bids" : [
                [999, 0.25], [998, 0.8], [995, 1.4]
            ]            
        }
        
        return {"data": ret_data}


    def GetTrades(self, symbol):
        """
        TODO: Implementation code
        """

        # Mock data for testing.
        ret_data = [
            {
                "id": 12232153,
                "time" : 1529919412968,
                "price": 1000,
                "amount": 0.5,
                "type": "buy",
            }, {
                "id": 12545664,
                "time" : 1529919412900,
                "price": 1001,
                "amount": 1,
                "type": "sell",
            }
        ]

        return {"data": ret_data}


    def GetRecords(self, symbol, period, limit):
        """
        TODO: Implementation code
        """

        # Mock data for testing.
        ret_data = [
            [1500793319, 1.1, 2.2, 3.3, 4.4, 5.5],
            [1500793259, 1.01, 2.02, 3.03, 4.04, 5.05],
        ]

        return {"data": ret_data}


    def GetMarkets(self):
        """
        TODO: Implementation code
        """

        ret_data = {}

        return {"data": ret_data}


    def GetTickers(self):
        """
        TODO: Implementation code
        """

        ret_data = {}

        return {"data": ret_data}


    def GetAccount(self):
        """
        GET /api/v5/account/balance
        """

        path = "/api/v5/account/balance"
        data = self.callSignedAPI("GET", path)

        ret_data = []
        if data["code"] != "0" or "data" not in data or not isinstance(data["data"], list):
            return {"error": json.dumps(data, ensure_ascii=False)}
        for ele in data["data"]:
            if "details" not in ele or not isinstance(ele["details"], list):
                return {"error": json.dumps(data, ensure_ascii=False)}
            for detail in ele["details"]:
                asset = {"currency": detail["ccy"], "free": detail["availEq"], "frozen": detail["ordFrozen"]}
                if detail["availEq"] == "":
                    asset["free"] = detail["availBal"]
                ret_data.append(asset)
        return {"data": ret_data, "raw": data}


    def GetAssets(self):
        """
        TODO: Implementation code
        """
        
        # Mock data for testing.
        ret_data = [
            {"currency": "TUSD", "free": "3000", "frozen": "0"},
            {"currency": "BTC", "free": "0.2482982056277609", "frozen": "0"}
        ]

        return {"data": ret_data}


    def CreateOrder(self, symbol, side, price, amount):
        """
        TODO: Implementation code
        """
        
        # Mock data for testing.
        ret_data = {
            "id": "BTC-USDT,123456"
        }

        return {"data": ret_data}

    
    def GetOrders(self, symbol):
        """
        GET /api/v5/trade/orders-pending  instType SPOT instId  after limit
        """
        
        baseCurrency, quoteCurrency = self.getCurrencys(symbol)
        if baseCurrency == "" or quoteCurrency == "":
            return {"error": "invalid symbol"}

        path = "/api/v5/trade/orders-pending"
        after = ""
        limit = 100

        ret_data = []
        while True:
            query = f"instType=SPOT&instId={baseCurrency}-{quoteCurrency}&limit={limit}"
            if after != "":
                query = f"instType=SPOT&instId={baseCurrency}-{quoteCurrency}&limit={limit}&after={after}"
        
            data = self.callSignedAPI("GET", path, query=query)
            
            if data["code"] != "0" or not isinstance(data["data"], list):
                return {"error": json.dumps(data, ensure_ascii=False)}
            for ele in data["data"]:
                order = {}

                order["id"] = f'{ele["instId"]},{ele["ordId"]}'
                order["symbol"] = f'{baseCurrency}-{quoteCurrency}'
                order["amount"] = ele["sz"]
                order["price"] = ele["px"]
                order["deal_amount"] = ele["accFillSz"]
                order["avg_price"] = 0 if ele["avgPx"] == "" else ele["avgPx"]
                order["type"] = "buy" if ele["side"] == "buy" else "sell"
                order["state"] = "pending"

                ret_data.append(order)
                after = ele["ordId"]

            if len(data["data"]) < limit:
                break

        return {"data": ret_data}


    def GetOrder(self, orderId):
        """
        TODO: Implementation code
        """
        
        # Mock data for testing.
        ret_data = {
            "id": "ETH-USDT,123456",
            "symbol": "ETH_USDT",
            "amount": 0.15,
            "price": 1002,
            "status": "pending",
            "deal_amount": 0,
            "type": "buy",
            "avg_price": 0,
        }

        return {"data": ret_data}


    def GetHistoryOrders(self, symbol, since, limit):
        """
        TODO: Implementation code
        """

        # Mock data for testing.
        ret_data = [
            {
                "id": "ETH-USDT,123456",
                "symbol": "ETH_USDT",
                "amount": 0.25,
                "price": 1005,
                "deal_amount": 0,
                "avg_price": 1000,
                "type": "buy",
                "status": "filled"
            }
        ]

        return {"data": ret_data}


    def CancelOrder(self, orderId):
        """
        TODO: Implementation code
        """

        # Mock data for testing.
        ret_data = True

        return {"data": ret_data}


    def IO(self, httpMethod, path, params={}):
        if httpMethod == "GET":
            query = urllib.parse.urlencode(params)
            data = self.callSignedAPI(httpMethod, path, query=query)
        else:
            data = self.callSignedAPI(httpMethod, path, params=params)
        
        if data["code"] != "0":
            return {"error": json.dumps(data, ensure_ascii=False)}

        return {"data": data}


class HttpServer(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        self.request_body = None
        self.request_path = None
        super().__init__(*args, **kwargs)


    def log_message(self, format, *args):
        return 


    def _sendResponse(self, body):
        self.send_response(200)
        self.send_header('Content-type', 'application/json; charset=utf-8')
        self.end_headers()
        self.wfile.write(json.dumps(body).encode('utf-8'))


    def do_GET(self):
        # The FMZ.COM custom protocol only send GET method request
        self._sendResponse({"error": "not support GET method."})


    def do_POST(self):
        """
        Returns:
            json: success, {"data": ...}
            json: error,   {"error": ...}
        """

        contentLen = int(self.headers['Content-Length'])
        self.request_body = self.rfile.read(contentLen)
        self.request_path = self.path
        exName = self.request_path.lstrip("/")

        # Print the request received from the FMZ.COM robot
        print(f"--------- request received from the FMZ.COM robot: --------- \n {self.requestline} | Body: {self.request_body} | Headers: {self.headers} \n")

        try:
            data = json.loads(self.request_body)
        except json.JSONDecodeError:
            data = {"error": self.request_body.decode('utf-8')}
            self._sendResponse(data)
            return 

        # fault tolerant
        if not all(k in data for k in ("access_key", "secret_key", "method", "params")):
            data = {"error": "missing required parameters"}
            self._sendResponse(data)
            return

        respData = {}
        accessKey = data["access_key"]
        secretKey = data["secret_key"]
        method = data["method"]
        params = data["params"]
        exchange = ProtocolFactory.createExWrapper("https://www.okx.com", accessKey, secretKey, exName)

        if method == "ticker":
            symbol = str(params["symbol"]).upper()
            respData = exchange.GetTicker(symbol)
        elif method == "depth":
            symbol = str(params["symbol"]).upper()
            respData = exchange.GetDepth(symbol)
        elif method == "trades":
            symbol = str(params["symbol"]).upper()
            respData = exchange.GetTrades(symbol)
        elif method == "records":
            symbol = str(params["symbol"]).upper()
            period = int(params["period"])
            limit = int(params["limit"])
            respData = exchange.GetRecords(symbol, period, limit)
        elif method == "accounts":
            respData = exchange.GetAccount()
        elif method == "assets":
            respData = exchange.GetAssets()
        elif method == "trade":
            amount = float(params["amount"])
            price = float(params["price"])
            symbol = str(params["symbol"])
            tradeType = str(params["type"])
            respData = exchange.CreateOrder(symbol, tradeType, price, amount)
        elif method == "orders":
            symbol = str(params["symbol"]).upper()
            respData = exchange.GetOrders(symbol)
        elif method == "order":
            orderId = str(params["id"])
            respData = exchange.GetOrder(orderId)
        elif method == "historyorders":
            symbol = str(params["symbol"])
            since = int(params["since"])
            limit = int(params["limit"])
            respData = exchange.GetHistoryOrders(symbol, since, limit)
        elif method == "cancel":
            orderId = str(params["id"])
            respData = exchange.CancelOrder(orderId)
        elif method[:6] == "__api_":
            respData = exchange.IO(self.headers["Http-Method"], method[6:], params)
        else:
            respData = {"error": f'invalid method: {method}'}

        # Print the response to send to FMZ.COM robot
        print(f"response to send to FMZ.COM robot: {respData} \n")

        self._sendResponse(respData)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Run a FMZ.COM custom protocol plugin.")
    parser.add_argument("--port", type=int, default=6666, help="Port to run the server on.")
    parser.add_argument("--address", type=str, default="localhost", help="Address to bind the server to.")
    args = parser.parse_args() 

    with socketserver.TCPServer((args.address, args.port), HttpServer) as httpd:
        print(f"running... {args.address}:{args.port}", "\n")
        httpd.serve_forever()

আরো