async def listen_for_order_book_diffs(self, ev_loop: asyncio.BaseEventLoop, output: asyncio.Queue): while True: try: trading_pairs: List[str] = self._trading_pairs async with websockets.connect(Constants.EXCHANGE_WS_URI) as ws: ws: websockets.WebSocketClientProtocol = ws for trading_pair in trading_pairs: subscribe_request: Dict[str, Any] = { "event": "subscribe", "streams": [ stream.format(trading_pair= convert_to_exchange_trading_pair( trading_pair)) for stream in Constants.WS_OB_SUBSCRIBE_STREAMS ] } await ws.send(json.dumps(subscribe_request)) async for raw_msg in self._inner_messages(ws): # Altmarkets's data value for id is a large int too big for ujson to parse msg: Dict[str, Any] = json.loads(raw_msg) if "ping" in raw_msg: await ws.send( f'{{"op":"pong","timestamp": {str(msg["ping"])}}}' ) elif "subscribed" in raw_msg: pass elif ".ob-inc" in raw_msg: # msg_key = list(msg.keys())[0] trading_pair = list(msg.keys())[0].split(".")[0] order_book_message: OrderBookMessage = AltmarketsOrderBook.diff_message_from_exchange( msg[f"{trading_pair}.ob-inc"], metadata={"trading_pair": trading_pair}) output.put_nowait(order_book_message) elif ".ob-snap" in raw_msg: # msg_key = list(msg.keys())[0] trading_pair = list(msg.keys())[0].split(".")[0] order_book_message: OrderBookMessage = AltmarketsOrderBook.snapshot_message_from_exchange( msg[f"{trading_pair}.ob-snap"], metadata={"trading_pair": trading_pair}) output.put_nowait(order_book_message) else: # Debug log output for pub WS messages self.logger().info( f"OB: Unrecognized message received from Altmarkets websocket: {msg}" ) except asyncio.CancelledError: raise except Exception: self.logger().error( "Unexpected error with WebSocket connection. Retrying after 30 seconds...", exc_info=True) await asyncio.sleep(Constants.MESSAGE_TIMEOUT)
def setUpClass(cls) -> None: super().setUpClass() cls.ev_loop = asyncio.get_event_loop() cls.base_asset = "HBOT" cls.quote_asset = "USDT" cls.trading_pair = f"{cls.base_asset}-{cls.quote_asset}" cls.exchange_trading_pair = convert_to_exchange_trading_pair( cls.trading_pair) cls.api_key = "testKey" cls.api_secret_key = "testSecretKey" cls.username = "******" cls.throttler = AsyncThrottler(Constants.RATE_LIMITS)
async def get_last_traded_prices( cls, trading_pairs: List[str]) -> Dict[str, float]: results = dict() # Altmarkets rate limit is 100 https requests per 10 seconds random.seed() randSleep = (random.randint(1, 9) + random.randint(1, 9)) / 10 await asyncio.sleep(0.5 + randSleep) async with aiohttp.ClientSession() as client: resp = await client.get(Constants.EXCHANGE_ROOT_API + Constants.TICKER_URI) resp_json = await resp.json() for trading_pair in trading_pairs: resp_record = [ resp_json[symbol] for symbol in list(resp_json.keys()) if symbol == convert_to_exchange_trading_pair(trading_pair) ][0]['ticker'] results[trading_pair] = float(resp_record["last"]) return results
async def get_snapshot(client: aiohttp.ClientSession, trading_pair: str) -> Dict[str, Any]: # when type is set to "step0", the default value of "depth" is 150 # params: Dict = {"symbol": trading_pair, "type": "step0"} # Altmarkets rate limit is 100 https requests per 10 seconds random.seed() randSleep = (random.randint(1, 9) + random.randint(1, 9)) / 10 await asyncio.sleep(0.5 + randSleep) async with client.get( Constants.EXCHANGE_ROOT_API + Constants.DEPTH_URI.format( trading_pair=convert_to_exchange_trading_pair( trading_pair))) as response: response: aiohttp.ClientResponse = response if response.status != 200: raise IOError( f"Error fetching Altmarkets market snapshot for {trading_pair}. " f"HTTP status is {response.status}.") api_data = await response.read() data: Dict[str, Any] = json.loads(api_data) return data
async def _create_order(self, trade_type: TradeType, order_id: str, trading_pair: str, amount: Decimal, order_type: OrderType, price: Decimal): """ Calls create-order API end point to place an order, starts tracking the order and triggers order created event. :param trade_type: BUY or SELL :param order_id: Internal order id (also called client_order_id) :param trading_pair: The market to place order :param amount: The order amount (in base token value) :param order_type: The order type :param price: The order price """ trading_rule = self._trading_rules[trading_pair] try: amount = self.quantize_order_amount(trading_pair, amount) price = self.quantize_order_price( trading_pair, s_decimal_0 if math.isnan(price) else price) if amount < trading_rule.min_order_size: raise ValueError( f"Buy order amount {amount} is lower than the minimum order size " f"{trading_rule.min_order_size}.") order_type_str = order_type.name.lower().split("_")[0] api_params = { "market": convert_to_exchange_trading_pair(trading_pair), "side": trade_type.name.lower(), "ord_type": order_type_str, # "price": f"{price:f}", "client_id": order_id, "volume": f"{amount:f}", } if order_type is not OrderType.MARKET: api_params['price'] = f"{price:f}" # if order_type is OrderType.LIMIT_MAKER: # api_params["postOnly"] = "true" self.start_tracking_order(order_id, None, trading_pair, trade_type, price, amount, order_type) order_result = await self._api_request( "POST", Constants.ENDPOINT["ORDER_CREATE"], params=api_params, is_auth_required=True, limit_id=Constants.RL_ID_ORDER_CREATE, disable_retries=True) exchange_order_id = str(order_result["id"]) tracked_order = self._in_flight_orders.get(order_id) if tracked_order is not None: self.logger().info( f"Created {order_type.name} {trade_type.name} order {order_id} for " f"{amount} {trading_pair}.") tracked_order.update_exchange_order_id(exchange_order_id) else: raise Exception('Order not tracked.') if trade_type is TradeType.BUY: event_tag = MarketEvent.BuyOrderCreated event_cls = BuyOrderCreatedEvent else: event_tag = MarketEvent.SellOrderCreated event_cls = SellOrderCreatedEvent self.trigger_event( event_tag, event_cls(self.current_timestamp, order_type, trading_pair, amount, price, order_id, exchange_order_id)) except asyncio.CancelledError: raise except Exception as e: if isinstance(e, AltmarketsAPIError): error_reason = e.error_payload.get('error', {}).get( 'message', e.error_payload.get('errors')) else: error_reason = e if error_reason and "upstream connect error" not in str( error_reason): self.stop_tracking_order(order_id) self.trigger_event( MarketEvent.OrderFailure, MarketOrderFailureEvent(self.current_timestamp, order_id, order_type)) else: self._order_not_created_records[order_id] = 0 self.logger().network( f"Error submitting {trade_type.name} {order_type.name} order to {Constants.EXCHANGE_NAME} for " f"{amount} {trading_pair} {price} - {error_reason}.", exc_info=True, app_warning_msg= (f"Error submitting order to {Constants.EXCHANGE_NAME} - {error_reason}." ))