Type/to search
8
Follow
1363
Followers
Inventor Quantitative Trading Platform の一般的なプロトコル アクセス ガイド
Discussions
Created 2024-10-29 14:37:56  Updated 2024-11-12 21:58:55
 0
 1361

img

Inventor Quantitative Trading Platform は、多くの暗号通貨取引所をサポートし、市場の主流の取引所をカプセル化します。しかし、まだパッケージ化されていない取引所も多数存在します。これらの取引所を利用する必要があるユーザーは、発明者によって定量化されたユニバーサルプロトコルを通じてアクセスできます。暗号通貨取引所に限らず、REST同意またはFIX協定のプラットフォームにもアクセス可能です。

この記事はRESTプロトコル アクセスを例に、Inventor Quantitative Trading Platform の一般的なプロトコルを使用して OKX Exchange の API をカプセル化してアクセスする方法を説明します。特に指定がない限り、この記事では REST の一般的なプロトコルについて説明します。

  • 一般的なプロトコルのワークフローは次のとおりです。
    リクエストプロセス: カストディアン上で実行されている戦略インスタンス -> 一般的なプロトコルプログラム -> Exchange API
    応答プロセス: Exchange API -> 一般的なプロトコルプログラム -> カストディアン上で実行される戦略インスタンス

1. 取引所を設定する

Inventor Quantitative Trading Platform での取引所の設定ページ:

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

img

  • プロトコルを選択: 「一般プロトコル」を選択します。
  • サービスアドレス: 一般的なプロトコルプログラムは本質的にRPCサービスです
    そのため、交換を設定する際には、サービス アドレスとポートを明確に指定する必要があります。だから托管者上运行的策略实例 -> 通用协议程序プロセス中、ホストは共通プロトコル プログラムにアクセスする場所を認識します。
    例えば:http://127.0.0.1:6666/OKX通常、共通プロトコルプログラムとホストは同一デバイス(サーバー)上で実行されるため、サービスアドレスはローカルマシン(localhost)と記述され、ポートはシステムが占有していないポートでも構いません。
  • Access Key:
    托管者上运行的策略实例 -> 通用协议程序プロセス中に渡された交換構成情報。
  • Secret Key:
    托管者上运行的策略实例 -> 通用协议程序プロセス中に渡された交換構成情報。
  • ラベル:
    Inventor Quantitative Trading Platform の交換オブジェクト ラベルは、特定の交換オブジェクトを識別するために使用されます。

記事で公開されている OKX プラグイン構成のスクリーンショットは次のとおりです。

img

OKX取引所の秘密鍵の設定情報:

txt
accessKey: accesskey123 // accesskey123 这些并不是实际秘钥,仅仅是演示 secretKey: secretkey123 passphrase: passphrase123

2. カストディアンとユニバーサルプロトコルプログラム(プラグイン)の展開

    1. ホスト
      Inventor Quantitative Trading Platform でリアルタイム戦略を実行するには、カストディアンをデプロイする必要があります。カストディアンの具体的なデプロイについては、プラットフォーム チュートリアルを参照してください。ここでは繰り返しません。
    1. 汎用プロトコルプログラム(プラグイン)
      ホストとユニバーサル プロトコルは通常、同じデバイスに展開されます。ユニバーサル プロトコル (サービス) プログラムは任意の言語で記述できます。この記事は Python3 で記述されています。他の Python プログラムを実行する場合と同様に、直接実行できます (事前に Python 環境のさまざまな構成を作成します)。
      もちろん、FMZ は Python プログラムの実行もサポートしており、この一般的なプロトコルを実際のディスクとして実行して、発明者の定量取引プラットフォームにパッケージ化されていない取引所 API アクセスのサポートを提供することもできます。
      一般的なプロトコル プログラムが実行されたら、監視を開始します。http://127.0.0.1:6666一般的なプロトコルプログラムでは、特定のパスを指定することができます。たとえば、/OKX処理されます。

3. 戦略インスタンスがFMZ API関数を要求する

戦略で (FMZ) プラットフォーム API 関数が呼び出されると、ユニバーサル プロトコル プログラムは管理者からの要求を受け取ります。プラットフォームのデバッグ ツールを使用してテストすることもできます。例:

デバッグ ツール ページ:

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

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: 上記のプラットフォーム「Exchangeの設定」で設定された交換キー
  • secret_key: 上記のプラットフォーム「Exchangeの設定」で設定された交換キー
  • メソッド: 戦略の呼び出しインターフェースに関連し、呼び出しexchange.GetTicker()時間、methodつまりticker
  • nonce: リクエストが発生したときのタイムスタンプ。
  • params: ポリシー内のインターフェース呼び出しに関連するパラメータ。exchange.GetTicker()呼び出すときに関連するパラメータは次のとおりです。{"symbol":"LTC_USDT"}

4. 交換インターフェースへの汎用プロトコルプログラムアクセス

汎用プロトコルプログラムは、カストディアンからのリクエストを受信すると、リクエストに含まれる情報に基づいて、戦略が要求するプラットフォームAPI関数(パラメータ情報を含む)、交換キーなどの情報を取得できます。

この情報に基づいて、一般的なプロトコル プログラムは交換インターフェイスにアクセスして必要なデータを取得したり、特定の操作を実行したりできます。

通常、交換インターフェースには GET/POST/PUT/DELETE などのメソッドがあり、これらはパブリック インターフェースとプライベート インターフェースに分かれています。

  • パブリック インターフェイス: 署名検証を必要とせず、一般的なプロトコル プログラムで直接要求されるインターフェイス。
  • プライベート インターフェース: 署名検証を必要とするインターフェース。これらの交換の API インターフェースを要求するには、一般的なプロトコル プログラムに署名を実装する必要があります。

一般的なプロトコル プログラムは、交換インターフェイス応答データを受信し、それをさらに処理して、管理者が期待するデータに構築します (以下で説明)。
OKXスポット取引所、Pythonの一般的なプロトコル例でのCustomProtocolOKXクラスの実装を参照してください。GetTickerGetAccountそしてその他の機能。

5. 一般的なプロトコルプログラムはデータを管理者に応答する

一般的なプロトコル プログラムが取引所の API インターフェイスにアクセスし、特定の操作を実行したり、特定のデータを取得したりする場合、その結果を管理者にフィードバックする必要があります。

カストディアンにフィードバックされるデータは、戦略によって呼び出されるインターフェースによって異なり、まず 2 つのカテゴリに分類されます。

  • 一般的なプロトコル プログラムは、交換インターフェイスを正常に呼び出します。

    json
    { "data": null, // "data" can be of any type "raw": null // "raw" can be of any type }
    • データ: このフィールドの具体的な構造は、一般的なプロトコル プログラムによって受信される要求の構造と同じです。method以下は、FMZ プラットフォーム API 関数によって返されるデータ構造を構築するために使用されるすべてのインターフェイスのリストです。
    • Raw: このフィールドは、Exchange APIレスポンスの生データを渡すために使用できます。例:exchange.GetTicker()関数によって返される Ticker 構造体には、Ticker 構造体の Info フィールドに記録された次の情報が含まれます。rawフィールドとdataフィールドのデータ。一部のプラットフォーム API 関数ではこのデータは必要ありません。
  • 一般的なプロトコル プログラムが交換インターフェイスの呼び出しに失敗しました (ビジネス エラー、ネットワーク エラーなど)

    json
    { "error": "" // "error" contains an error message as a string }
    • error: エラー情報。(FMZ)プラットフォームの実ディスク、デバッグツール、その他のページのログ領域のエラーログに表示されます。

ポリシー プログラムによって受信される一般的なプロトコル応答データを示します。

javascript
// FMZ平台的调试工具中测试 function main() { Log(exchange.GetTicker("USDT")) // 交易对不完整,缺少BaseCurrency部分,需要通用协议插件程序返回报错信息: {"error": "..."} Log(exchange.GetTicker("LTC_USDT")) }

img

6. 一般的なプロトコルにおけるデータ構造の合意

上記は、一般的なプロトコル プログラムが (FMZ のパッケージ化されていない) 交換 API へのアクセスに参加する方法の簡単なプロセスです。このプロセスでは、(FMZ) プラットフォーム デバッグ ツールを呼び出す方法のみを説明しています。exchange.GetTicker()関数プロセス。次に、すべてのプラットフォーム API 関数の相互作用の詳細について詳しく説明します。

このプラットフォームは、さまざまな取引所の共通機能をカプセル化し、特定の機能に統合します。たとえば、特定の商品の現在の市場情報を要求する GetTicker 機能などです。これは基本的にすべての取引所が持つ API です。したがって、プラットフォームのカプセル化された API インターフェイスが戦略インスタンスでアクセスされると、管理者は「ユニバーサル プロトコル」プラグイン (上記) にリクエストを送信します。

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

戦略内で異なる発明者プラットフォームのカプセル化された API 関数 (GetTicker など) を呼び出す場合、管理者が一般プロトコルに送信する要求形式も異なります。本文中のデータ(JSON)は、methodそしてparams。一般的なプロトコルを設計する場合、methodコンテンツに応じて特定の操作を実行できます。以下は、すべてのインターフェースの要求応答シナリオです。

スポット取引

たとえば、現在の取引ペアは次のとおりです。ETH_USDT、後ほど詳細には触れません。管理者が一般的なプロトコルが応答することを期待するデータは主にデータ フィールドに書き込まれ、交換インターフェースの元のデータを記録するために raw フィールドを追加することもできます。

  • GetTicker

    • メソッドフィールド: "ticker"

    • パラメータフィールド:

      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

    • メソッドフィールド: "depth"

    • パラメータフィールド:

      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

    • メソッドフィールド: "trades"

    • パラメータフィールド:

      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

    • メソッドフィールド: "records"

    • パラメータフィールド:

      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

    • メソッドフィールド: "accounts"

    • パラメータフィールド:

      json
      {}
    • ホストが一般的なプロトコル応答で期待するデータ:

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

    • メソッドフィールド: "assets"

    • パラメータフィールド:

      json
      {}
    • ホストが一般的なプロトコル応答で期待するデータ:

      json
      { "data": [ {"currency": "TUSD", "free": "3000", "frozen": "0"}, {"currency": "BTC", "free": "0.2482982056277609", "frozen": "0"}, // ... ] }
  • CreateOrder / Buy / Sell

    • メソッドフィールド: "trade"

    • パラメータフィールド:

      json
      {"amount":"0.1","price":"1000","symbol":"BTC_USDT","type":"buy"}
    • ホストが一般的なプロトコル応答で期待するデータ:

      json
      { "data": { "id": "BTC-USDT,123456" } }
  • GetOrders

    • メソッド フィールド: "orders"

    • パラメータフィールド:

      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

    • メソッドフィールド: "order"

    • パラメータフィールド:

      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

    • メソッド フィールド: "historyorders"

    • パラメータフィールド:

      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関数は、exchangeインターフェースに直接アクセスするために使用されます。たとえば、次のように使用します。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 应答的数据 }
  • 他の
    戦略例で使用されるその他の Inventor プラットフォーム API 関数:
    exchange.Go()exchange.GetRawJSON()このような関数はカプセル化する必要がなく、呼び出し方法と機能は変更されません。

先物取引所

先物取引所では、現物取引所のすべての機能をサポートしているほか、先物取引所独自の API 機能もいくつかあります。

実施予定

  • GetPositions
  • SetMarginLevel
  • GetFundings

一般的なプロトコル例のPythonバージョン

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()
Comment
All comments (0)
No data
No data
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)