def from_dict(values: Dict[str, Any], quantity_key: str, price_key: str = 'price', price: Decimal = None, futures: bool = False) -> 'Order': if price is None: price = parse_decimal(values[price_key]) quantity = parse_decimal(values[quantity_key]) order_list_id = values['orderListId'] if values.get('orderListId', -1) != -1 else None return Order(values['symbol'], values['side'], values['type'], values['status'], values['orderId'], order_list_id, quantity, price, futures)
def process_api_spot_message(self, msg: dict) -> None: if msg['e'] == 'executionReport': order_list_id = msg['g'] if msg['g'] != -1 else None order = Order(symbol=msg['s'], side=msg['S'], order_type=msg['o'], status=msg['X'], order_id=msg['i'], order_list_id=order_list_id, quantity=parse_decimal(msg['l']), price=parse_decimal(msg['L'])) self._process_api_order(order)
def process_api_futures_message(self, message: dict) -> None: if message['data']['e'] == 'ORDER_TRADE_UPDATE': msg = message['data']['o'] order = Order(symbol=msg['s'], side=msg['S'], order_type=msg['o'], original_type=msg['ot'], status=msg['X'], order_id=msg['i'], order_list_id=None, quantity=parse_decimal(msg['l']), price=parse_decimal(msg['L']), futures=True) self._process_api_order(order)
def _check_is_empty(self, symbol: str) -> None: positions = self._client.futures_position_information(symbol=symbol) assert len(positions) == 1 assert parse_decimal(positions[0]['positionAmt']) == Decimal( 0), f'{symbol} has open future position' assert len(self._client.futures_get_open_orders( symbol=symbol)) == 0, f'{symbol} has open future order'
def _get_last_buy_order(self, symbol: str) -> Optional[Order]: api_orders = self._client.get_all_orders(symbol=symbol) api_orders.sort(key=lambda o: o['updateTime'], reverse=True) for info in api_orders: if info['side'] == Order.SIDE_BUY and info[ 'status'] == Order.STATUS_FILLED: # price is zero in original response price = parse_decimal( info['cummulativeQuoteQty']) / parse_decimal( info['executedQty']) return Order.from_dict(info, price=price, quantity_key='executedQty') else: return None
def market_sell(self, symbol: str, quantity: Decimal) -> Order: info = self._client.order_market_sell( symbol=symbol, quantity=quantity, ) if info['status'] != Order.STATUS_FILLED: sleep(1) info = self._client.get_order(symbol=info['symbol'], orderId=info['orderId']) # price is zero in original response price = parse_decimal(info['cummulativeQuoteQty']) / parse_decimal( info['executedQty']) order = Order.from_dict(info, price=price, quantity_key='executedQty') assert order.status == Order.STATUS_FILLED, f'Got {order.status}' return order
def get_sell_order_pnl(self, sell_order: Order) -> Optional[Decimal]: assert sell_order.side == Order.SIDE_SELL assert sell_order.status == Order.STATUS_FILLED trades = self._client.futures_account_trades(symbol=sell_order.symbol) pln = [ parse_decimal(info['realizedPnl']) for info in trades if info['orderId'] == sell_order.order_id ] return pln[0] if len(pln) != 0 else None
def _symbol_infos(self) -> Dict[str, SymbolInfo]: if len(self.__symbol_infos) == 0: all_info = self._client.futures_exchange_info() for info in all_info['symbols']: symbol = info['symbol'] min_notional = [ parse_decimal(f['notional']) for f in info['filters'] if f['filterType'] == 'MIN_NOTIONAL' ][0] self.__symbol_infos[symbol] = SymbolInfo( int(info['quantityPrecision']), int(info['pricePrecision']), min_notional) return self.__symbol_infos
def get_symbol_info(self, symbol: str) -> SymbolInfo: if symbol not in self._symbol_infos: info = self._client.get_symbol_info(symbol=symbol) quantity_precision, price_precision, min_notional = None, None, None def parse(key: str) -> int: return int(round(-math.log(Decimal(f[key]), 10), 0)) for f in info['filters']: if f['filterType'] == 'LOT_SIZE': quantity_precision = parse('stepSize') elif f['filterType'] == 'PRICE_FILTER': price_precision = parse('tickSize') elif f['filterType'] == 'MIN_NOTIONAL': min_notional = parse_decimal(f['minNotional']) assert quantity_precision is not None and price_precision is not None and min_notional is not None self._symbol_infos[symbol] = SymbolInfo(quantity_precision, price_precision, min_notional) return self._symbol_infos[symbol]
def get_current_price(self, symbol: str) -> Decimal: info = self._client.get_symbol_ticker(symbol=symbol) return parse_decimal(info['price'])
def get_open_position_quantity(self, symbol: str) -> Decimal: info = self._client.futures_position_information(symbol=symbol) assert len(info) == 1 return parse_decimal(info[0]['positionAmt'])