def __init__(self, **kwargs): """Initialize Trade module.""" e = None if not kwargs.get("account"): e = Error("param account miss") if not kwargs.get("strategy"): e = Error("param strategy miss") if not kwargs.get("symbol"): e = Error("param symbol miss") if not kwargs.get("host"): kwargs["host"] = "https://api.binance.com" if not kwargs.get("wss"): kwargs["wss"] = "wss://stream.binance.com:9443" if not kwargs.get("access_key"): e = Error("param access_key miss") if not kwargs.get("secret_key"): e = Error("param secret_key miss") if e: logger.error(e, caller=self) SingleTask.run(kwargs["error_callback"], e) SingleTask.run(kwargs["init_callback"], False) self._account = kwargs["account"] self._strategy = kwargs["strategy"] self._platform = kwargs["platform"] self._symbol = kwargs["symbol"] self._host = kwargs["host"] self._wss = kwargs["wss"] self._access_key = kwargs["access_key"] self._secret_key = kwargs["secret_key"] self._order_update_callback = kwargs.get("order_update_callback") self._init_callback = kwargs.get("init_callback") self._error_callback = kwargs.get("error_callback") self._raw_symbol = self._symbol.replace( "/", "") # Row symbol name, same as Binance Exchange. self._listen_key = None # Listen key for Websocket authentication. self._assets = { } # Asset data. e.g. {"BTC": {"free": "1.1", "locked": "2.2", "total": "3.3"}, ... } self._orders = {} # Order data. e.g. {order_id: order, ... } # Initialize our REST API client. self._rest_api = BinanceRestAPI(self._access_key, self._secret_key, self._host) # Create a loop run task to reset listen key every 30 minutes. LoopRunTask.register(self._reset_listen_key, 60 * 30) # Create a coroutine to initialize Websocket connection. SingleTask.run(self._init_websocket) LoopRunTask.register(self._send_heartbeat_msg, 10)
async def create_order(self, action, price, quantity, *args, **kwargs): """Create an order. Args: action: Trade direction, `BUY` or `SELL`. price: Price of each order. quantity: The buying or selling quantity. kwargs: order_type: Order type, `LIMIT` / `MARKET`, default is `LIMIT`. Returns: order_id: Order id if created successfully, otherwise it's None. error: Error information, otherwise it's None. """ order_type = kwargs.get("order_type", ORDER_TYPE_LIMIT) client_order_id = kwargs.get("client_order_id") if action == ORDER_ACTION_BUY: if order_type == ORDER_TYPE_LIMIT: t = "buy-limit" elif order_type == ORDER_TYPE_MARKET: t = "buy-market" else: e = Error( "order_type error! order_type: {}".format(order_type)) logger.error(e, caller=self) SingleTask.run(self._error_callback, e) return None, "order type error" elif action == ORDER_ACTION_SELL: if order_type == ORDER_TYPE_LIMIT: t = "sell-limit" elif order_type == ORDER_TYPE_MARKET: t = "sell-market" else: e = Error( "order_type error! order_type:: {}".format(order_type)) logger.error(e, caller=self) SingleTask.run(self._error_callback, e) return None, "order type error" else: logger.error("action error! action:", action, caller=self) e = Error("action error! action:: {}".format(action)) logger.error(e, caller=self) SingleTask.run(self._error_callback, e) return None, "action error" result, error = await self._rest_api.create_order( self._raw_symbol, price, quantity, t, client_order_id) if error: SingleTask.run(self._error_callback, error) return None, error order_id = result["data"] return order_id, None
def __init__(self, **kwargs): """Initialize Trade module.""" e = None if not kwargs.get("account"): e = Error("param account miss") if not kwargs.get("strategy"): e = Error("param strategy miss") if not kwargs.get("symbol"): e = Error("param symbol miss") if not kwargs.get("host"): kwargs["host"] = "https://api.huobi.pro" if not kwargs.get("wss"): kwargs["wss"] = "wss://api.huobi.pro" if not kwargs.get("access_key"): e = Error("param access_key miss") if not kwargs.get("secret_key"): e = Error("param secret_key miss") if e: logger.error(e, caller=self) SingleTask.run(self._error_callback, e) SingleTask.run(kwargs["init_callback"], False) return self._account = kwargs["account"] self._strategy = kwargs["strategy"] self._platform = kwargs["platform"] self._symbol = kwargs["symbol"] self._host = kwargs["host"] self._wss = kwargs["wss"] self._access_key = kwargs["access_key"] self._secret_key = kwargs["secret_key"] self._order_update_callback = kwargs.get("order_update_callback") self._init_callback = kwargs.get("init_callback") self._error_callback = kwargs.get("error_callback") self._raw_symbol = self._symbol.replace("/", "").lower() self._order_channel = "orders.{}".format(self._raw_symbol) self._assets = {} self._orders = {} # Initialize our REST API client. self._rest_api = HuobiRestAPI( self._access_key, self._secret_key, self._host, ) url = self._wss + "/ws/v1" self._ws = Websocket(url, self.connected_callback, process_binary_callback=self.process_binary)
async def _auth_success_callback(self): # Get current open orders. success, error = await self._rest_api.get_open_orders(self._raw_symbol) if error: e = Error("get open orders error: {}".format(error)) SingleTask.run(self._error_callback, e) SingleTask.run(self._init_callback, False) return for order_info in success["data"]: data = { "order-id": order_info["id"], "order-type": order_info["type"], "order-state": order_info["state"], "unfilled-amount": float(order_info["amount"]) - float(order_info["filled-amount"]), "order-price": float(order_info["price"]), "price": float(order_info["price"]), "order-amount": float(order_info["amount"]), "created-at": order_info["created-at"], "utime": order_info["created-at"], } self._update_order(data) # Subscript order channel. params = {"op": "sub", "topic": self._order_channel} await self._ws.send(params)
async def process_binary(self, raw): """Process binary message that received from websocket. Args: raw: Binary message received from websocket. Returns: None. """ msg = json.loads(gzip.decompress(raw).decode()) logger.debug("msg:", msg, caller=self) op = msg.get("op") if op == "auth": if msg["err-code"] != 0: e = Error( "Websocket connection authorized failed: {}".format(msg)) logger.error(e, caller=self) SingleTask.run(self._error_callback, e) SingleTask.run(self._init_callback, False) return logger.info("Websocket connection authorized successfully.", caller=self) await self._auth_success_callback() elif op == "ping": # ping params = {"op": "pong", "ts": msg["ts"]} await self._ws.send(params) elif op == "sub": if msg["topic"] != self._order_channel: return if msg["err-code"] != 0: e = Error("subscribe order event error: {}".format(msg)) SingleTask.run(self._error_callback, e) SingleTask.run(self._init_callback, False) else: SingleTask.run(self._init_callback, True) elif op == "notify": if msg["topic"] != self._order_channel: return data = msg["data"] data["utime"] = msg["ts"] self._update_order(data)
async def connected_callback(self): """After websocket connection created successfully, pull back all open order information.""" logger.info("Websocket connection authorized successfully.", caller=self) order_infos, error = await self._rest_api.get_open_orders(self._raw_symbol) if error: e = Error("get open orders error: {}".format(error)) SingleTask.run(self._error_callback, e) SingleTask.run(self._init_callback, False) return for order_info in order_infos: if order_info["status"] == "NEW": status = ORDER_STATUS_SUBMITTED elif order_info["status"] == "PARTIALLY_FILLED": status = ORDER_STATUS_PARTIAL_FILLED elif order_info["status"] == "FILLED": status = ORDER_STATUS_FILLED elif order_info["status"] == "CANCELED": status = ORDER_STATUS_CANCELED elif order_info["status"] == "REJECTED": status = ORDER_STATUS_FAILED elif order_info["status"] == "EXPIRED": status = ORDER_STATUS_FAILED else: logger.warn("unknown status:", order_info, caller=self) SingleTask.run(self._error_callback, "order status error.") continue order_id = str(order_info["orderId"]) info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_id": order_id, "client_order_id": order_info["clientOrderId"], "action": ORDER_ACTION_BUY if order_info["side"] == "BUY" else ORDER_ACTION_SELL, "order_type": ORDER_TYPE_LIMIT if order_info["type"] == "LIMIT" else ORDER_TYPE_MARKET, "symbol": self._symbol, "price": order_info["price"], "quantity": order_info["origQty"], "remain": float(order_info["origQty"]) - float(order_info["executedQty"]), "status": status, "avg_price": order_info["price"], "ctime": order_info["time"], "utime": order_info["updateTime"] } order = Order(**info) self._orders[order_id] = order SingleTask.run(self._order_update_callback, copy.copy(order)) SingleTask.run(self._init_callback, True)
async def _init_websocket(self): """Initialize Websocket connection.""" # Get listen key first. success, error = await self._rest_api.get_listen_key() if error: e = Error("get listen key failed: {}".format(error)) logger.error(e, caller=self) SingleTask.run(self._error_callback, e) SingleTask.run(self._init_callback, False) return self._listen_key = success["listenKey"] uri = "/ws/" + self._listen_key url = urljoin(self._wss, uri) self._ws = Websocket(url, self.connected_callback, process_callback=self.process)
def __init__(self, **kwargs): """Initialize.""" e = None if not kwargs.get("account"): e = Error("param account miss") if not kwargs.get("strategy"): e = Error("param strategy miss") if not kwargs.get("symbol"): e = Error("param symbol miss") if not kwargs.get("host"): kwargs["host"] = "https://www.okex.com" if not kwargs.get("wss"): kwargs["wss"] = "wss://real.okex.com:8443" if not kwargs.get("access_key"): e = Error("param access_key miss") if not kwargs.get("secret_key"): e = Error("param secret_key miss") if not kwargs.get("passphrase"): e = Error("param passphrase miss") if e: logger.error(e, caller=self) SingleTask.run(kwargs["error_callback"], e) SingleTask.run(kwargs["init_callback"], False) return self._account = kwargs["account"] self._strategy = kwargs["strategy"] self._platform = kwargs["platform"] self._symbol = kwargs["symbol"] self._host = kwargs["host"] self._wss = kwargs["wss"] self._access_key = kwargs["access_key"] self._secret_key = kwargs["secret_key"] self._passphrase = kwargs["passphrase"] self._order_update_callback = kwargs.get("order_update_callback") self._init_callback = kwargs.get("init_callback") self._error_callback = kwargs.get("error_callback") self._raw_symbol = self._symbol.replace("/", "-") self._order_channel = "spot/order:{symbol}".format( symbol=self._raw_symbol) url = self._wss + "/ws/v3" self._ws = Websocket(url, self.connected_callback, process_binary_callback=self.process_binary) self._assets = { } # Asset object. e.g. {"BTC": {"free": "1.1", "locked": "2.2", "total": "3.3"}, ... } self._orders = {} # Order objects. e.g. {"order_id": Order, ... } # Initializing our REST API client. self._rest_api = OKExRestAPI(self._access_key, self._secret_key, self._passphrase, self._host) # Create a loop run task to send ping message to server per 5 seconds. LoopRunTask.register(self._send_heartbeat_msg, 5)
def __init__(self, strategy=None, platform=None, symbol=None, host=None, wss=None, account=None, access_key=None, secret_key=None, passphrase=None, order_update_callback=None, position_update_callback=None, init_callback=None, error_callback=None, **kwargs): """Initialize trade object.""" kwargs["strategy"] = strategy kwargs["platform"] = platform kwargs["symbol"] = symbol kwargs["host"] = host kwargs["wss"] = wss kwargs["account"] = account kwargs["access_key"] = access_key kwargs["secret_key"] = secret_key kwargs["passphrase"] = passphrase kwargs["order_update_callback"] = self._on_order_update_callback kwargs["position_update_callback"] = self._on_position_update_callback kwargs["init_callback"] = self._on_init_callback kwargs["error_callback"] = self._on_error_callback self._raw_params = copy.copy(kwargs) self._order_update_callback = order_update_callback self._position_update_callback = position_update_callback self._init_callback = init_callback self._error_callback = error_callback if platform == const.BINANCE: from aioquant.platform.binance import BinanceTrade as T elif platform == const.HUOBI: from aioquant.platform.huobi import HuobiTrade as T elif platform == const.OKEX: from aioquant.platform.okex import OKExTrade as T else: logger.error("platform error:", platform, caller=self) e = Error("platform error") SingleTask.run(self._on_error_callback, e) SingleTask.run(self._on_init_callback, False) return self._t = T(**kwargs)
def _update_order(self, order_info): """Order update. Args: order_info: Order information. Returns: None. Note: order-state: Order status, `submitting` / `submitted` / `partial-filled` / `partial-canceled` / `filled` / `canceled` """ order_id = str(order_info["order-id"]) action = ORDER_ACTION_BUY if order_info["order-type"] in [ "buy-market", "buy-limit" ] else ORDER_ACTION_SELL state = order_info["order-state"] remain = "%.8f" % float(order_info["unfilled-amount"]) avg_price = "%.8f" % float(order_info["price"]) ctime = order_info["created-at"] utime = order_info["utime"] if state == "canceled": status = ORDER_STATUS_CANCELED elif state == "partial-canceled": status = ORDER_STATUS_CANCELED elif state == "submitting": status = ORDER_STATUS_SUBMITTED elif state == "submitted": status = ORDER_STATUS_SUBMITTED elif state == "partial-filled": status = ORDER_STATUS_PARTIAL_FILLED elif state == "filled": status = ORDER_STATUS_FILLED else: e = Error("status error! order_info: {}".format(order_info)) logger.error(e, caller=self) SingleTask.run(self._error_callback, e) return None order = self._orders.get(order_id) if not order: info = { "platform": self._platform, "account": self._account, "strategy": self._strategy, "order_id": order_id, "action": action, "symbol": self._symbol, "price": "%.8f" % float(order_info["order-price"]), "quantity": "%.8f" % float(order_info["order-amount"]), "remain": remain, "status": status } order = Order(**info) self._orders[order_id] = order order.remain = remain order.status = status order.avg_price = avg_price order.ctime = ctime order.utime = utime SingleTask.run(self._order_update_callback, copy.copy(order)) if status in [ ORDER_STATUS_FAILED, ORDER_STATUS_CANCELED, ORDER_STATUS_FILLED ]: self._orders.pop(order_id)
async def process_binary(self, raw): """Process binary message that received from websocket. Args: raw: Binary message received from websocket. Returns: None. """ decompress = zlib.decompressobj(-zlib.MAX_WBITS) msg = decompress.decompress(raw) msg += decompress.flush() msg = msg.decode() if msg == "pong": return logger.debug("msg:", msg, caller=self) msg = json.loads(msg) # Authorization message received. if msg.get("event") == "login": if not msg.get("success"): e = Error( "Websocket connection authorized failed: {}".format(msg)) logger.error(e, caller=self) SingleTask.run(self._error_callback, e) SingleTask.run(self._init_callback, False) return logger.info("Websocket connection authorized successfully.", caller=self) # Fetch orders from server. (open + partially filled) order_infos, error = await self._rest_api.get_open_orders( self._raw_symbol) if error: e = Error("get open orders error: {}".format(msg)) SingleTask.run(self._error_callback, e) SingleTask.run(self._init_callback, False) return if len(order_infos) > 100: logger.warn("order length too long! (more than 100)", caller=self) for order_info in order_infos: order_info["ctime"] = order_info["created_at"] order_info["utime"] = order_info["timestamp"] self._update_order(order_info) # Subscribe order channel. data = {"op": "subscribe", "args": [self._order_channel]} await self._ws.send(data) return # Subscribe response message received. if msg.get("event") == "subscribe": if msg.get("channel") == self._order_channel: SingleTask.run(self._init_callback, True) else: e = Error("subscribe order event error: {}".format(msg)) SingleTask.run(self._error_callback, e) SingleTask.run(self._init_callback, False) return # Order update message received. if msg.get("table") == "spot/order": for data in msg["data"]: data["ctime"] = data["timestamp"] data["utime"] = data["last_fill_time"] self._update_order(data)