예제 #1
0
    def _format_trading_rules(self, exchange_info_dict: Dict[str, Any]) -> List[TradingRule]:
        rules: list = exchange_info_dict.get("symbols", [])
        return_val: list = []
        for rule in rules:
            try:
                trading_pair = convert_from_exchange_trading_pair(rule["symbol"])
                filters = rule["filters"]
                filt_dict = {fil["filterType"]: fil for fil in filters}

                min_order_size = Decimal(filt_dict.get("LOT_SIZE").get("minQty"))
                step_size = Decimal(filt_dict.get("LOT_SIZE").get("stepSize"))
                tick_size = Decimal(filt_dict.get("PRICE_FILTER").get("tickSize"))

                # TODO: BINANCE PERPETUALS DOES NOT HAVE A MIN NOTIONAL VALUE, NEED TO CREATE NEW DERIVATIVES INFRASTRUCTURE
                # min_notional = 0

                return_val.append(
                    TradingRule(trading_pair,
                                min_order_size=min_order_size,
                                min_price_increment=Decimal(tick_size),
                                min_base_amount_increment=Decimal(step_size),
                                # min_notional_size=Decimal(min_notional))
                                )
                )
            except Exception as e:
                self.logger().error(f"Error parsing the trading pair rule {rule}. Error: {e}. Skipping...",
                                    exc_info=True)
        return return_val
예제 #2
0
 async def fetch_trading_pairs(domain=None) -> List[str]:
     try:
         from hummingbot.connector.derivative.binance_perpetual.binance_perpetual_utils import convert_from_exchange_trading_pair
         BASE_URL = TESTNET_BASE_URL if domain == "binance_perpetual_testnet" else PERPETUAL_BASE_URL
         async with aiohttp.ClientSession() as client:
             async with client.get(EXCHANGE_INFO_URL.format(BASE_URL),
                                   timeout=10) as response:
                 if response.status == 200:
                     data = await response.json()
                     raw_trading_pairs = [
                         d["symbol"] for d in data["symbols"]
                         if d["status"] == "TRADING"
                     ]
                     trading_pair_list: List[str] = []
                     for raw_trading_pair in raw_trading_pairs:
                         try:
                             trading_pair = convert_from_exchange_trading_pair(
                                 raw_trading_pair)
                             if trading_pair is not None:
                                 trading_pair_list.append(trading_pair)
                             else:
                                 continue
                         except Exception:
                             pass
                     return trading_pair_list
     except Exception:
         pass
     return []
 async def fetch_trading_pairs(
         domain: str = CONSTANTS.DOMAIN,
         throttler: Optional[AsyncThrottler] = None) -> List[str]:
     url = utils.rest_url(path_url=CONSTANTS.EXCHANGE_INFO_URL,
                          domain=domain)
     throttler = throttler or BinancePerpetualAPIOrderBookDataSource._get_throttler_instance(
     )
     async with throttler.execute_task(
             limit_id=CONSTANTS.EXCHANGE_INFO_URL):
         async with aiohttp.ClientSession() as client:
             async with client.get(url=url, timeout=10) as response:
                 if response.status == 200:
                     data = await response.json()
                     # fetch d["pair"] for binance perpetual
                     raw_trading_pairs = [
                         d["pair"] for d in data["symbols"]
                         if d["status"] == "TRADING"
                     ]
                     trading_pair_targets = [
                         f"{d['baseAsset']}-{d['quoteAsset']}"
                         for d in data["symbols"]
                         if d["status"] == "TRADING"
                     ]
                     trading_pair_list: List[str] = []
                     for raw_trading_pair, pair_target in zip(
                             raw_trading_pairs, trading_pair_targets):
                         trading_pair = utils.convert_from_exchange_trading_pair(
                             raw_trading_pair)
                         if trading_pair is not None and trading_pair == pair_target:
                             trading_pair_list.append(trading_pair)
                     return trading_pair_list
     return []
예제 #4
0
 async def _update_positions(self):
     # local_position_names = set(self._account_positions.keys())
     # remote_position_names = set()
     positions = await self.request(path="/fapi/v2/positionRisk", add_timestamp=True, is_signed=True)
     for position in positions:
         trading_pair = position.get("symbol")
         position_side = PositionSide[position.get("positionSide")]
         unrealized_pnl = Decimal(position.get("unRealizedProfit"))
         entry_price = Decimal(position.get("entryPrice"))
         amount = Decimal(position.get("positionAmt"))
         leverage = Decimal(position.get("leverage"))
         if amount != 0:
             self._account_positions[trading_pair + position_side.name] = Position(
                 trading_pair=convert_from_exchange_trading_pair(trading_pair),
                 position_side=position_side,
                 unrealized_pnl=unrealized_pnl,
                 entry_price=entry_price,
                 amount=amount,
                 leverage=leverage
             )
         else:
             if (trading_pair + position_side.name) in self._account_positions:
                 del self._account_positions[trading_pair + position_side.name]
예제 #5
0
    async def _user_stream_event_listener(self):
        async for event_message in self._iter_user_event_queue():
            try:
                event_type = event_message.get("e")
                if event_type == "ORDER_TRADE_UPDATE":
                    order_message = event_message.get("o")
                    client_order_id = order_message.get("c")

                    # If the order has already been cancelled
                    if client_order_id not in self._in_flight_orders:
                        continue

                    tracked_order = self._in_flight_orders.get(client_order_id)
                    tracked_order.update_with_execution_report(event_message)

                    # Execution Type: Trade => Filled
                    trade_type = TradeType.BUY if order_message.get("S") == "BUY" else TradeType.SELL
                    if order_message.get("X") in ["PARTIALLY_FILLED", "FILLED"]:
                        order_filled_event = OrderFilledEvent(
                            timestamp=event_message.get("E") * 1e-3,
                            order_id=client_order_id,
                            trading_pair=convert_from_exchange_trading_pair(order_message.get("s")),
                            trade_type=trade_type,
                            order_type=OrderType.LIMIT if order_message.get("o") == "LIMIT" else OrderType.MARKET,
                            price=Decimal(order_message.get("L")),
                            amount=Decimal(order_message.get("l")),
                            trade_fee=self.get_fee(
                                base_currency=tracked_order.base_asset,
                                quote_currency=tracked_order.quote_asset,
                                order_type=tracked_order.order_type,
                                order_side=trade_type,
                                amount=Decimal(order_message.get("q")),
                                price=Decimal(order_message.get("p"))
                            ),
                            exchange_trade_id=order_message.get("t")
                        )
                        self.trigger_event(self.MARKET_ORDER_FILLED_EVENT_TAG, order_filled_event)

                    if tracked_order.is_done:
                        if not tracked_order.is_failure:
                            event_tag = None
                            event_class = None
                            if trade_type is TradeType.BUY:
                                event_tag = self.MARKET_BUY_ORDER_COMPLETED_EVENT_TAG
                                event_class = BuyOrderCompletedEvent
                            else:
                                event_tag = self.MARKET_SELL_ORDER_COMPLETED_EVENT_TAG
                                event_class = SellOrderCompletedEvent
                            self.logger().info(f"The {tracked_order.order_type.name.lower()} {trade_type} order {client_order_id} has completed "
                                               f"according to websocket delta.")
                            self.trigger_event(event_tag,
                                               event_class(self.current_timestamp,
                                                           client_order_id,
                                                           tracked_order.base_asset,
                                                           tracked_order.quote_asset,
                                                           (tracked_order.fee_asset or tracked_order.quote_asset),
                                                           tracked_order.executed_amount_base,
                                                           tracked_order.executed_amount_quote,
                                                           tracked_order.fee_paid,
                                                           tracked_order.order_type))
                        else:
                            if tracked_order.is_cancelled:
                                if tracked_order.client_order_id in self._in_flight_orders:
                                    self.logger().info(f"Successfully cancelled order {tracked_order.client_order_id} according to websocket delta.")
                                    self.trigger_event(self.MARKET_ORDER_CANCELLED_EVENT_TAG,
                                                       OrderCancelledEvent(self.current_timestamp,
                                                                           tracked_order.client_order_id))
                                else:
                                    self.logger().info(f"The {tracked_order.order_type.name.lower()} order {tracked_order.client_order_id} has failed "
                                                       f"according to websocket delta.")
                                    self.trigger_event(self.MARKET_ORDER_FAILURE_EVENT_TAG,
                                                       MarketOrderFailureEvent(self.current_timestamp,
                                                                               tracked_order.client_order_id,
                                                                               tracked_order.order_type))
                        self.stop_tracking_order(tracked_order.client_order_id)
                elif event_type == "ACCOUNT_UPDATE":
                    update_data = event_message.get("a", {})
                    # update balances
                    for asset in update_data.get("B", []):
                        asset_name = asset["a"]
                        self._account_balances[asset_name] = Decimal(asset["wb"])
                        self._account_available_balances[asset_name] = Decimal(asset["cw"])

                    # update position
                    for asset in update_data.get("P", []):
                        position = self._account_positions.get(f"{asset['s']}{asset['ps']}", None)
                        if position is not None:
                            position.update_position(position_side=PositionSide[asset["ps"]],
                                                     unrealized_pnl = Decimal(asset["up"]),
                                                     entry_price = Decimal(asset["ep"]),
                                                     amount = Decimal(asset["pa"]))
                        else:
                            await self._update_positions()
                elif event_type == "MARGIN_CALL":
                    positions = event_message.get("p", [])
                    total_maint_margin_required = 0
                    # total_pnl = 0
                    negative_pnls_msg = ""
                    for position in positions:
                        existing_position = self._account_positions.get(f"{asset['s']}{asset['ps']}", None)
                        if existing_position is not None:
                            existing_position.update_position(position_side=PositionSide[asset["ps"]],
                                                              unrealized_pnl = Decimal(asset["up"]),
                                                              amount = Decimal(asset["pa"]))
                        total_maint_margin_required += position.get("mm", 0)
                        if position.get("up", 0) < 1:
                            negative_pnls_msg += f"{position.get('s')}: {position.get('up')}, "
                    self.logger().warning("Margin Call: Your position risk is too high, and you are at risk of "
                                          "liquidation. Close your positions or add additional margin to your wallet.")
                    self.logger().info(f"Margin Required: {total_maint_margin_required}. Total Unrealized PnL: "
                                       f"{negative_pnls_msg}. Negative PnL assets: {negative_pnls_msg}.")
            except asyncio.CancelledError:
                raise
            except Exception as e:
                self.logger().error(f"Unexpected error in user stream listener loop: {e}", exc_info=True)
                await asyncio.sleep(5.0)
 def test_parse_three_letters_base_and_four_letters_quote(self):
     parsed_pair = utils.convert_from_exchange_trading_pair("BTCUSDT")
     self.assertEqual(parsed_pair, "BTC-USDT")
 def test_parse_three_letters_base_and_three_letters_quote_matching_with_a_four_letters_quote_candidate(self):
     parsed_pair = utils.convert_from_exchange_trading_pair("VETUSD")
     self.assertEqual(parsed_pair, "VET-USD")