[Add]websocket api for OnetokenGateway
This commit is contained in:
parent
94c4e0beda
commit
194ccdb09c
@ -13,6 +13,7 @@ from vnpy.gateway.oes import OesGateway
|
|||||||
from vnpy.gateway.okex import OkexGateway
|
from vnpy.gateway.okex import OkexGateway
|
||||||
from vnpy.gateway.huobi import HuobiGateway
|
from vnpy.gateway.huobi import HuobiGateway
|
||||||
from vnpy.gateway.bitfinex import BitfinexGateway
|
from vnpy.gateway.bitfinex import BitfinexGateway
|
||||||
|
from vnpy.gateway.onetoken import OnetokenGateway
|
||||||
|
|
||||||
from vnpy.app.cta_strategy import CtaStrategyApp
|
from vnpy.app.cta_strategy import CtaStrategyApp
|
||||||
from vnpy.app.csv_loader import CsvLoaderApp
|
from vnpy.app.csv_loader import CsvLoaderApp
|
||||||
@ -36,6 +37,7 @@ def main():
|
|||||||
main_engine.add_gateway(OkexGateway)
|
main_engine.add_gateway(OkexGateway)
|
||||||
main_engine.add_gateway(HuobiGateway)
|
main_engine.add_gateway(HuobiGateway)
|
||||||
main_engine.add_gateway(BitfinexGateway)
|
main_engine.add_gateway(BitfinexGateway)
|
||||||
|
main_engine.add_gateway(OnetokenGateway)
|
||||||
|
|
||||||
main_engine.add_app(CtaStrategyApp)
|
main_engine.add_app(CtaStrategyApp)
|
||||||
main_engine.add_app(CtaBacktesterApp)
|
main_engine.add_app(CtaBacktesterApp)
|
||||||
|
@ -3,16 +3,19 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import sys
|
||||||
import hmac
|
import hmac
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
from requests import ConnectionError
|
from requests import ConnectionError
|
||||||
|
|
||||||
from vnpy.api.rest import Request, RestClient
|
from vnpy.api.rest import Request, RestClient
|
||||||
|
from vnpy.api.websocket import WebsocketClient
|
||||||
from vnpy.trader.constant import (
|
from vnpy.trader.constant import (
|
||||||
Direction,
|
Direction,
|
||||||
Exchange,
|
Exchange,
|
||||||
@ -22,20 +25,34 @@ from vnpy.trader.constant import (
|
|||||||
)
|
)
|
||||||
from vnpy.trader.gateway import BaseGateway
|
from vnpy.trader.gateway import BaseGateway
|
||||||
from vnpy.trader.object import (
|
from vnpy.trader.object import (
|
||||||
|
TickData,
|
||||||
PositionData,
|
PositionData,
|
||||||
AccountData,
|
AccountData,
|
||||||
OrderRequest,
|
OrderRequest,
|
||||||
CancelRequest,
|
CancelRequest,
|
||||||
SubscribeRequest,
|
SubscribeRequest,
|
||||||
ContractData
|
ContractData,
|
||||||
|
OrderData,
|
||||||
|
TradeData
|
||||||
)
|
)
|
||||||
|
from vnpy.trader.event import EVENT_TIMER
|
||||||
|
|
||||||
REST_HOST = 'https://1token.trade/api'
|
|
||||||
|
REST_HOST = "https://1token.trade/api"
|
||||||
|
DATA_WEBSOCKET_HOST = "wss://1token.trade/api/v1/ws/tick"
|
||||||
|
TRADE_WEBSOCKET_HOST = "wss://1token.trade/api/v1/ws/trade"
|
||||||
|
|
||||||
DIRECTION_VT2ONETOKEN = {Direction.LONG: "b", Direction.SHORT: "s"}
|
DIRECTION_VT2ONETOKEN = {Direction.LONG: "b", Direction.SHORT: "s"}
|
||||||
DIRECTION_ONETOKEN2VT = {v: k for k, v in DIRECTION_VT2ONETOKEN.items()}
|
DIRECTION_ONETOKEN2VT = {v: k for k, v in DIRECTION_VT2ONETOKEN.items()}
|
||||||
|
|
||||||
|
|
||||||
|
EXCHANGE_VT2ONETOKEN = {
|
||||||
|
Exchange.OKEX: "okex",
|
||||||
|
Exchange.HUOBI: "huobi"
|
||||||
|
}
|
||||||
|
EXCHANGE_ONETOKEN2VT = {v: k for k, v in EXCHANGE_VT2ONETOKEN.items()}
|
||||||
|
|
||||||
|
|
||||||
class OnetokenGateway(BaseGateway):
|
class OnetokenGateway(BaseGateway):
|
||||||
"""
|
"""
|
||||||
VN Trader Gateway for 1Token connection
|
VN Trader Gateway for 1Token connection
|
||||||
@ -44,7 +61,7 @@ class OnetokenGateway(BaseGateway):
|
|||||||
default_setting = {
|
default_setting = {
|
||||||
"OT Key": "",
|
"OT Key": "",
|
||||||
"OT Secret": "",
|
"OT Secret": "",
|
||||||
"交易所": "",
|
"交易所": ["BINANCE", "BITMEX", "OKEX", "OKEF"],
|
||||||
"账户": "",
|
"账户": "",
|
||||||
"会话数": 3,
|
"会话数": 3,
|
||||||
"代理地址": "127.0.0.1",
|
"代理地址": "127.0.0.1",
|
||||||
@ -53,9 +70,13 @@ class OnetokenGateway(BaseGateway):
|
|||||||
|
|
||||||
def __init__(self, event_engine):
|
def __init__(self, event_engine):
|
||||||
"""Constructor"""
|
"""Constructor"""
|
||||||
super(OnetokenGateway, self).__init__(event_engine, "1Token")
|
super(OnetokenGateway, self).__init__(event_engine, "1TOKEN")
|
||||||
|
|
||||||
self.rest_api = OnetokenRestApi(self)
|
self.rest_api = OnetokenRestApi(self)
|
||||||
|
self.data_ws_api = OnetokenDataWebsocketApi(self)
|
||||||
|
self.trade_ws_api = OnetokenTradeWebsocketApi(self)
|
||||||
|
|
||||||
|
self.count = 0
|
||||||
|
|
||||||
def connect(self, setting: dict):
|
def connect(self, setting: dict):
|
||||||
""""""
|
""""""
|
||||||
@ -66,13 +87,18 @@ class OnetokenGateway(BaseGateway):
|
|||||||
account = setting["账户"]
|
account = setting["账户"]
|
||||||
proxy_host = setting["代理地址"]
|
proxy_host = setting["代理地址"]
|
||||||
proxy_port = setting["代理端口"]
|
proxy_port = setting["代理端口"]
|
||||||
|
|
||||||
self.rest_api.connect(key, secret, session_number,
|
self.rest_api.connect(key, secret, session_number,
|
||||||
exchange, account, proxy_host, proxy_port)
|
exchange, account, proxy_host, proxy_port)
|
||||||
|
self.data_ws_api.connect(proxy_host, proxy_port)
|
||||||
|
self.trade_ws_api.connect(
|
||||||
|
key, secret, exchange, account, proxy_host, proxy_port)
|
||||||
|
|
||||||
|
self.init_ping()
|
||||||
|
|
||||||
def subscribe(self, req: SubscribeRequest):
|
def subscribe(self, req: SubscribeRequest):
|
||||||
""""""
|
""""""
|
||||||
pass
|
self.data_ws_api.subscribe(req)
|
||||||
# self.ws_api.subscribe(req)
|
|
||||||
|
|
||||||
def send_order(self, req: OrderRequest):
|
def send_order(self, req: OrderRequest):
|
||||||
""""""
|
""""""
|
||||||
@ -93,6 +119,22 @@ class OnetokenGateway(BaseGateway):
|
|||||||
def close(self):
|
def close(self):
|
||||||
""""""
|
""""""
|
||||||
self.rest_api.stop()
|
self.rest_api.stop()
|
||||||
|
self.data_ws_api.stop()
|
||||||
|
self.trade_ws_api.stop()
|
||||||
|
|
||||||
|
def process_timer_event(self, event):
|
||||||
|
""""""
|
||||||
|
self.count += 1
|
||||||
|
if self.count < 20:
|
||||||
|
return
|
||||||
|
self.count = 0
|
||||||
|
|
||||||
|
self.data_ws_api.ping()
|
||||||
|
self.trade_ws_api.ping()
|
||||||
|
|
||||||
|
def init_ping(self):
|
||||||
|
""""""
|
||||||
|
self.event_engine.register(EVENT_TIMER, self.process_timer_event)
|
||||||
|
|
||||||
|
|
||||||
class OnetokenRestApi(RestClient):
|
class OnetokenRestApi(RestClient):
|
||||||
@ -123,23 +165,24 @@ class OnetokenRestApi(RestClient):
|
|||||||
"""
|
"""
|
||||||
method = request.method
|
method = request.method
|
||||||
|
|
||||||
endpoint = '/' + request.path.split('/', 3)[3]
|
endpoint = "/" + request.path.split("/", 3)[3]
|
||||||
# v1/trade/okex/mock-example/info -> okex/mock-example/info
|
# v1/trade/okex/mock-example/info -> okex/mock-example/info
|
||||||
parsed_url = urlparse(endpoint)
|
parsed_url = urlparse(endpoint)
|
||||||
path = parsed_url.path
|
path = parsed_url.path
|
||||||
|
|
||||||
nonce = str(int(time.time() * 1e6))
|
nonce = str(int(time.time() * 1e6))
|
||||||
data = request.data
|
data = request.data
|
||||||
json_str = data if data else ''
|
json_str = data if data else ""
|
||||||
|
|
||||||
message = method + path + nonce + json_str
|
message = method + path + nonce + json_str
|
||||||
|
|
||||||
signature = hmac.new(bytes(self.secret, 'utf8'), bytes(message, 'utf8'), digestmod=hashlib.sha256).hexdigest()
|
signature = hmac.new(bytes(self.secret, "utf8"), bytes(
|
||||||
|
message, "utf8"), digestmod=hashlib.sha256).hexdigest()
|
||||||
|
|
||||||
headers = {'Api-Nonce': nonce,
|
headers = {"Api-Nonce": nonce,
|
||||||
'Api-Key': self.key,
|
"Api-Key": self.key,
|
||||||
'Api-Signature': signature,
|
"Api-Signature": signature,
|
||||||
'Content-Type': 'application/json'}
|
"Content-Type": "application/json"}
|
||||||
request.headers = headers
|
request.headers = headers
|
||||||
|
|
||||||
return request
|
return request
|
||||||
@ -174,58 +217,13 @@ class OnetokenRestApi(RestClient):
|
|||||||
|
|
||||||
self.query_time()
|
self.query_time()
|
||||||
self.query_contract()
|
self.query_contract()
|
||||||
self.query_account()
|
# self.query_account()
|
||||||
|
|
||||||
def _new_order_id(self):
|
def _new_order_id(self):
|
||||||
with self.order_count_lock:
|
with self.order_count_lock:
|
||||||
self.order_count += 1
|
self.order_count += 1
|
||||||
return self.order_count
|
return self.order_count
|
||||||
|
|
||||||
def query_account(self): # get balance and positions at the same time
|
|
||||||
""""""
|
|
||||||
self.add_request(
|
|
||||||
"GET",
|
|
||||||
"/v1/trade/{}/{}/info".format(self.exchange, self.account),
|
|
||||||
callback=self.on_query_account
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_query_account(self, data, request):
|
|
||||||
"""This is for WS Example"""
|
|
||||||
for account_data in data["position"]:
|
|
||||||
_type = account_data['type']
|
|
||||||
if 'spot' in _type: # 统计balance
|
|
||||||
account = AccountData(
|
|
||||||
accountid=account_data["contract"],
|
|
||||||
balance=float(account_data["total_amount"]),
|
|
||||||
frozen=float(account_data["frozen"]),
|
|
||||||
gateway_name=self.gateway_name
|
|
||||||
)
|
|
||||||
self.gateway.on_account(account)
|
|
||||||
elif _type == 'future': # 期货合约
|
|
||||||
long_position = PositionData(
|
|
||||||
symbol=account_data["contract"],
|
|
||||||
exchange=Exchange.OKEX, # todo add Exchange
|
|
||||||
direction=Direction.LONG,
|
|
||||||
volume=account_data['total_amount_long'],
|
|
||||||
frozen=account_data['total_amount_long'] - account_data['available_long'],
|
|
||||||
gateway_name=self.gateway_name,
|
|
||||||
# yd_volume=?
|
|
||||||
)
|
|
||||||
short_position = PositionData(
|
|
||||||
symbol=account_data["contract"],
|
|
||||||
exchange=Exchange.OKEX, # todo add Exchange
|
|
||||||
direction=Direction.SHORT,
|
|
||||||
volume=account_data['total_amount_short'],
|
|
||||||
frozen=account_data['total_amount_short'] - account_data['available_short'],
|
|
||||||
gateway_name=self.gateway_name,
|
|
||||||
# yd_volume=?
|
|
||||||
)
|
|
||||||
self.gateway.on_position(long_position)
|
|
||||||
self.gateway.on_position(short_position)
|
|
||||||
|
|
||||||
self.gateway.write_log("账户资金查询成功")
|
|
||||||
self.gateway.write_log("账户持仓查询成功")
|
|
||||||
|
|
||||||
def query_time(self):
|
def query_time(self):
|
||||||
""""""
|
""""""
|
||||||
self.add_request(
|
self.add_request(
|
||||||
@ -238,7 +236,7 @@ class OnetokenRestApi(RestClient):
|
|||||||
""""""
|
""""""
|
||||||
server_timestamp = data["server_time"]
|
server_timestamp = data["server_time"]
|
||||||
dt = datetime.utcfromtimestamp(server_timestamp)
|
dt = datetime.utcfromtimestamp(server_timestamp)
|
||||||
server_time = dt.isoformat() + 'Z'
|
server_time = dt.isoformat() + "Z"
|
||||||
local_time = datetime.utcnow().isoformat()
|
local_time = datetime.utcnow().isoformat()
|
||||||
msg = f"服务器时间:{server_time},本机时间:{local_time}"
|
msg = f"服务器时间:{server_time},本机时间:{local_time}"
|
||||||
self.gateway.write_log(msg)
|
self.gateway.write_log(msg)
|
||||||
@ -260,30 +258,31 @@ class OnetokenRestApi(RestClient):
|
|||||||
exchange=Exchange.OKEX, # todo
|
exchange=Exchange.OKEX, # todo
|
||||||
name=symbol,
|
name=symbol,
|
||||||
product=Product.SPOT, # todo
|
product=Product.SPOT, # todo
|
||||||
size=float(instrument_data['min_amount']),
|
size=float(instrument_data["min_amount"]),
|
||||||
pricetick=float(instrument_data['unit_amount']),
|
pricetick=float(instrument_data["unit_amount"]),
|
||||||
gateway_name=self.gateway_name
|
gateway_name=self.gateway_name
|
||||||
)
|
)
|
||||||
self.gateway.on_contract(contract)
|
self.gateway.on_contract(contract)
|
||||||
self.gateway.write_log("合约信息查询成功")
|
self.gateway.write_log("合约信息查询成功")
|
||||||
|
|
||||||
# Start websocket api after instruments data collected
|
# Start websocket api after instruments data collected
|
||||||
# self.gateway.ws_api.start()
|
self.gateway.data_ws_api.start()
|
||||||
|
self.gateway.trade_ws_api.start()
|
||||||
|
|
||||||
def send_order(self, req: OrderRequest):
|
def send_order(self, req: OrderRequest):
|
||||||
""""""
|
""""""
|
||||||
orderid = str(self.connect_time + self._new_order_id())
|
orderid = str(self.connect_time + self._new_order_id())
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'contract': self.exchange + '/' + req.symbol,
|
"contract": self.exchange + "/" + req.symbol,
|
||||||
'price': float(req.price),
|
"price": float(req.price),
|
||||||
"bs": DIRECTION_VT2ONETOKEN[req.direction],
|
"bs": DIRECTION_VT2ONETOKEN[req.direction],
|
||||||
'amount': float(req.volume),
|
"amount": float(req.volume),
|
||||||
'client_oid': orderid
|
"client_oid": orderid
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.offset == Offset.CLOSE:
|
if req.offset == Offset.CLOSE:
|
||||||
data['options'] = {'close': True}
|
data["options"] = {"close": True}
|
||||||
data = json.dumps(data)
|
data = json.dumps(data)
|
||||||
order = req.create_order_data(orderid, self.gateway_name)
|
order = req.create_order_data(orderid, self.gateway_name)
|
||||||
|
|
||||||
@ -304,7 +303,7 @@ class OnetokenRestApi(RestClient):
|
|||||||
def cancel_order(self, req: CancelRequest):
|
def cancel_order(self, req: CancelRequest):
|
||||||
""""""
|
""""""
|
||||||
params = {
|
params = {
|
||||||
'client_oid': req.orderid
|
"client_oid": req.orderid
|
||||||
}
|
}
|
||||||
|
|
||||||
self.add_request(
|
self.add_request(
|
||||||
@ -358,3 +357,337 @@ class OnetokenRestApi(RestClient):
|
|||||||
# Record exception if not ConnectionError
|
# Record exception if not ConnectionError
|
||||||
if not issubclass(exception_type, ConnectionError):
|
if not issubclass(exception_type, ConnectionError):
|
||||||
self.on_error(exception_type, exception_value, tb, request)
|
self.on_error(exception_type, exception_value, tb, request)
|
||||||
|
|
||||||
|
|
||||||
|
class OnetokenDataWebsocketApi(WebsocketClient):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(self, gateway):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.gateway = gateway
|
||||||
|
self.gateway_name = gateway.gateway_name
|
||||||
|
|
||||||
|
self.ticks = {}
|
||||||
|
self.callbacks = {
|
||||||
|
"auth": self.on_login,
|
||||||
|
"single-tick-verbose": self.on_tick
|
||||||
|
}
|
||||||
|
|
||||||
|
def connect(
|
||||||
|
self,
|
||||||
|
proxy_host: str,
|
||||||
|
proxy_port: int
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
self.init(DATA_WEBSOCKET_HOST, proxy_host, proxy_port)
|
||||||
|
|
||||||
|
def subscribe(self, req: SubscribeRequest):
|
||||||
|
"""
|
||||||
|
Subscribe to tick data upate.
|
||||||
|
"""
|
||||||
|
tick = TickData(
|
||||||
|
symbol=req.symbol,
|
||||||
|
exchange=req.exchange,
|
||||||
|
name=req.symbol,
|
||||||
|
datetime=datetime.now(),
|
||||||
|
gateway_name=self.gateway_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
contract_symbol = f"{req.exchange.value.lower()}/{req.symbol.lower()}"
|
||||||
|
self.ticks[contract_symbol] = tick
|
||||||
|
|
||||||
|
req = {
|
||||||
|
"uri": "subscribe-single-tick-verbose",
|
||||||
|
"contract": contract_symbol
|
||||||
|
}
|
||||||
|
self.send_packet(req)
|
||||||
|
|
||||||
|
def on_connected(self):
|
||||||
|
""""""
|
||||||
|
self.gateway.write_log("行情Websocket API连接成功")
|
||||||
|
self.login()
|
||||||
|
|
||||||
|
def on_disconnected(self):
|
||||||
|
""""""
|
||||||
|
self.gateway.write_log("行情Websocket API连接断开")
|
||||||
|
|
||||||
|
def on_packet(self, packet: dict):
|
||||||
|
""""""
|
||||||
|
channel = packet.get("uri", "")
|
||||||
|
if not channel:
|
||||||
|
return
|
||||||
|
|
||||||
|
data = packet.get("data", None)
|
||||||
|
callback = self.callbacks.get(channel, None)
|
||||||
|
|
||||||
|
if callback:
|
||||||
|
callback(data)
|
||||||
|
|
||||||
|
def on_error(self, exception_type: type, exception_value: Exception, tb):
|
||||||
|
""""""
|
||||||
|
msg = f"触发异常,状态码:{exception_type},信息:{exception_value}"
|
||||||
|
self.gateway.write_log(msg)
|
||||||
|
|
||||||
|
sys.stderr.write(self.exception_detail(
|
||||||
|
exception_type, exception_value, tb))
|
||||||
|
|
||||||
|
def login(self):
|
||||||
|
"""
|
||||||
|
Need to login befores subscribe to websocket topic.
|
||||||
|
"""
|
||||||
|
req = {"uri": "auth"}
|
||||||
|
self.send_packet(req)
|
||||||
|
|
||||||
|
self.callbacks["auth"] = self.on_login
|
||||||
|
|
||||||
|
def on_login(self, data: dict):
|
||||||
|
""""""
|
||||||
|
self.gateway.write_log("行情Websocket API登录成功")
|
||||||
|
|
||||||
|
def on_tick(self, data: dict):
|
||||||
|
""""""
|
||||||
|
contract_symbol = data["contract"]
|
||||||
|
tick = self.ticks.get(contract_symbol, None)
|
||||||
|
if not tick:
|
||||||
|
return
|
||||||
|
|
||||||
|
tick.last_price = data["last"]
|
||||||
|
tick.datetime = datetime.strptime(
|
||||||
|
data["time"][:-6], "%Y-%m-%dT%H:%M:%S.%f")
|
||||||
|
|
||||||
|
bids = data["bids"]
|
||||||
|
asks = data["asks"]
|
||||||
|
for n, buf in enumerate(bids):
|
||||||
|
tick.__setattr__("bid_price_%s" % (n + 1), buf["price"])
|
||||||
|
tick.__setattr__("bid_volume_%s" % (n + 1), buf["volume"])
|
||||||
|
|
||||||
|
for n, buf in enumerate(asks):
|
||||||
|
tick.__setattr__("ask_price_%s" % (n + 1), buf["price"])
|
||||||
|
tick.__setattr__("ask_volume_%s" % (n + 1), buf["volume"])
|
||||||
|
|
||||||
|
self.gateway.on_tick(copy(tick))
|
||||||
|
|
||||||
|
def ping(self):
|
||||||
|
""""""
|
||||||
|
self.send_packet({"uri": "ping"})
|
||||||
|
|
||||||
|
|
||||||
|
class OnetokenTradeWebsocketApi(WebsocketClient):
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(self, gateway):
|
||||||
|
""""""
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.gateway = gateway
|
||||||
|
self.gateway_name = gateway.gateway_name
|
||||||
|
|
||||||
|
self.key = ""
|
||||||
|
self.secret = ""
|
||||||
|
self.exchange = ""
|
||||||
|
self.account = ""
|
||||||
|
|
||||||
|
self.trade_count = 0
|
||||||
|
|
||||||
|
self.callbacks = {
|
||||||
|
"sub-info": self.on_subscribe_info,
|
||||||
|
"sub-order": self.on_subscribe_order,
|
||||||
|
"info": self.on_info,
|
||||||
|
"order": self.on_order
|
||||||
|
}
|
||||||
|
|
||||||
|
def connect(
|
||||||
|
self,
|
||||||
|
key: str,
|
||||||
|
secret: str,
|
||||||
|
exchange: str,
|
||||||
|
account: str,
|
||||||
|
proxy_host: str,
|
||||||
|
proxy_port: int
|
||||||
|
):
|
||||||
|
""""""
|
||||||
|
self.key = key
|
||||||
|
self.secret = secret
|
||||||
|
self.exchange = exchange
|
||||||
|
self.account = account
|
||||||
|
|
||||||
|
# Create header for ws connection
|
||||||
|
nonce = str(int(time.time() * 1e6))
|
||||||
|
path = f"/ws/{self.account}"
|
||||||
|
message = "GET" + path + nonce
|
||||||
|
|
||||||
|
signature = hmac.new(bytes(self.secret, "utf8"), bytes(
|
||||||
|
message, "utf8"), digestmod=hashlib.sha256).hexdigest()
|
||||||
|
|
||||||
|
header = {
|
||||||
|
"Api-Nonce": nonce,
|
||||||
|
"Api-Key": self.key,
|
||||||
|
"Api-Signature": signature
|
||||||
|
}
|
||||||
|
|
||||||
|
host = f"{TRADE_WEBSOCKET_HOST}/{self.exchange}/{self.account}"
|
||||||
|
|
||||||
|
self.init(host, proxy_host, proxy_port, header=header)
|
||||||
|
|
||||||
|
def subscribe_info(self):
|
||||||
|
"""
|
||||||
|
Subscribe to account update.
|
||||||
|
"""
|
||||||
|
self.send_packet({"uri": "sub-info"})
|
||||||
|
|
||||||
|
def subscribe_order(self):
|
||||||
|
"""
|
||||||
|
Subscribe to order update.
|
||||||
|
"""
|
||||||
|
self.send_packet({"uri": "sub-order"})
|
||||||
|
|
||||||
|
def on_connected(self):
|
||||||
|
""""""
|
||||||
|
self.gateway.write_log("交易Websocket API连接成功")
|
||||||
|
self.subscribe_info()
|
||||||
|
self.subscribe_order()
|
||||||
|
|
||||||
|
def on_disconnected(self):
|
||||||
|
""""""
|
||||||
|
self.gateway.write_log("交易Websocket API连接断开")
|
||||||
|
|
||||||
|
def on_packet(self, packet: dict):
|
||||||
|
""""""
|
||||||
|
if "uri" in packet:
|
||||||
|
channel = packet["uri"]
|
||||||
|
|
||||||
|
if "data" in packet:
|
||||||
|
data = packet["data"]
|
||||||
|
elif "code" in packet:
|
||||||
|
data = packet["code"]
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
|
||||||
|
elif "action" in packet:
|
||||||
|
channel = packet["action"]
|
||||||
|
data = packet.get("data", None)
|
||||||
|
else:
|
||||||
|
print(packet)
|
||||||
|
return
|
||||||
|
|
||||||
|
callback = self.callbacks.get(channel, None)
|
||||||
|
if callback:
|
||||||
|
callback(data)
|
||||||
|
|
||||||
|
def on_error(self, exception_type: type, exception_value: Exception, tb):
|
||||||
|
""""""
|
||||||
|
msg = f"触发异常,状态码:{exception_type},信息:{exception_value}"
|
||||||
|
self.gateway.write_log(msg)
|
||||||
|
|
||||||
|
sys.stderr.write(self.exception_detail(
|
||||||
|
exception_type, exception_value, tb))
|
||||||
|
|
||||||
|
def on_subscribe_info(self, data: str):
|
||||||
|
""""""
|
||||||
|
if data == "success":
|
||||||
|
self.gateway.write_log("账户资金推送订阅成功")
|
||||||
|
|
||||||
|
def on_subscribe_order(self, data: str):
|
||||||
|
""""""
|
||||||
|
if data == "success":
|
||||||
|
self.gateway.write_log("委托更新推送订阅成功")
|
||||||
|
|
||||||
|
def on_info(self, data: dict):
|
||||||
|
""""""
|
||||||
|
for account_data in data["position"]:
|
||||||
|
_type = account_data["type"]
|
||||||
|
|
||||||
|
# Spot
|
||||||
|
if "spot" in _type:
|
||||||
|
account = AccountData(
|
||||||
|
accountid=account_data["contract"],
|
||||||
|
balance=float(account_data["total_amount"]),
|
||||||
|
frozen=float(account_data["frozen"]),
|
||||||
|
gateway_name=self.gateway_name
|
||||||
|
)
|
||||||
|
self.gateway.on_account(account)
|
||||||
|
|
||||||
|
# Futures
|
||||||
|
elif _type == "future":
|
||||||
|
long_position = PositionData(
|
||||||
|
symbol=account_data["contract"],
|
||||||
|
exchange=Exchange.OKEX, # todo add Exchange
|
||||||
|
direction=Direction.LONG,
|
||||||
|
volume=account_data["total_amount_long"],
|
||||||
|
frozen=account_data["total_amount_long"] - \
|
||||||
|
account_data["available_long"],
|
||||||
|
gateway_name=self.gateway_name,
|
||||||
|
)
|
||||||
|
short_position = PositionData(
|
||||||
|
symbol=account_data["contract"],
|
||||||
|
exchange=Exchange.OKEX, # todo add Exchange
|
||||||
|
direction=Direction.SHORT,
|
||||||
|
volume=account_data["total_amount_short"],
|
||||||
|
frozen=account_data["total_amount_short"] - \
|
||||||
|
account_data["available_short"],
|
||||||
|
gateway_name=self.gateway_name,
|
||||||
|
)
|
||||||
|
self.gateway.on_position(long_position)
|
||||||
|
self.gateway.on_position(short_position)
|
||||||
|
|
||||||
|
def on_order(self, data: dict):
|
||||||
|
""""""
|
||||||
|
print("--------------------------")
|
||||||
|
for order_data in data:
|
||||||
|
print(order_data)
|
||||||
|
contract_symbol = order_data["contract"]
|
||||||
|
exchange_str, symbol = contract_symbol.split("/")
|
||||||
|
timestamp = order_data["entrust_time"][11:19]
|
||||||
|
|
||||||
|
orderid = order_data["options"]["client_oid"]
|
||||||
|
|
||||||
|
order = OrderData(
|
||||||
|
symbol=symbol,
|
||||||
|
exchange=EXCHANGE_ONETOKEN2VT[exchange_str],
|
||||||
|
orderid=orderid,
|
||||||
|
direction=DIRECTION_ONETOKEN2VT[order_data["bs"]],
|
||||||
|
price=order_data["entrust_price"],
|
||||||
|
volume=order_data["entrust_amount"],
|
||||||
|
traded=order_data["dealt_amount"],
|
||||||
|
time=timestamp,
|
||||||
|
gateway_name=self.gateway_name
|
||||||
|
)
|
||||||
|
|
||||||
|
if order_data["canceled_time"]:
|
||||||
|
order.status = Status.CANCELLED
|
||||||
|
else:
|
||||||
|
if order.traded == order.volume:
|
||||||
|
order.status = Status.ALLTRADED
|
||||||
|
elif not order.traded:
|
||||||
|
order.status = Status.NOTTRADED
|
||||||
|
else:
|
||||||
|
order.status = Status.PARTTRADED
|
||||||
|
|
||||||
|
self.gateway.on_order(order)
|
||||||
|
|
||||||
|
# Push trade data
|
||||||
|
if not order_data["last_dealt_amount"]:
|
||||||
|
return
|
||||||
|
|
||||||
|
trade_timestamp = order_data["last_update"][11:19]
|
||||||
|
self.trade_count += 1
|
||||||
|
|
||||||
|
trade = TradeData(
|
||||||
|
symbol=order.symbol,
|
||||||
|
exchange=order.exchange,
|
||||||
|
orderid=order.orderid,
|
||||||
|
tradeid=str(self.trade_count),
|
||||||
|
direction=order.direction,
|
||||||
|
price=order_data["average_dealt_price"],
|
||||||
|
volume=order_data["last_dealt_amount"],
|
||||||
|
gateway_name=self.gateway_name,
|
||||||
|
time=trade_timestamp
|
||||||
|
)
|
||||||
|
|
||||||
|
self.gateway.on_trade(trade)
|
||||||
|
|
||||||
|
def ping(self):
|
||||||
|
""""""
|
||||||
|
self.send_packet({"uri": "ping"})
|
||||||
|
Loading…
Reference in New Issue
Block a user