[TOC]
Nền tảng giao dịch định lượng của nhà phát minh hỗ trợ nhiều sàn giao dịch tiền điện tử và bao gồm các sàn giao dịch chính trên thị trường. Tuy nhiên, vẫn còn nhiều sàn giao dịch chưa được bao gồm, người dùng cần sử dụng các sàn giao dịch này có thể truy cập thông qua giao thức chung định lượng của nhà phát minh.RESTthỏa thuận hoặcFIXCác nền tảng của thỏa thuận cũng có thể được truy cập.
Bài viết này sẽRESTVí dụ như giao thức truy cập, giải thích cách sử dụng giao thức chung của nền tảng giao dịch định lượng của nhà phát minh để gói và truy cập vào OKX Exchange API.
Các nhà phát minh định lượng nền tảng giao dịch cấu hình sàn giao dịch:
托管者上运行的策略实例 -> 通用协议程序
Trong quá trình này, người quản lý biết nơi truy cập vào quy trình giao thức chung.
Ví dụ:http://127.0.0.1:6666/OKX
Các chương trình giao thức chung thường chạy trên cùng một thiết bị (máy chủ), vì vậy địa chỉ dịch vụ được viết vào máy chủ (localhost), cảng sử dụng một hệ thống không chiếm dụng.托管者上运行的策略实例 -> 通用协议程序
Trong quá trình này, thông tin cấu hình của sàn giao dịch được truyền đi.托管者上运行的策略实例 -> 通用协议程序
Trong quá trình này, thông tin cấu hình của sàn giao dịch được truyền đi.Các trang web của các công ty khác cũng có thể tham khảo thông tin về các ứng dụng này.
Thông tin cấu hình khóa bí mật OKX:
accessKey: accesskey123 // accesskey123 这些并不是实际秘钥,仅仅是演示
secretKey: secretkey123
passphrase: passphrase123
Người quản lý Bất kỳ chiến lược thực tế nào chạy trên nền tảng giao dịch định lượng của nhà phát minh đều phải triển khai một người quản lý, người quản lý triển khai cụ thể có thể tham khảo hướng dẫn nền tảng, không được mô tả ở đây.
2, Ứng dụng giao thức chung (plugin)
Người quản lý và giao thức chung thường được triển khai trên cùng một thiết bị, và các chương trình giao thức chung có thể sử dụng bất kỳ ngôn ngữ nào để viết thiết kế, và bài viết này được viết bằng Python 3.
Tất nhiên, FMZ cũng hỗ trợ chạy các chương trình python, và cũng có thể sử dụng giao thức này như một ổ đĩa thực để cung cấp cho các nhà phát minh nền tảng giao dịch định lượng hỗ trợ truy cập API giao dịch chưa đóng gói.
Sau khi chạy chương trình giao thức chung, bắt đầu nghe lén:http://127.0.0.1:6666
Các chương trình giao thức chung có thể sử dụng các đường dẫn cụ thể, chẳng hạn như/OKX
Những người bị bắt giữ ở đây là những người bị bắt.
Khi gọi các hàm API của nền tảng (FMZ) trong chính sách, các chương trình giao thức chung sẽ nhận được các yêu cầu từ người quản lý. Các công cụ giải quyết của nền tảng cũng có thể được thử nghiệm, ví dụ:
Trang công cụ gỡ lỗi:
function main() {
return exchange.GetTicker("LTC_USDT")
}
Gọi điệnexchange.GetTicker()
Các chức năng, các chương trình giao thức chung nhận được yêu cầu:
POST /OKX HTTP/1.1
{
"access_key":"xxx",
"method":"ticker",
"nonce":1730275031047002000,
"params":{"symbol":"LTC_USDT"},
"secret_key":"xxx"
}
exchange.GetTicker()
Những người khác cũng có thể làm điều đó.method
Đó làticker
。exchange.GetTicker()
Khi gọi, các tham số liên quan là:{"symbol":"LTC_USDT"}
。Khi chương trình giao thức chung nhận được yêu cầu từ người quản lý, thông tin có thể được biết dựa trên thông tin được mang theo trong yêu cầu: các chức năng API nền tảng được yêu cầu (bao gồm thông tin tham số) và các khóa bí mật của sàn giao dịch.
Dựa trên thông tin này, chương trình giao thức chung có thể truy cập vào giao diện giao dịch, lấy dữ liệu hoặc thực hiện một số hoạt động.
Thông thường giao diện giao dịch có các phương pháp như GET / POST / PUT / DELETE, được chia thành giao diện công cộng và giao diện riêng tư.
Các quy trình giao thức chung nhận được dữ liệu phản hồi giao diện giao dịch để xử lý thêm, tạo thành dữ liệu mà người quản lý mong đợi (được giới thiệu dưới đây).
Cụ thể là OKX Exchange, một ứng dụng của lớp OKX trong mô hình giao thức chung Python.GetTicker
、GetAccount
Tương tự như vậy.
Khi một chương trình giao thức chung truy cập vào giao diện API của sàn giao dịch, thực hiện một số thao tác hoặc thu thập một số dữ liệu, nó cần phản hồi kết quả cho người quản lý.
Dữ liệu phản hồi cho người quản lý khác nhau tùy thuộc vào giao diện mà chính sách được gọi và được chia thành hai loại:
Một số người khác cũng cho biết rằng họ đang tìm kiếm một ứng dụng khác để gọi giao dịch.
{
"data": null, // "data" can be of any type
"raw": null // "raw" can be of any type
}
method
Liên quan, cấu trúc dữ liệu được sử dụng để xây dựng các hàm API nền tảng FMZ cuối cùng được trả về, sau đó sẽ có danh sách tất cả các giao diện.exchange.GetTicker()
Tiến trình tick được trả về, được ghi trong Info field của tickraw
Các trường vàdata
Một số ứng dụng trên nền tảng không cần dữ liệu này.Chương trình giao thức chung gọi giao diện giao dịch thất bại (lỗi kinh doanh, lỗi mạng, v.v.)
{
"error": "" // "error" contains an error message as a string
}
Dữ liệu phản hồi giao thức chung của chương trình chiến lược trình bày:
// FMZ平台的调试工具中测试
function main() {
Log(exchange.GetTicker("USDT")) // 交易对不完整,缺少BaseCurrency部分,需要通用协议插件程序返回报错信息: {"error": "..."}
Log(exchange.GetTicker("LTC_USDT"))
}
Những nội dung trên là một quá trình ngắn gọn của các chương trình giao thức chung tham gia vào việc truy cập (FMZ chưa được đóng gói) API sàn giao dịch, quá trình này chỉ giải thích các cuộc gọi trong công cụ debugging nền tảng (FMZ).exchange.GetTicker()
Tiến trình trong khi chức năng. Tiếp theo sẽ được chi tiết chi tiết về các chi tiết tương tác của tất cả các chức năng API nền tảng.
Nền tảng bao bọc các chức năng chung của các sàn giao dịch, tất cả đều thống nhất thành một chức năng, chẳng hạn như chức năng GetTicker, yêu cầu thông tin thị trường hiện tại của một loại nào đó, về cơ bản là một API có sẵn cho tất cả các sàn giao dịch. Vì vậy, khi truy cập vào giao diện API được bao bọc trên nền tảng trong trường hợp chính sách, người quản lý sẽ gửi yêu cầu đến plugin "General Protocol" (được đề cập ở trên):
POST /OKX HTTP/1.1
{
"access_key": "xxx",
"method": "ticker",
"nonce": 1730275031047002000,
"params": {"symbol":"LTC_USDT"},
"secret_key": "xxx"
}
Khi gọi các hàm API được bọc bởi các nền tảng phát minh khác nhau trong chính sách (ví dụ như GetTicker), các nhà quản lý sẽ gửi các yêu cầu khác nhau đến giao thức chung.method
vàparams
◎ Khi thiết kế giao thức chung, hãy dựa vàomethod
Các nội dung của các giao diện này được sử dụng để thực hiện các hoạt động cụ thể. Dưới đây là tất cả các trường hợp yêu cầu - trả lời của giao diện.
Ví dụ, cặp giao dịch hiện tại là:ETH_USDT
, sau đó không còn nói gì nữa. Dữ liệu mà người quản lý mong đợi giao thức tổng quát trả lời được viết chủ yếu trong trường dữ liệu, hoặc có thể thêm một trường raw ghi lại dữ liệu gốc của giao diện giao dịch.
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Đang được thực hiện
{}
{}
GetTickersĐang được thực hiện
{}
{}
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 / Mua / Bán
{"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"
},
// ...
]
}
Hủy lệnh
{"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
Ví dụ:
// 策略实例中调用
exchange.IO("api", "GET", "/api/v5/trade/orders-pending", "instType=SPOT&instId=ETH-USDT")
"__api_/api/v5/trade/orders-pending"
Nội dung của trường method bắt đầu bằng __api_, cho thấy điều này được kích hoạt bởi cuộc gọi của hàm exchange.IO trong trường hợp chính sách.{"instId":"ETH-USDT","instType":"SPOT"} // instType=SPOT&instId=ETH-USDT编码的参数会被还原为JSON
{
"data": {"code": "0", "data": [], "msg": ""} // data属性值为交易所API:GET /api/v5/trade/orders-pending 应答的数据
}
Những người khác
Các chức năng API nền tảng người phát minh khác được sử dụng trong các trường hợp chiến lược, ví dụ:exchange.Go()
、exchange.GetRawJSON()
Các chức năng như không cần đóng gói, cách gọi, chức năng không thay đổi.
Ngoài việc hỗ trợ các chức năng của tất cả các sàn giao dịch trực tiếp, sàn giao dịch tương lai cũng có một số chức năng API riêng biệt của sàn giao dịch tương lai.
Đang được thực hiện
REST General Protocol Plug truy cập OKX Exchange REST API, được đóng gói như một đối tượng giao dịch trực tiếp. Thực hiện một giao diện công cộng với dữ liệu yêu cầu và trả lời. Thực hiện một giao diện riêng để ký kết, yêu cầu và trả lời dữ liệu bao bì. Mô hình này chủ yếu là để thử nghiệm học tập, phần còn lại của giao diện sử dụng dữ liệu giả lập trả lời trực tiếp cho người quản lý để thử nghiệm.
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()