موجد مقداری تجارتی پلیٹ فارم بہت سے کریپٹو کرنسی ایکسچینجز کو سپورٹ کرتا ہے اور مارکیٹ میں مرکزی دھارے کے تبادلے کو سمیٹتا ہے۔ تاہم، ابھی بھی بہت سے ایکسچینجز ہیں جو پیک نہیں کیے گئے ہیں، جن صارفین کو ان ایکسچینجز کو استعمال کرنے کی ضرورت ہے، وہ ان تک رسائی حاصل کر سکتے ہیں جو کہ موجد کے ذریعہ طے شدہ یونیورسل پروٹوکول ہے۔ کرپٹو کرنسی ایکسچینج تک محدود نہیں، کوئی بھیRESTمعاہدہ یاFIXمعاہدے کے پلیٹ فارم تک بھی رسائی حاصل کی جا سکتی ہے۔
یہ مضمون کرے گاRESTپروٹوکول تک رسائی کو ایک مثال کے طور پر لیتے ہوئے، یہ بتاتا ہے کہ OKX ایکسچینج کے API کو سمیٹنے اور اس تک رسائی حاصل کرنے کے لیے Inventor Quantitative Trading Platform کے عمومی پروٹوکول کو کیسے استعمال کیا جائے۔ جب تک کہ دوسری صورت میں وضاحت نہ کی گئی ہو، یہ مضمون REST جنرل پروٹوکول کا حوالہ دیتا ہے۔
- عام پروٹوکول کا ورک فلو یہ ہے:
درخواست کا عمل: محافظ پر چلنے والی حکمت عملی مثال -> جنرل پروٹوکول پروگرام -> ایکسچینج API
رسپانس کا عمل: ایکسچینج API -> جنرل پروٹوکول پروگرام -> سٹریٹیجی مثال کسٹوڈین پر چل رہی ہے
1. ایکسچینج کو ترتیب دیں۔
موجد مقداری تجارتی پلیٹ فارم پر تبادلے کو ترتیب دینے کا صفحہ:
- پروٹوکول منتخب کریں: "جنرل پروٹوکول" کو منتخب کریں۔
- سروس کا پتہ: جنرل پروٹوکول پروگرام بنیادی طور پر ایک RPC سروس ہے۔
لہذا، ایکسچینج کو ترتیب دیتے وقت سروس کا پتہ اور پورٹ واضح طور پر بتانا ضروری ہے۔ تو میں托管者上运行的策略实例 -> 通用协议程序عمل کے دوران، میزبان جانتا ہے کہ عام پروٹوکول پروگرام تک کہاں رسائی حاصل کرنی ہے۔
مثال کے طور پر:http://127.0.0.1:6666/OKXعام طور پر، مشترکہ پروٹوکول پروگرام اور میزبان ایک ہی ڈیوائس (سرور) پر چلائے جاتے ہیں، اس لیے سروس ایڈریس کو لوکل مشین (لوکل ہوسٹ) کے طور پر لکھا جاتا ہے، اور پورٹ ایک ایسی بندرگاہ ہو سکتی ہے جس پر سسٹم کا قبضہ نہ ہو۔ - Access Key:
托管者上运行的策略实例 -> 通用协议程序تبادلے کی ترتیب کی معلومات اس عمل کے دوران گزر گئی۔ - Secret Key:
托管者上运行的策略实例 -> 通用协议程序تبادلے کی ترتیب کی معلومات اس عمل کے دوران گزر گئی۔ - لیبل:
انوینٹر کوانٹیٹیو ٹریڈنگ پلیٹ فارم پر ایکسچینج آبجیکٹ کا لیبل کسی مخصوص ایکسچینج آبجیکٹ کی شناخت کے لیے استعمال ہوتا ہے۔
مضمون میں ظاہر کردہ OKX پلگ ان کنفیگریشن کا اسکرین شاٹ درج ذیل ہے:
OKX تبادلہ خفیہ کلیدی ترتیب کی معلومات:
txt
accessKey: accesskey123 // accesskey123 这些并不是实际秘钥,仅仅是演示
secretKey: secretkey123
passphrase: passphrase123
2. نگران کی تعیناتی اور یونیورسل پروٹوکول پروگرام (پلگ ان)
-
- میزبان
انوینٹر کوانٹیٹیو ٹریڈنگ پلیٹ فارم پر کسی بھی حقیقی وقت کی حکمت عملی کو چلانے کے لیے، آپ کو ایک نگران تعینات کرنا چاہیے، براہ کرم پلیٹ فارم ٹیوٹوریل کا حوالہ دیں، جسے یہاں دہرایا نہیں جائے گا۔
- میزبان
-
- جنرل پروٹوکول پروگرام (پلگ ان)
ہوسٹ اور یونیورسل پروٹوکول کو عام طور پر ایک ہی ڈیوائس پر لگایا جاتا ہے۔ یہ مضمون Python3 میں لکھا جا سکتا ہے۔ کسی بھی Python پروگرام کو چلانے کی طرح، آپ اسے براہ راست انجام دے سکتے ہیں (پائیتھون ماحول کی مختلف کنفیگریشنز پہلے سے بنائیں)۔
بلاشبہ، FMZ Python پروگراموں کو چلانے کی بھی حمایت کرتا ہے، اور یہ عام پروٹوکول ایک حقیقی ڈسک کے طور پر چلایا جا سکتا ہے تاکہ موجد کے مقداری تجارتی پلیٹ فارم کو بغیر پیکج شدہ ایکسچینج API تک رسائی کے لیے تعاون فراہم کیا جا سکے۔
عام پروٹوکول پروگرام چلنے کے بعد، نگرانی شروع کریں:http://127.0.0.1:6666عام پروٹوکول پروگرام میں، مخصوص راستے بتائے جا سکتے ہیں، مثال کے طور پر/OKXعملدرآمد کیا جائے.
- جنرل پروٹوکول پروگرام (پلگ ان)
3. حکمت عملی مثال FMZ API فنکشن کی درخواست کرتی ہے۔
جب حکمت عملی میں (FMZ) پلیٹ فارم API فنکشن کو کال کیا جاتا ہے، تو یونیورسل پروٹوکول پروگرام کو کسٹوڈین سے ایک درخواست موصول ہوتی ہے۔ آپ پلیٹ فارم کے ڈیبگنگ ٹولز کا استعمال کرتے ہوئے بھی ٹیسٹ کر سکتے ہیں، مثال کے طور پر:
ڈیبگنگ ٹولز کا صفحہ:
javascript
function main() {
return exchange.GetTicker("LTC_USDT")
}
کال کریں۔exchange.GetTicker()فنکشن، جنرل پروٹوکول پروگرام کو درخواست موصول ہوتی ہے:
http
POST /OKX HTTP/1.1
{
"access_key":"xxx",
"method":"ticker",
"nonce":1730275031047002000,
"params":{"symbol":"LTC_USDT"},
"secret_key":"xxx"
}
- access_key: اوپر دیئے گئے پلیٹ فارم "کنفیگر ایکسچینج" میں کنفیگر کردہ ایکسچینج کلید
- secret_key: اوپر پلیٹ فارم "کنفیگر ایکسچینج" میں کنفیگر کردہ ایکسچینج کلید
- طریقہ: حکمت عملی میں کالنگ انٹرفیس سے متعلق، کالنگ
exchange.GetTicker()گھنٹہmethodیعنیticker。 - nonce: ٹائم اسٹیمپ جب درخواست ہوئی تھی۔
- params: پالیسی میں انٹرفیس کال سے متعلق پیرامیٹرز۔
exchange.GetTicker()کال کرتے وقت، متعلقہ پیرامیٹرز یہ ہیں:{"symbol":"LTC_USDT"}。
4. جنرل پروٹوکول پروگرام ایکسچینج انٹرفیس تک رسائی
جب عام پروٹوکول پروگرام کو کسٹوڈین کی طرف سے کوئی درخواست موصول ہوتی ہے، تو وہ معلومات حاصل کر سکتا ہے جیسے کہ پلیٹ فارم API فنکشن (بشمول پیرامیٹر کی معلومات) حکمت عملی کے ذریعے درخواست کی گئی، تبادلے کی کلید وغیرہ۔ درخواست میں دی گئی معلومات کی بنیاد پر۔
اس معلومات کی بنیاد پر، جنرل پروٹوکول پروگرام مطلوبہ ڈیٹا حاصل کرنے یا کچھ آپریشنز کرنے کے لیے ایکسچینج انٹرفیس تک رسائی حاصل کر سکتا ہے۔
عام طور پر ایکسچینج انٹرفیس میں GET/POST/PUT/DELETE جیسے طریقے ہوتے ہیں، جنہیں عوامی انٹرفیس اور نجی انٹرفیس میں تقسیم کیا جاتا ہے۔
- عوامی انٹرفیس: ایک انٹرفیس جس میں دستخطی تصدیق کی ضرورت نہیں ہوتی ہے اور اس کی براہ راست درخواست ایک عام پروٹوکول پروگرام میں کی جاتی ہے۔
- پرائیویٹ انٹرفیس: ایک انٹرفیس جس میں دستخط کی تصدیق کی ضرورت ہوتی ہے ان ایکسچینجز کے API انٹرفیس کی درخواست کرنے کے لیے دستخط کو عام پروٹوکول پروگرام میں لاگو کرنے کی ضرورت ہوتی ہے۔
جنرل پروٹوکول پروگرام ایکسچینج انٹرفیس رسپانس ڈیٹا حاصل کرتا ہے، اس پر مزید کارروائی کرتا ہے، اور اسے نگران کے متوقع ڈیٹا میں بناتا ہے (ذیل میں بیان کیا گیا ہے)۔
OKX سپاٹ ایکسچینج، Python جنرل پروٹوکول کی مثال میں CustomProtocolOKX کلاس کا نفاذ دیکھیںGetTicker、GetAccountاور دیگر افعال۔
5. جنرل پروٹوکول پروگرام کسٹوڈین کو ڈیٹا کا جواب دیتا ہے۔
جب عام پروٹوکول پروگرام ایکسچینج کے API انٹرفیس تک رسائی حاصل کرتا ہے، کچھ آپریشن کرتا ہے یا کچھ ڈیٹا حاصل کرتا ہے، تو اسے نتائج کو محافظ کو واپس کرنے کی ضرورت ہوتی ہے۔
نگہبان کو دیا گیا ڈیٹا حکمت عملی کے ذریعے بلائے گئے انٹرفیس کے مطابق مختلف ہوتا ہے، اور اسے پہلے دو قسموں میں تقسیم کیا جاتا ہے:
-
جنرل پروٹوکول پروگرام کامیابی کے ساتھ ایکسچینج انٹرفیس کو کال کرتا ہے:
json{ "data": null, // "data" can be of any type "raw": null // "raw" can be of any type }- ڈیٹا: اس فیلڈ کا مخصوص ڈھانچہ وہی ہے جو عام پروٹوکول پروگرام کے ذریعے موصول ہونے والی درخواست میں ہے۔
methodذیل میں FMZ پلیٹ فارم API فنکشن کے ذریعے واپس کیے گئے ڈیٹا ڈھانچے کی تعمیر کے لیے استعمال ہونے والے تمام انٹرفیسز کی فہرست ہے۔ - Raw: اس فیلڈ کو ایکسچینج API جواب کے خام ڈیٹا میں منتقل کرنے کے لیے استعمال کیا جا سکتا ہے، مثال کے طور پر
exchange.GetTicker()فنکشن کے ذریعے لوٹائے گئے ٹکر ڈھانچے میں درج ذیل معلومات ٹکر ڈھانچے کے انفارمیشن فیلڈ میں درج ہوتی ہیں۔rawفیلڈز اورdataفیلڈ کا ڈیٹا؛ کچھ پلیٹ فارم API فنکشنز کو اس ڈیٹا کی ضرورت نہیں ہے۔
- ڈیٹا: اس فیلڈ کا مخصوص ڈھانچہ وہی ہے جو عام پروٹوکول پروگرام کے ذریعے موصول ہونے والی درخواست میں ہے۔
-
جنرل پروٹوکول پروگرام ایکسچینج انٹرفیس کو کال کرنے میں ناکام رہا (کاروباری غلطی، نیٹ ورک کی خرابی، وغیرہ)
json{ "error": "" // "error" contains an error message as a string }- ایرر: ایرر کی معلومات، جو (FMZ) پلیٹ فارم ریئل ڈسک، ڈیبگنگ ٹول اور دیگر صفحات کے لاگ ایریا میں ایرر لاگ میں ظاہر ہوگی۔
پالیسی پروگرام کو موصول ہونے والے عمومی پروٹوکول جوابی ڈیٹا کو ظاہر کرتا ہے:
javascript
// FMZ平台的调试工具中测试
function main() {
Log(exchange.GetTicker("USDT")) // 交易对不完整,缺少BaseCurrency部分,需要通用协议插件程序返回报错信息: {"error": "..."}
Log(exchange.GetTicker("LTC_USDT"))
}
6. عام پروٹوکول میں ڈیٹا ڈھانچہ کا معاہدہ
مندرجہ بالا ایک مختصر عمل ہے کہ کس طرح عام پروٹوکول پروگرام (FMZ unpackaged) ایکسچینج API تک رسائی حاصل کرتا ہے یہ عمل صرف اس بات کی وضاحت کرتا ہے کہ (FMZ) پلیٹ فارم ڈیبگنگ ٹول کو کیسے کال کرنا ہے۔exchange.GetTicker()فنکشن کا عمل۔ اس کے بعد، تمام پلیٹ فارم API فنکشنز کے تعامل کی تفصیلات کو تفصیل سے بیان کیا جائے گا۔
پلیٹ فارم مختلف ایکسچینجز کے مشترکہ افعال کو سمیٹتا ہے اور انہیں ایک مخصوص فنکشن میں یکجا کرتا ہے، جیسے کہ گیٹ ٹِکر فنکشن، جو کسی خاص پروڈکٹ کی موجودہ مارکیٹ کی معلومات کی درخواست کرتا ہے، یہ بنیادی طور پر ایک API ہے جو تمام ایکسچینجز کے پاس ہے۔ لہذا جب حکمت عملی کی مثال میں پلیٹ فارم encapsulated API انٹرفیس تک رسائی حاصل کی جاتی ہے، تو نگران "یونیورسل پروٹوکول" پلگ ان (اوپر مذکور) کو ایک درخواست بھیجے گا۔
http
POST /OKX HTTP/1.1
{
"access_key": "xxx",
"method": "ticker",
"nonce": 1730275031047002000,
"params": {"symbol":"LTC_USDT"},
"secret_key": "xxx"
}
حکمت عملی میں مختلف موجد پلیٹ فارم encapsulated API فنکشنز کو کال کرتے وقت (جیسے GetTicker)، نگران کی طرف سے جنرل پروٹوکول کو بھیجی جانے والی درخواست کی شکل بھی مختلف ہوگی۔ جسم میں ڈیٹا (JSON) صرف اس میں مختلف ہے۔methodاورparams. عام پروٹوکول کو ڈیزائن کرتے وقت،methodآپ مواد کی بنیاد پر مخصوص آپریشن کر سکتے ہیں۔ مندرجہ ذیل تمام انٹرفیس کے لیے درخواست کے جواب کے منظرنامے ہیں۔
اسپاٹ ایکسچینج
مثال کے طور پر، موجودہ تجارتی جوڑی ہے:ETH_USDT، میں بعد میں تفصیلات میں نہیں جاؤں گا۔ وہ ڈیٹا جس کا نگران عام پروٹوکول سے جواب دینے کی توقع کرتا ہے وہ بنیادی طور پر ڈیٹا فیلڈ میں لکھا جاتا ہے، اور ایکسچینج انٹرفیس کے اصل ڈیٹا کو ریکارڈ کرنے کے لیے ایک خام فیلڈ بھی شامل کیا جا سکتا ہے۔
-
GetTicker
-
طریقہ کا میدان: "ٹکر"
-
پیرامز فیلڈ:
json{"symbol":"ETH_USDT"} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "data": { "symbol": "ETH_USDT", // 对应GetTicker函数返回的Ticker结构中的Symbol字段 "buy": "2922.18", // ...对应Buy字段 "sell": "2922.19", "high": "2955", "low": "2775.15", "open": "2787.72", "last": "2922.18", "vol": "249400.888156", "time": "1731028903911" }, "raw": {} // 可以增加一个raw字段记录交易所API接口应答的原始数据 }
-
-
GetDepth
-
طریقہ کا میدان: "گہرائی"
-
پیرامز فیلڈ:
json{"limit":"30","symbol":"ETH_USDT"} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "data" : { "time" : 1500793319499, "asks" : [ [1000, 0.5], [1001, 0.23], [1004, 2.1] // ... ], "bids" : [ [999, 0.25], [998, 0.8], [995, 1.4] // ... ] } }
-
-
GetTrades
-
طریقہ کا میدان: "تجارت"
-
پیرامز فیلڈ:
json{"symbol":"eth_usdt"} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "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
-
طریقہ کا میدان: "ریکارڈز"
-
پیرامز فیلڈ:
json{ "limit":"500", "period":"60", // 60分钟 "symbol":"ETH_USDT" } -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "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
عمل میں لایا جائے۔-
طریقہ کا میدان: ""
-
پیرامز فیلڈ:
json{} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{}
-
-
GetTickers
عمل میں لایا جائے۔-
طریقہ کا میدان: ""
-
پیرامز فیلڈ:
json{} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{}
-
-
GetAccount
-
طریقہ کا میدان: "اکاؤنٹس"
-
پیرامز فیلڈ:
json{} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "data": [ {"currency": "TUSD", "free": "3000", "frozen": "0"}, {"currency": "BTC", "free": "0.2482982056277609", "frozen": "0"}, // ... ] }
-
-
GetAssets
-
طریقہ کا میدان: "اثاثے"
-
پیرامز فیلڈ:
json{} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "data": [ {"currency": "TUSD", "free": "3000", "frozen": "0"}, {"currency": "BTC", "free": "0.2482982056277609", "frozen": "0"}, // ... ] }
-
-
CreateOrder / Buy / Sell
-
طریقہ کا میدان: "تجارت"
-
پیرامز فیلڈ:
json{"amount":"0.1","price":"1000","symbol":"BTC_USDT","type":"buy"} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "data": { "id": "BTC-USDT,123456" } }
-
-
GetOrders
-
طریقہ کا میدان: "احکامات"
-
پیرامز فیلڈ:
json{"symbol":"ETH_USDT"} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "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" }, // ... ] }
-
-
GetOrder
-
طریقہ کا میدان: "آرڈر"
-
پیرامز فیلڈ:
json{ "id":"ETH-USDT,123456", // 策略中调用:exchange.GetOrder("ETH-USDT,123456") "symbol":"ETH_USDT" } -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "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, // 如果交易所没有提供,在处理时可以赋值为0 } }
-
-
GetHistoryOrders
-
طریقہ کا میدان: "ہسٹری آرڈرز"
-
پیرامز فیلڈ:
json{"limit":0,"since":0,"symbol":"ETH_USDT"} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "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" }, // ... ] }
-
-
CancelOrder
-
طریقہ کا میدان: "منسوخ کریں"
-
پیرامز فیلڈ:
json{"id":"ETH-USDT,123456","symbol":"ETH_USDT"} -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "data": true // 只要该JSON中没有error字段,都默认为撤单成功 }
-
-
IO
Exchange.IO فنکشن کو براہ راست ایکسچینج انٹرفیس تک رسائی حاصل کرنے کے لیے استعمال کیا جاتا ہے، مثال کے طور پر، ہم استعمال کرتے ہیں۔
GET /api/v5/trade/orders-pending, 参数:instType=SPOT,instId=ETH-USDTمثال کے طور پر۔javascript// 策略实例中调用 exchange.IO("api", "GET", "/api/v5/trade/orders-pending", "instType=SPOT&instId=ETH-USDT")-
طریقہ کا میدان:
"__api_/api/v5/trade/orders-pending"میتھڈ فیلڈ _api سے شروع ہوتی ہے، جو اس بات کی نشاندہی کرتی ہے کہ یہ حکمت عملی مثال میں exchange.IO فنکشن کال کے ذریعے متحرک ہے۔ -
پیرامز فیلڈ:
json{"instId":"ETH-USDT","instType":"SPOT"} // instType=SPOT&instId=ETH-USDT编码的参数会被还原为JSON -
وہ ڈیٹا جس کی میزبان کو عام پروٹوکول کے جواب میں توقع ہے:
json{ "data": {"code": "0", "data": [], "msg": ""} // data属性值为交易所API:GET /api/v5/trade/orders-pending 应答的数据 }
-
-
دوسرے
حکمت عملی کی مثالوں میں استعمال ہونے والے دیگر موجد پلیٹ فارم API افعال، جیسے:
exchange.Go()、exchange.GetRawJSON()اس طرح کے افعال کو انکیپسولیٹ کرنے کی ضرورت نہیں ہے، اور کال کرنے کا طریقہ اور فعالیت میں کوئی تبدیلی نہیں ہے۔
فیوچر ایکسچینج
سپاٹ ایکسچینجز کے تمام فنکشنز کو سپورٹ کرنے کے علاوہ، فیوچر ایکسچینجز میں کچھ API فنکشنز بھی ہوتے ہیں جو فیوچر ایکسچینجز کے لیے منفرد ہوتے ہیں۔
عمل میں لایا جائے۔
- GetPositions
- SetMarginLevel
- GetFundings
عام پروٹوکول کی مثال کا ازگر ورژن
REST جنرل پروٹوکول - OKX ایکسچینج REST API انٹرفیس تک رسائی اور اسے اسپاٹ ایکسچینج آبجیکٹ کے طور پر سمیٹیں۔
درخواست اور جوابی ڈیٹا انکیپسولیشن کے لیے ایک مشترکہ انٹرفیس نافذ کیا۔
ایک نجی انٹرفیس دستخط، درخواست اور جوابی ڈیٹا انکیپسولیشن کو نافذ کیا۔
یہ مثال بنیادی طور پر جانچ اور سیکھنے کے لیے ہے۔
python
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'
if httpMethod == "GET":
jsonStr = json.dumps(params, separators=(',', ':')) if len(params) > 0 else ""
else:
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()
- 1




