Type/to search
8
Follow
1363
Followers
Guia de acesso ao protocolo geral da plataforma de negociação quantitativa Inventor
Discussions
Created 2024-10-29 14:37:56  Updated 2024-11-12 21:58:55
 0
 1361

img

A plataforma de negociação quantitativa Inventor oferece suporte a muitas bolsas de criptomoedas e engloba as principais bolsas do mercado. No entanto, ainda há muitas exchanges que não são empacotadas. Para usuários que precisam usar essas exchanges, eles podem acessá-las por meio do protocolo universal quantificado pelo inventor. Não limitado a trocas de criptomoedas, qualquerRESTAcordo ouFIXA plataforma do acordo também pode ser acessada.

Este artigo iráRESTTomando o acesso ao protocolo como exemplo, ele explica como usar o protocolo geral da Plataforma de Negociação Quantitativa do Inventor para encapsular e acessar a API da OKX Exchange. A menos que especificado de outra forma, este artigo se refere ao protocolo geral REST.

  • O fluxo de trabalho do protocolo geral é:
    Processo de solicitação: Instância de estratégia em execução no custodiante -> Programa de protocolo geral -> API de troca
    Processo de resposta: API de troca -> Programa de protocolo geral -> Instância de estratégia em execução no custodiante

1. Configurar a troca

A página para configurar a bolsa na Plataforma de Negociação Quantitativa do Inventor:

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

img

  • Selecionar protocolo: Selecione "Protocolo Geral".
  • Endereço do serviço: O programa de protocolo geral é essencialmente um serviço RPC
    Portanto, é necessário especificar claramente o endereço e a porta do serviço ao configurar a troca. Então em托管者上运行的策略实例 -> 通用协议程序Durante o processo, o host sabe onde acessar o programa de protocolo comum.
    Por exemplo:http://127.0.0.1:6666/OKXNormalmente, o programa de protocolo comum e o host são executados no mesmo dispositivo (servidor), então o endereço do serviço é escrito como a máquina local (localhost), e a porta pode ser uma porta que não esteja ocupada pelo sistema.
  • Access Key:
    托管者上运行的策略实例 -> 通用协议程序As informações de configuração de troca transmitidas durante o processo.
  • Secret Key:
    托管者上运行的策略实例 -> 通用协议程序As informações de configuração de troca transmitidas durante o processo.
  • Rótulo:
    O rótulo do objeto de troca na Plataforma de Negociação Quantitativa do Inventor é usado para identificar um determinado objeto de troca.

A captura de tela da configuração do plug-in OKX divulgada no artigo é a seguinte:

img

Informações de configuração da chave secreta da troca OKX:

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

2. Implantação do programa custodiante e do protocolo universal (plugin)

    1. Anfitrião
      Para executar qualquer estratégia em tempo real na Inventor Quantitative Trading Platform, você deve implementar um custodiante. Para implementação específica do custodiante, consulte o tutorial da plataforma, que não será repetido aqui.
    1. Programa de protocolo geral (plugin)
      O host e o protocolo universal são geralmente implantados no mesmo dispositivo. O programa do protocolo universal (serviço) pode ser escrito em qualquer linguagem. Este artigo foi escrito em Python3. Assim como executar qualquer programa Python, você pode executá-lo diretamente (fazer várias configurações do ambiente Python com antecedência).
      Claro, o FMZ também suporta a execução de programas Python, e esse protocolo geral também pode ser executado como um disco real para fornecer à plataforma de negociação quantitativa do inventor suporte para acesso à API de câmbio não empacotada.
      Após a execução do programa de protocolo geral, inicie o monitoramento:http://127.0.0.1:6666No programa de protocolo geral, caminhos específicos podem ser especificados, por exemplo/OKXpara ser processado.

3. A instância de estratégia solicita a função FMZ API

Quando a função da API da plataforma (FMZ) é chamada na estratégia, o Programa de Protocolo Universal recebe uma solicitação do custodiante. Você também pode testar usando as ferramentas de depuração da plataforma, por exemplo:

Página de ferramentas de depuração:

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

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

Chamarexchange.GetTicker()Função, o programa de protocolo geral recebe a solicitação:

http
POST /OKX HTTP/1.1 { "access_key":"xxx", "method":"ticker", "nonce":1730275031047002000, "params":{"symbol":"LTC_USDT"}, "secret_key":"xxx" }
  • access_key: A chave de troca configurada na plataforma "Configurar Exchange" acima
  • secret_key: A chave de troca configurada na plataforma "Configurar Exchange" acima
  • método: relacionado à interface de chamada na estratégia, chamandoexchange.GetTicker()hora,methodAquilo éticker
  • nonce: O registro de data e hora em que a solicitação ocorreu.
  • params: Parâmetros relacionados à chamada de interface na política.exchange.GetTicker()Ao chamar, os parâmetros relevantes são:{"symbol":"LTC_USDT"}

4. Programa de protocolo geral de acesso à interface de troca

Quando o programa de protocolo geral recebe uma solicitação do custodiante, ele pode obter informações como a função da API da plataforma (incluindo informações de parâmetros) solicitadas pela estratégia, a chave de troca, etc. com base nas informações contidas na solicitação.

Com base nessas informações, o programa de protocolo geral pode acessar a interface de troca para obter os dados necessários ou executar determinadas operações.

Normalmente, a interface de troca tem métodos como GET/POST/PUT/DELETE, que são divididos em interface pública e interface privada.

  • Interface pública: uma interface que não requer verificação de assinatura e é solicitada diretamente em um programa de protocolo geral.
  • Interface privada: uma interface que requer verificação de assinatura. A assinatura precisa ser implementada no programa de protocolo geral para solicitar a interface API dessas trocas.

O programa de protocolo geral recebe os dados de resposta da interface de troca, processa-os posteriormente e os constrói nos dados esperados pelo custodiante (descritos abaixo).
Consulte a troca de pontos OKX, implementação da classe CustomProtocolOKX no exemplo de protocolo geral PythonGetTickerGetAccountE outras funções.

5. O programa de protocolo geral responde os dados ao custodiante

Quando o programa de protocolo geral acessa a interface API da exchange, executa determinadas operações ou obtém determinados dados, ele precisa enviar os resultados ao custodiante.

Os dados retornados ao custodiante variam de acordo com a interface chamada pela estratégia e são primeiramente divididos em duas categorias:

  • O programa de protocolo geral chama com sucesso a interface de troca:

    json
    { "data": null, // "data" can be of any type "raw": null // "raw" can be of any type }
    • dados: A estrutura específica deste campo é a mesma da solicitação recebida pelo programa de protocolo geral.methodA seguir está uma lista de todas as interfaces usadas para construir a estrutura de dados retornada pela função API da plataforma FMZ.
    • Bruto: Este campo pode ser usado para passar os dados brutos da resposta da API de troca, por exemploexchange.GetTicker()A estrutura Ticker retornada pela função tem as seguintes informações registradas no campo Info da estrutura Ticker:rawCampos edataOs dados do campo; algumas funções da API da plataforma não precisam desses dados.
  • O programa de protocolo geral falhou ao chamar a interface de troca (erro comercial, erro de rede, etc.)

    json
    { "error": "" // "error" contains an error message as a string }
    • erro: informações de erro, que serão exibidas no log de erros na área de log do disco real da plataforma (FMZ), ferramenta de depuração e outras páginas.

Demonstra os dados gerais de resposta do protocolo recebidos pelo programa de políticas:

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

img

6. Acordo de estrutura de dados no protocolo geral

O acima é um breve processo de como o programa de protocolo geral participa do acesso à API de troca (FMZ unpackaged). Este processo explica apenas como chamar a ferramenta de depuração da plataforma (FMZ).exchange.GetTicker()Processo de função. A seguir, os detalhes de interação de todas as funções da API da plataforma serão explicados em detalhes.

A plataforma encapsula as funções comuns de várias exchanges e as unifica em uma determinada função, como a função GetTicker, que solicita as informações atuais do mercado de um determinado produto. Esta é basicamente uma API que todas as exchanges têm. Então, quando a interface da API encapsulada na plataforma é acessada na instância de estratégia, o custodiante enviará uma solicitação ao plug-in "Universal Protocol" (mencionado acima):

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

Ao chamar diferentes funções de API encapsuladas da plataforma do inventor na estratégia (como GetTicker), o formato de solicitação enviado pelo custodiante ao protocolo geral também será diferente. Os dados (JSON) no corpo diferem apenas emmethodeparams. Ao projetar um protocolo geral,methodVocê pode executar operações específicas com base no conteúdo. A seguir estão cenários de solicitação-resposta para todas as interfaces.

Troca à vista

Por exemplo, o par de negociação atual é:ETH_USDT, Não entrarei em detalhes mais tarde. Os dados que o custodiante espera que o protocolo geral responda são principalmente escritos no campo de dados, e um campo bruto também pode ser adicionado para registrar os dados originais da interface de troca.

  • GetTicker

    • campo de método: "ticker"

    • campo params:

      json
      {"symbol":"ETH_USDT"}
    • Dados que o host espera na resposta do protocolo geral:

      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

    • campo de método: "profundidade"

    • campo params:

      json
      {"limit":"30","symbol":"ETH_USDT"}
    • Dados que o host espera na resposta do protocolo geral:

      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

    • campo de método: "negociações"

    • campo params:

      json
      {"symbol":"eth_usdt"}
    • Dados que o host espera na resposta do protocolo geral:

      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

    • campo de método: "registros"

    • campo params:

      json
      { "limit":"500", "period":"60", // 60分钟 "symbol":"ETH_USDT" }
    • Dados que o host espera na resposta do protocolo geral:

      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
    A ser implementado

    • campo de método: ""

    • campo params:

      json
      {}
    • Dados que o host espera na resposta do protocolo geral:

      json
      {}
  • GetTickers
    A ser implementado

    • campo de método: ""

    • campo params:

      json
      {}
    • Dados que o host espera na resposta do protocolo geral:

      json
      {}
  • GetAccount

    • campo de método: "contas"

    • campo params:

      json
      {}
    • Dados que o host espera na resposta do protocolo geral:

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

    • campo de método: "ativos"

    • campo params:

      json
      {}
    • Dados que o host espera na resposta do protocolo geral:

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

    • campo de método: "comércio"

    • campo params:

      json
      {"amount":"0.1","price":"1000","symbol":"BTC_USDT","type":"buy"}
    • Dados que o host espera na resposta do protocolo geral:

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

    • campo de método: "pedidos"

    • campo params:

      json
      {"symbol":"ETH_USDT"}
    • Dados que o host espera na resposta do protocolo geral:

      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

    • campo de método: "order"

    • campo params:

      json
      { "id":"ETH-USDT,123456", // 策略中调用:exchange.GetOrder("ETH-USDT,123456") "symbol":"ETH_USDT" }
    • Dados que o host espera na resposta do protocolo geral:

      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

    • campo de método: "historyorders"

    • campo params:

      json
      {"limit":0,"since":0,"symbol":"ETH_USDT"}
    • Dados que o host espera na resposta do protocolo geral:

      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

    • campo de método: "cancelar"

    • campo params:

      json
      {"id":"ETH-USDT,123456","symbol":"ETH_USDT"}
    • Dados que o host espera na resposta do protocolo geral:

      json
      { "data": true // 只要该JSON中没有error字段,都默认为撤单成功 }
  • IO

    A função exchange.IO é usada para acessar diretamente a interface de troca. Por exemplo, usamosGET /api/v5/trade/orders-pending, 参数:instType=SPOT,instId=ETH-USDTPor exemplo.

    javascript
    // 策略实例中调用 exchange.IO("api", "GET", "/api/v5/trade/orders-pending", "instType=SPOT&instId=ETH-USDT")
    • campo método:"__api_/api/v5/trade/orders-pending"O campo do método começa com _api, indicando que ele é acionado pela chamada de função exchange.IO na instância da estratégia.

    • campo params:

      json
      {"instId":"ETH-USDT","instType":"SPOT"} // instType=SPOT&instId=ETH-USDT编码的参数会被还原为JSON
    • Dados que o host espera na resposta do protocolo geral:

      json
      { "data": {"code": "0", "data": [], "msg": ""} // data属性值为交易所API:GET /api/v5/trade/orders-pending 应答的数据 }
  • outro
    Outras funções da API da Plataforma Inventor usadas nos exemplos de estratégia, como:
    exchange.Go()exchange.GetRawJSON()Funções como essas não precisam ser encapsuladas, e o método de chamada e a funcionalidade permanecem inalterados.

Bolsa de Futuros

Além de oferecer suporte a todas as funções das bolsas à vista, as bolsas de futuros também têm algumas funções de API exclusivas das bolsas de futuros.

A ser implementado

  • GetPositions
  • SetMarginLevel
  • GetFundings

Versão Python do exemplo de protocolo geral

Protocolo geral REST - acesso à interface da API REST de troca OKX e encapsulamento como um objeto de troca pontual.
Implementou uma interface comum para encapsulamento de dados de solicitação e resposta.
Implementou uma assinatura de interface privada, encapsulamento de dados de solicitação e resposta.
Este exemplo é principalmente para teste e aprendizado. As outras interfaces usam dados simulados para responder diretamente ao host para teste.

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)