async def _reset_listen_key(self, *args, **kwargs): """Reset listen key.""" if not self._listen_key: logger.error("listen key not initialized!", caller=self) return await self._rest_api.put_listen_key(self._listen_key) logger.info("reset listen key success!", caller=self)
async def on_event_orderbook_update(self, orderbook: Orderbook): """ 订单薄更新 """ logger.debug("orderbook:", orderbook, caller=self) bid3_price = orderbook.bids[2][0] # 买三价格 bid4_price = orderbook.bids[3][0] # 买四价格 # 判断是否需要撤单 if self.order_id: if float(self.create_order_price) < float(bid3_price) or float( self.create_order_price) > float(bid4_price): return _, error = await self.trader.revoke_order(self.order_id) if error: logger.error("revoke order error! error:", error, caller=self) return self.order_id = None logger.info("revoke order:", self.order_id, caller=self) # 创建新订单 price = (float(bid3_price) + float(bid4_price)) / 2 quantity = "0.1" # 假设委托数量为0.1 action = ORDER_ACTION_BUY order_id, error = await self.trader.create_order( action, price, quantity) if error: logger.error("create order error! error:", error, caller=self) return self.order_id = order_id self.create_order_price = price logger.info("create new order:", order_id, caller=self)
def _update_order(self, order_info): """Order update. Args: order_info: Order information. Returns: None. """ order_id = str(order_info["order_id"]) state = order_info["state"] remain = float(order_info["size"]) - float(order_info["filled_size"]) avg_price = order_info.get("last_fill_px") ctime = tools.utctime_str_to_ms(order_info["ctime"]) utime = tools.utctime_str_to_ms(order_info["utime"]) if state == "-2": status = ORDER_STATUS_FAILED elif state == "-1": status = ORDER_STATUS_CANCELED elif state == "0": status = ORDER_STATUS_SUBMITTED elif state == "1": status = ORDER_STATUS_PARTIAL_FILLED elif state == "2": status = ORDER_STATUS_FILLED else: logger.error("status error! order_info:", order_info, caller=self) SingleTask.run(self._error_callback, "order status error.") 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, "client_order_id": order_info["client_oid"], "action": ORDER_ACTION_BUY if order_info["side"] == "buy" else ORDER_ACTION_SELL, "symbol": self._symbol, "price": order_info["price"], "quantity": order_info["size"] } order = Order(**info) self._orders[order_id] = order order.remain = remain order.status = status order.avg_price = avg_price if avg_price else 0 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 _check_connection(self, *args, **kwargs): if self._connected and self._channel and self._channel.is_open: return logger.error("CONNECTION LOSE! START RECONNECT RIGHT NOW!", caller=self) self._connected = False self._protocol = None self._channel = None self._event_handler = {} SingleTask.run(self.connect, reconnect=True)
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)
async def _on_consume_event_msg(self, channel, body, envelope, properties): try: key = "{exchange}:{routing_key}".format(exchange=envelope.exchange_name, routing_key=envelope.routing_key) funcs = self._event_handler[key] for func in funcs: SingleTask.run(func, channel, body, envelope, properties) except: logger.error("event handle error! body:", body, caller=self) return finally: await self._channel.basic_client_ack(delivery_tag=envelope.delivery_tag) # response ack
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)
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 _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)
async def _connect(self) -> None: logger.info("url:", self._url, caller=self) proxy = config.proxy session = aiohttp.ClientSession() try: self._ws = await session.ws_connect(self._url, proxy=proxy) except aiohttp.ClientConnectorError: logger.error("connect to Websocket server error! url:", self._url, caller=self) return if self._connected_callback: SingleTask.run(self._connected_callback) SingleTask.run(self._receive)
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)
async def create_order(self, action, symbol, price, quantity, order_type=ORDER_TYPE_LIMIT, client_oid=None): """Create an order. Args: action: Action type, `BUY` or `SELL`. symbol: Trading pair, e.g. `BTC-USDT`. price: Order price. quantity: Order quantity. order_type: Order type, `MARKET` or `LIMIT`. client_oid: Client order id, default is `None`. Returns: success: Success results, otherwise it's None. error: Error information, otherwise it's None. """ uri = "/api/spot/v3/orders" data = { "side": "buy" if action == ORDER_ACTION_BUY else "sell", "instrument_id": symbol, "margin_trading": 1 } if order_type == ORDER_TYPE_LIMIT: data["type"] = "limit" data["price"] = price data["size"] = quantity elif order_type == ORDER_TYPE_MARKET: data["type"] = "market" if action == ORDER_ACTION_BUY: data["notional"] = quantity # buy price. else: data["size"] = quantity # sell quantity. else: logger.error("order_type error! order_type:", order_type, caller=self) return None, "order type error!" if client_oid: data["client_oid"] = client_oid result, error = await self.request("POST", uri, body=data, auth=True) return result, error
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 _receive(self): """Receive stream message from Websocket connection.""" async for msg in self.ws: if msg.type == aiohttp.WSMsgType.TEXT: if self._process_callback: try: data = json.loads(msg.data) except: data = msg.data SingleTask.run(self._process_callback, data) elif msg.type == aiohttp.WSMsgType.BINARY: if self._process_binary_callback: SingleTask.run(self._process_binary_callback, msg.data) elif msg.type == aiohttp.WSMsgType.CLOSED: logger.warn("receive event CLOSED:", msg, caller=self) SingleTask.run(self.reconnect) elif msg.type == aiohttp.WSMsgType.ERROR: logger.error("receive event ERROR:", msg, caller=self) else: logger.warn("unhandled msg:", msg, caller=self)
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
async def send(self, data) -> bool: """ Send message to Websocket server. Args: data: Message content, must be dict or string. Returns: If send successfully, return True, otherwise return False. """ if not self.ws: logger.warn("Websocket connection not connected yet!", caller=self) return False if isinstance(data, dict): await self.ws.send_json(data) elif isinstance(data, str): await self.ws.send_str(data) else: logger.error("send message failed:", data, caller=self) return False logger.debug("send message:", data, caller=self) return True
def __init__(self) -> None: """ '吃盘口毛刺'的简化策略: 根据实时盘口变化取卖6和卖8的价格,并根据他们的平均价格来挂卖单 即, average_price = (ask6_price + ask8_price) / 2 取指定位数的数据, EOS是4位; 在策略启动的时候判断是否有挂单, 如果有挂单, 判断价格是否已经超过 ask6_price 和 ask8_price 的区间, 如果超过那么撤单后再重新挂单. """ self._symbol = "EOSUSDT" self._action = "SELL" self._quantity = "2.5" self._order_id = "" self._price = 0.0 self._is_ok = False params = dict( strategy=config.strategy_name, platform=config.platform, symbol=config.symbol, account=config.account, # 分布式...多个 access_key=config.access_key, secret_key=config.secret_key, passphrase=config.passphrase, order_update_callback=self.on_order_update_callback, init_callback=self.on_init_callback, error_callback=self.on_error_callback, ) self._trade = Trade(**params) if config.platform == BINANCE: LoopRunTask.register(self.dynamic_trade_with_binance, interval=2) elif config.platform == HUOBI: LoopRunTask.register(self.dynamic_trade_with_huobi, interval=2) elif config.platform == OKEX: LoopRunTask.register(self.dynamic_trade_with_okex, interval=2) else: logger.error("platform error:", config.platform, caller=self) quant.stop()
def __init__(self, market_type, platform, symbol, callback): """Initialize.""" if platform == "#" or symbol == "#": multi = True else: multi = False if market_type == const.MARKET_TYPE_ORDERBOOK: from aioquant.event import EventOrderbook EventOrderbook(Orderbook(platform, symbol)).subscribe(callback, multi) elif market_type == const.MARKET_TYPE_TRADE: from aioquant.event import EventTrade EventTrade(Trade(platform, symbol)).subscribe(callback, multi) elif market_type in [ const.MARKET_TYPE_KLINE, const.MARKET_TYPE_KLINE_3M, const.MARKET_TYPE_KLINE_5M, const.MARKET_TYPE_KLINE_15M, const.MARKET_TYPE_KLINE_30M, const.MARKET_TYPE_KLINE_1H, const.MARKET_TYPE_KLINE_3H, const.MARKET_TYPE_KLINE_6H, const.MARKET_TYPE_KLINE_12H, const.MARKET_TYPE_KLINE_1D, const.MARKET_TYPE_KLINE_3D, const.MARKET_TYPE_KLINE_1W, const.MARKET_TYPE_KLINE_15D, const.MARKET_TYPE_KLINE_1MON, const.MARKET_TYPE_KLINE_1Y]: from aioquant.event import EventKline EventKline(Kline(platform, symbol, kline_type=market_type)).subscribe(callback, multi) else: logger.error("market_type error:", market_type, caller=self)
async def connect(self, reconnect=False): """Connect to RabbitMQ server and create default exchange. Args: reconnect: If this invoke is a re-connection ? """ logger.info("host:", self._host, "port:", self._port, caller=self) if self._connected: return # Create a connection. try: transport, protocol = await aioamqp.connect(host=self._host, port=self._port, login=self._username, password=self._password, login_method="PLAIN") except Exception as e: logger.error("connection error:", e, caller=self) return finally: if self._connected: return channel = await protocol.channel() self._protocol = protocol self._channel = channel self._connected = True logger.info("Rabbitmq initialize success!", caller=self) # Create default exchanges. exchanges = ["Orderbook", "Kline", "Trade"] for name in exchanges: await self._channel.exchange_declare(exchange_name=name, type_name="topic") logger.debug("create default exchanges success!", caller=self) if reconnect: self._bind_and_consume() else: # Maybe we should waiting for all modules to be initialized successfully. asyncio.get_event_loop().call_later(5, self._bind_and_consume)
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)
async def fetch(cls, method, url, params=None, body=None, data=None, headers=None, timeout=30, **kwargs): """ Create a HTTP request. Args: method: HTTP request method. `GET` / `POST` / `PUT` / `DELETE` url: Request url. params: HTTP query params. body: HTTP request body, string or bytes format. data: HTTP request body, dict format. headers: HTTP request header. timeout: HTTP request timeout(seconds), default is 30s. kwargs: proxy: HTTP proxy. Return: code: HTTP response code. success: HTTP response data. If something wrong, this field is None. error: If something wrong, this field will holding a Error information, otherwise it's None. Raises: HTTP request exceptions or response data parse exceptions. All the exceptions will be captured and return Error information. """ session = cls._get_session(url) if not kwargs.get("proxy"): kwargs[ "proxy"] = config.proxy # If there is a `HTTP PROXY` Configuration in config file? try: if method == "GET": response = await session.get(url, params=params, headers=headers, timeout=timeout, **kwargs) elif method == "POST": response = await session.post(url, params=params, data=body, json=data, headers=headers, timeout=timeout, **kwargs) elif method == "PUT": response = await session.put(url, params=params, data=body, json=data, headers=headers, timeout=timeout, **kwargs) elif method == "DELETE": response = await session.delete(url, params=params, data=body, json=data, headers=headers, timeout=timeout, **kwargs) else: error = "http method error!" return None, None, error except Exception as e: logger.error("method:", method, "url:", url, "headers:", headers, "params:", params, "body:", body, "data:", data, "Error:", e, caller=cls) return None, None, e code = response.status if code not in (200, 201, 202, 203, 204, 205, 206): text = await response.text() logger.error("method:", method, "url:", url, "headers:", headers, "params:", params, "body:", body, "data:", data, "code:", code, "result:", text, caller=cls) return code, None, text try: result = await response.json() except: result = await response.text() logger.debug("response data is not json format!", "method:", method, "url:", url, "headers:", headers, "params:", params, "body:", body, "data:", data, "code:", code, "result:", result, caller=cls) logger.debug("method:", method, "url:", url, "headers:", headers, "params:", params, "body:", body, "data:", data, "code:", code, "result:", json.dumps(result), caller=cls) return code, result, None
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)