[TOC]
The inventor quantified trading platform supports a large number of cryptocurrency exchanges and encapsulates the mainstream exchanges on the market. However, there are still many exchanges that are not encapsulated, and users who need to use these exchanges can access them through the inventor quantified general protocol. Not limited to cryptocurrency exchanges, any supportRESTAgreement orFIXThe platforms of the protocol are also accessible.
This article will focus on:RESTProtocol access, for example, explains how to use the inventor's Quantified Trading Platform General Protocol to encapsulate and access the OKX Exchange API. This article refers to the REST General Protocol unless otherwise specified.
Inventors of Quantitative Trading Platforms Configure their Exchanges:
托管者上运行的策略实例 -> 通用协议程序
In the process, the trustee knows where to access the protocol procedure.
For example:http://127.0.0.1:6666/OKX
The protocol is usually run on the same device (server) as the host, so the service address is written to the localhost, and the port is used for an unoccupied system.托管者上运行的策略实例 -> 通用协议程序
In the process, exchange configuration information is passed on.托管者上运行的策略实例 -> 通用协议程序
In the process, exchange configuration information is passed on.A screenshot of the OKX plug-in configuration that was made public in the article is below:
OKX's secret key configuration information:
accessKey: accesskey123 // accesskey123 这些并不是实际秘钥,仅仅是演示
secretKey: secretkey123
passphrase: passphrase123
1st, the administrator Any strategy that runs on the inventor's Quantitative Trading Platform must have a host deployed, and the specific deployment host can refer to the platform tutorial, which is not discussed here.
2, General protocol program (plugin)
The host and the general protocol are usually deployed on the same device, and the general protocol ((service)) program can be written in any language to design, and this article is written in Python3 and can be executed directly (as with running any Python program) (pre-configured Python environment).
Of course, there is also support for running python programs on FMZ, and the general protocol can also be run as a hard drive to provide inventors with a quantified trading platform for unpackaged exchange API access.
After the General Protocol procedure is run, start listening:http://127.0.0.1:6666
In general protocol procedures, a specific path can be used, for example/OKX
I have to go to work.
When the FMZ platform's API function is called in the policy, the UTP receives a request from the host. Debugging tools can also be tested using the platform, such as:
Debugging tools page:
function main() {
return exchange.GetTicker("LTC_USDT")
}
Callingexchange.GetTicker()
Function, the general protocol processor receives the request:
POST /OKX HTTP/1.1
{
"access_key":"xxx",
"method":"ticker",
"nonce":1730275031047002000,
"params":{"symbol":"LTC_USDT"},
"secret_key":"xxx"
}
exchange.GetTicker()
I'm not going to lie.method
I mean,ticker
。exchange.GetTicker()
When called, the relevant parameters are:{"symbol":"LTC_USDT"}
。When a protocol processor receives a request from a trustee, it can know the following information from the information carried in the request: the platform API function (including parameter information) requested by the policy, the secret key of the exchange, etc.
Based on this information, the general protocol program can access the exchange's interface, obtain the required data or perform certain operations.
Usually exchange interfaces have methods such as GET/POST/PUT/DELETE, divided into public and private interfaces.
The General Protocol process receives exchange interface response data for further processing, which constitutes the data expected by the custodian (see below).
Refer to OKX spot exchange, a custom protocol implementation of the OKX class in the Python General Protocol paradigmGetTicker
、GetAccount
Let's say that we have an even function.
When a general protocol program accesses an exchange's API interface, performs certain operations or obtains certain data, it needs to feed back the results to the custodian.
Feedback to the administrator data varies depending on the interface the policy calls from, and is first divided into two categories:
The exchange interface was successfully invoked by the General Protocol process:
{
"data": null, // "data" can be of any type
"raw": null // "raw" can be of any type
}
method
Related, the data structures that are used to construct the final return of the FMZ platform API function are listed below.exchange.GetTicker()
The ticker structure that the function returns, recorded in the Info field of the ticker structure israw
Fields anddata
The data of the fields; the API functions of the platform, some of which do not require this data.Failed to call the exchange interface (business error, network error, etc.)
{
"error": "" // "error" contains an error message as a string
}
The following is a summary of the general protocol response data received by the demonstration strategy program:
// FMZ平台的调试工具中测试
function main() {
Log(exchange.GetTicker("USDT")) // 交易对不完整,缺少BaseCurrency部分,需要通用协议插件程序返回报错信息: {"error": "..."}
Log(exchange.GetTicker("LTC_USDT"))
}
The above is a brief process for the general protocol program involved in accessing the FMZ unwrapped exchange API, which only explains the call in the FMZ platform debugging tool.exchange.GetTicker()
The process when a function is created. The following details the interaction of all platform API functions.
The platform encapsulates the common functionality of the various exchanges and unifies them into a function, such as the GetTicker function, which requests the current market information of a certain variety, which is basically the API available to all exchanges. So when the policy instance accesses the API interface encapsulated by the platform, the administrator sends a request to the "General Protocol" plugin (as mentioned above):
POST /OKX HTTP/1.1
{
"access_key": "xxx",
"method": "ticker",
"nonce": 1730275031047002000,
"params": {"symbol":"LTC_USDT"},
"secret_key": "xxx"
}
When an API function (such as GetTicker) that is wrapped in a different inventor platform is called in the policy, the request format that the host sends to the universal protocol will also be different.method
andparams
◦ When designing a general protocol, use the following principles:method
The following are the request-response scenarios for all interfaces.
For example, the current trading pair is:ETH_USDT
The data that the administrator expects to be responded to by the protocol is written primarily in the data field, and a raw field can be added to record the raw data of the exchange interface.
GetTicker
{"symbol":"ETH_USDT"}
{
"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
{"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分钟
"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 What to do
{}
{}
GetTickers What to do
{}
{}
GetAccount
{}
{
"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"},
// ...
]
}
CreateOrder / Buy / Sell
{"amount":"0.1","price":"1000","symbol":"BTC_USDT","type":"buy"}
{
"data": {
"id": "BTC-USDT,123456"
}
}
GetOrders
{"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"
},
// ...
]
}
GetOrder
{
"id":"ETH-USDT,123456", // 策略中调用: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, // 如果交易所没有提供,在处理时可以赋值为0
}
}
GetHistoryOrders
{"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"
},
// ...
]
}
CancelOrder
{"id":"ETH-USDT,123456","symbol":"ETH_USDT"}
{
"data": true // 只要该JSON中没有error字段,都默认为撤单成功
}
IO
exchange.IO函数用于直接访问交易所接口,例如我们以
GET /api/v5/trade/orders-pending, 参数:instType=SPOT,instId=ETH-USDT
For example:
// 策略实例中调用
exchange.IO("api", "GET", "/api/v5/trade/orders-pending", "instType=SPOT&instId=ETH-USDT")
"__api_/api/v5/trade/orders-pending"
The contents of the method field begin with _api_, indicating that this is triggered by a call to the exchange.IO function in the policy instance.{"instId":"ETH-USDT","instType":"SPOT"} // instType=SPOT&instId=ETH-USDT编码的参数会被还原为JSON
{
"data": {"code": "0", "data": [], "msg": ""} // data属性值为交易所API:GET /api/v5/trade/orders-pending 应答的数据
}
Other
Other Inventor Platform API functions used in the policy instance, such as:exchange.Go()
、exchange.GetRawJSON()
For example, functions don't need to be wrapped, the way they are called, and the functions don't change.
In addition to the functions supported by all over-the-counter exchanges, there are also a number of futures exchange-specific API functions.
What to do
The REST General Protocol plug-in accesses the OKX exchange REST API interface and is packaged as a spot exchange object. Request and response data encapsulation is implemented in a public interface. Implementation of a private interface for signing, requesting and responding data wrapping. This example is primarily for testing learning, with the rest of the interface using simulated data to respond directly to the host for testing.
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()