From b75026fcdc837b27d76849e08f7466cbd0b1483e Mon Sep 17 00:00:00 2001 From: Petio Petrov Date: Wed, 3 May 2023 10:38:11 +0200 Subject: [PATCH 1/3] (feat) Adds request rate limit throttling to Injective Perpetual --- .../injective_perpetual_api_data_source.py | 96 ++++++++----- .../injective_perpetual_constants.py | 134 ++++++++++++++++++ 2 files changed, 193 insertions(+), 37 deletions(-) diff --git a/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_api_data_source.py b/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_api_data_source.py index 3e6a67737f..61a743b0f0 100644 --- a/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_api_data_source.py +++ b/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_api_data_source.py @@ -53,6 +53,7 @@ from hummingbot.connector.gateway.gateway_in_flight_order import GatewayInFlightOrder from hummingbot.connector.trading_rule import TradingRule from hummingbot.connector.utils import combine_to_hb_trading_pair, split_hb_trading_pair +from hummingbot.core.api_throttler.async_throttler import AsyncThrottler from hummingbot.core.data_type.common import OrderType, PositionAction, PositionMode, PositionSide, TradeType from hummingbot.core.data_type.funding_info import FundingInfo, FundingInfoUpdate from hummingbot.core.data_type.in_flight_order import InFlightOrder, OrderState, OrderUpdate, TradeUpdate @@ -89,6 +90,7 @@ def __init__( self._network_obj = CONSTANTS.NETWORK_CONFIG[self._network] self._client = AsyncClient(network=self._network_obj) self._account_address: Optional[str] = None + self._throttler = AsyncThrottler(rate_limits=CONSTANTS.RATE_LIMITS) self._composer = Composer(network=self._network_obj.string()) self._order_hash_manager: Optional[OrderHashManager] = None @@ -171,7 +173,8 @@ async def stop(self): async def check_network_status(self) -> NetworkStatus: status = NetworkStatus.CONNECTED try: - await self._client.ping() + async with self._throttler.execute_task(limit_id=CONSTANTS.PING_LIMIT_ID): + await self._client.ping() await self._get_gateway_instance().ping_gateway() except asyncio.CancelledError: raise @@ -188,9 +191,10 @@ async def set_trading_pair_leverage(self, trading_pair: str, leverage: int) -> T async def get_order_book_snapshot(self, trading_pair: str) -> OrderBookMessage: market_info = self._markets_info[trading_pair] price_scaler: Decimal = Decimal(f"1e-{market_info.quote_token_meta.decimals}") - response: OrderbooksV2Response = await self._client.get_derivative_orderbooksV2( - market_ids=[market_info.market_id] - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.ORDER_BOOK_LIMIT_ID): + response: OrderbooksV2Response = await self._client.get_derivative_orderbooksV2( + market_ids=[market_info.market_id] + ) snapshot_ob: DerivativeLimitOrderbookV2 = response.orderbooks[0].orderbook snapshot_timestamp_ms: float = max( @@ -455,9 +459,10 @@ async def batch_order_cancel(self, orders_to_cancel: List[InFlightOrder]) -> Lis async def fetch_positions(self) -> List[Position]: market_ids = self._get_market_ids() - backend_positions: PositionsResponse = await self._client.get_derivative_positions( - market_ids=market_ids, subaccount_id=self._account_id - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.POSITIONS_LIMIT_ID): + backend_positions: PositionsResponse = await self._client.get_derivative_positions( + market_ids=market_ids, subaccount_id=self._account_id + ) positions = [ self._parse_backed_position_to_position(backend_position=backed_position) @@ -477,9 +482,10 @@ async def get_account_balances(self) -> Dict[str, Dict[str, Decimal]]: await self._update_account_address_and_create_order_hash_manager() self._check_markets_initialized() or await self._update_markets() - portfolio_response: AccountPortfolioResponse = await self._client.get_account_portfolio( - account_address=self._account_address - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.ACCOUNT_PORTFOLIO_LIMIT_ID): + portfolio_response: AccountPortfolioResponse = await self._client.get_account_portfolio( + account_address=self._account_address + ) portfolio: Portfolio = portfolio_response.portfolio bank_balances: List[Coin] = portfolio.bank_balances @@ -546,9 +552,10 @@ async def fetch_last_fee_payment(self, trading_pair: str) -> Tuple[float, Decima if trading_pair not in self._markets_info: return timestamp, funding_rate, payment - response: FundingPaymentsResponse = await self._client.get_funding_payments( - subaccount_id=self._account_id, market_id=self._markets_info[trading_pair].market_id, limit=1 - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.FUNDING_PAYMENT_LIMIT_ID): + response: FundingPaymentsResponse = await self._client.get_funding_payments( + subaccount_id=self._account_id, market_id=self._markets_info[trading_pair].market_id, limit=1 + ) if len(response.payments) != 0: latest_funding_payment: FundingPayment = response.payments[0] # List of payments sorted by latest @@ -570,8 +577,10 @@ async def _update_account_address_and_create_order_hash_manager(self): ) self._account_address: str = response["injectiveAddress"] - await self._client.get_account(address=self._account_address) - await self._client.sync_timeout_height() + async with self._throttler.execute_task(limit_id=CONSTANTS.ACCOUNT_LIMIT_ID): + await self._client.get_account(address=self._account_address) + async with self._throttler.execute_task(limit_id=CONSTANTS.SYNC_TIMEOUT_HEIGHT_LIMIT_ID): + await self._client.sync_timeout_height() tasks_to_await_submitted_orders_to_be_processed_by_chain = [ asyncio.wait_for(order.wait_until_processed_by_exchange(), timeout=CONSTANTS.ORDER_CHAIN_PROCESSING_TIMEOUT) for order in self._gateway_order_tracker.active_orders.values() @@ -612,11 +621,15 @@ async def _update_markets(self): async def _fetch_derivative_markets(self) -> MarketsResponse: market_status: str = "active" - return await self._client.get_derivative_markets(market_status=market_status) + async with self._throttler.execute_task(limit_id=CONSTANTS.DERIVATIVE_MARKETS_LIMIT_ID): + derivative_markets = await self._client.get_derivative_markets(market_status=market_status) + return derivative_markets async def _fetch_spot_markets(self) -> MarketsResponse: market_status: str = "active" - return await self._client.get_spot_markets(market_status=market_status) + async with self._throttler.execute_task(limit_id=CONSTANTS.SPOT_MARKETS_LIMIT_ID): + spot_markets = await self._client.get_spot_markets(market_status=market_status) + return spot_markets def _update_market_map_attributes(self, markets: MarketsResponse): """Parses MarketsResponse and re-populate the market map attributes""" @@ -684,15 +697,16 @@ async def _fetch_order_history(self, order: GatewayInFlightOrder) -> Optional[De skip = 0 search_completed = False while not search_completed: - response: OrdersHistoryResponse = await self._client.get_historical_derivative_orders( - market_id=market.market_id, - subaccount_id=self._account_id, - direction=direction, - start_time=int(order.creation_timestamp * 1e3), - limit=CONSTANTS.FETCH_ORDER_HISTORY_LIMIT, - skip=skip, - order_types=[CONSTANTS.CLIENT_TO_BACKEND_ORDER_TYPES_MAP[(trade_type, order_type)]], - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.HISTORICAL_DERIVATIVE_ORDERS_LIMIT_ID): + response: OrdersHistoryResponse = await self._client.get_historical_derivative_orders( + market_id=market.market_id, + subaccount_id=self._account_id, + direction=direction, + start_time=int(order.creation_timestamp * 1e3), + limit=CONSTANTS.FETCH_ORDER_HISTORY_LIMIT, + skip=skip, + order_types=[CONSTANTS.CLIENT_TO_BACKEND_ORDER_TYPES_MAP[(trade_type, order_type)]], + ) if len(response.orders) == 0: search_completed = True else: @@ -717,13 +731,14 @@ async def _fetch_order_fills(self, order: InFlightOrder) -> List[DerivativeTrade direction: str = "buy" if order.trade_type == TradeType.BUY else "sell" while not search_completed: - trades = await self._client.get_derivative_trades( - market_id=market_id, - subaccount_id=self._account_id, - direction=direction, - skip=skip, - start_time=int(order.creation_timestamp * 1e3), - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.DERIVATIVE_TRADES_LIMIT_ID): + trades = await self._client.get_derivative_trades( + market_id=market_id, + subaccount_id=self._account_id, + direction=direction, + skip=skip, + start_time=int(order.creation_timestamp * 1e3), + ) if len(trades.trades) == 0: search_completed = True else: @@ -733,7 +748,9 @@ async def _fetch_order_fills(self, order: InFlightOrder) -> List[DerivativeTrade return all_trades async def _fetch_transaction_by_hash(self, transaction_hash: str) -> GetTxByTxHashResponse: - return await self._client.get_tx_by_hash(tx_hash=transaction_hash) + async with self._throttler.execute_task(limit_id=CONSTANTS.TRANSACTION_BY_HASH_LIMIT_ID): + transaction = await self._client.get_tx_by_hash(tx_hash=transaction_hash) + return transaction def _update_local_balances(self, balances: Dict[str, Dict[str, Decimal]]): # We need to keep local copy of total and available balance so we can trigger BalanceUpdateEvent with correct @@ -748,7 +765,10 @@ def _update_local_balances(self, balances: Dict[str, Dict[str, Decimal]]): async def _request_last_funding_rate(self, trading_pair: str) -> Decimal: # NOTE: Can be removed when GatewayHttpClient.clob_perp_funding_info is used. market_info: DerivativeMarketInfo = self._markets_info[trading_pair] - response: FundingRatesResponse = await self._client.get_funding_rates(market_id=market_info.market_id, limit=1) + async with self._throttler.execute_task(limit_id=CONSTANTS.FUNDING_RATES_LIMIT_ID): + response: FundingRatesResponse = await self._client.get_funding_rates( + market_id=market_info.market_id, limit=1 + ) funding_rate: FundingRate = response.funding_rates[0] # We only want the latest funding rate. return Decimal(funding_rate.rate) @@ -768,7 +788,8 @@ async def _request_oracle_price(self, market_info: DerivativeMarketInfo) -> Deci async def _request_last_trade_price(self, trading_pair: str) -> Decimal: # NOTE: Can be replaced by calling GatewayHTTPClient.clob_perp_last_trade_price market_info: DerivativeMarketInfo = self._markets_info[trading_pair] - response: TradesResponse = await self._client.get_derivative_trades(market_id=market_info.market_id) + async with self._throttler.execute_task(limit_id=CONSTANTS.DERIVATIVE_TRADES_LIMIT_ID): + response: TradesResponse = await self._client.get_derivative_trades(market_id=market_info.market_id) last_trade: DerivativeTrade = response.trades[0] price_scaler: Decimal = Decimal(f"1e-{market_info.quote_token_meta.decimals}") last_trade_price: Decimal = Decimal(last_trade.position_delta.execution_price) * price_scaler @@ -1199,7 +1220,8 @@ async def _request_funding_info(self, trading_pair: str) -> FundingInfo: last_funding_rate: Decimal = await self._request_last_funding_rate(trading_pair=trading_pair) oracle_price: Decimal = await self._request_oracle_price(market_info=market_info) last_trade_price: Decimal = await self._request_last_trade_price(trading_pair=trading_pair) - updated_market_info = await self._client.get_derivative_market(market_id=market_info.market_id) + async with self._throttler.execute_task(limit_id=CONSTANTS.SINGLE_DERIVATIVE_MARKET_LIMIT_ID): + updated_market_info = await self._client.get_derivative_market(market_id=market_info.market_id) funding_info = FundingInfo( trading_pair=trading_pair, index_price=last_trade_price, # Default to using last trade price diff --git a/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py b/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py index 530a078f1a..42c9667e92 100644 --- a/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py +++ b/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py @@ -9,6 +9,7 @@ testnet_config as TESTNET_TOKEN_META_CONFIG, ) +from hummingbot.core.api_throttler.data_types import LinkedLimitWeightPair, RateLimit from hummingbot.core.data_type.common import OrderType, PositionMode, TradeType from hummingbot.core.data_type.in_flight_order import OrderState @@ -89,3 +90,136 @@ def _parse_network_config_to_denom_meta(config: ConfigParser): "testnet": _parse_network_config_to_denom_meta(config=TESTNET_TOKEN_META_CONFIG), "devnet": _parse_network_config_to_denom_meta(config=DEVNET_TOKEN_META_CONFIG) } + +GLOBAL_LIMIT_ID = "GlobalLimitID" +GLOBAL_LIMIT = 5 +PING_LIMIT_ID = "PingLimitID" +PING_LIMIT = GLOBAL_LIMIT +ORDER_BOOK_LIMIT_ID = "OrderBookLimitID" +ORDER_BOOK_LIMIT = GLOBAL_LIMIT +POSITIONS_LIMIT_ID = "PositionsLimitID" +POSITIONS_LIMIT = GLOBAL_LIMIT +ACCOUNT_PORTFOLIO_LIMIT_ID = "AccountPortfolioLimitID" +ACCOUNT_PORTFOLIO_LIMIT = GLOBAL_LIMIT +FUNDING_PAYMENT_LIMIT_ID = "GetFundingPaymentLimitID" +FUNDING_PAYMENT_LIMIT = GLOBAL_LIMIT +ACCOUNT_LIMIT_ID = "AccountLimitID" +ACCOUNT_LIMIT = GLOBAL_LIMIT +SYNC_TIMEOUT_HEIGHT_LIMIT_ID = "SyncTimeoutHeightLimitID" +SYNC_TIMEOUT_HEIGHT_LIMIT = GLOBAL_LIMIT +DERIVATIVE_MARKETS_LIMIT_ID = "DerivativeMarketsLimitID" +DERIVATIVE_MARKETS_LIMIT = GLOBAL_LIMIT +SINGLE_DERIVATIVE_MARKET_LIMIT_ID = "SingleDerivativeMarketLimitID" +SINGLE_DERIVATIVE_MARKET_LIMIT = GLOBAL_LIMIT +SPOT_MARKETS_LIMIT_ID = "SpotMarketsLimitID" +SPOT_MARKETS_LIMIT = GLOBAL_LIMIT +HISTORICAL_DERIVATIVE_ORDERS_LIMIT_ID = "HistoricalDerivativeOrdersLimitID" +HISTORICAL_DERIVATIVE_ORDERS_LIMIT = GLOBAL_LIMIT +DERIVATIVE_TRADES_LIMIT_ID = "DerivativeTradesLimitID" +DERIVATIVE_TRADES_LIMIT = GLOBAL_LIMIT +TRANSACTION_BY_HASH_LIMIT_ID = "TransactionByHashLimitID" +TRANSACTION_BY_HASH_LIMIT = GLOBAL_LIMIT +FUNDING_RATES_LIMIT_ID = "FundingRatesLimitID" +FUNDING_RATES_LIMIT = GLOBAL_LIMIT +ORACLE_PRICES_LIMIT_ID = "OraclePricesLimitID" +ORACLE_PRICES_LIMIT = GLOBAL_LIMIT + +RATE_LIMITS = [ + RateLimit(limit_id=GLOBAL_LIMIT_ID, limit=GLOBAL_LIMIT, time_interval=1), + RateLimit( + limit_id=PING_LIMIT_ID, + limit=PING_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=PING_LIMIT_ID, + limit=PING_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=ORDER_BOOK_LIMIT_ID, + limit=ORDER_BOOK_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=POSITIONS_LIMIT_ID, + limit=POSITIONS_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=ACCOUNT_PORTFOLIO_LIMIT_ID, + limit=ACCOUNT_PORTFOLIO_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=FUNDING_PAYMENT_LIMIT_ID, + limit=FUNDING_PAYMENT_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=ACCOUNT_LIMIT_ID, + limit=ACCOUNT_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=SYNC_TIMEOUT_HEIGHT_LIMIT_ID, + limit=SYNC_TIMEOUT_HEIGHT_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=DERIVATIVE_MARKETS_LIMIT_ID, + limit=DERIVATIVE_MARKETS_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=SINGLE_DERIVATIVE_MARKET_LIMIT_ID, + limit=SINGLE_DERIVATIVE_MARKET_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=SPOT_MARKETS_LIMIT_ID, + limit=SPOT_MARKETS_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=HISTORICAL_DERIVATIVE_ORDERS_LIMIT_ID, + limit=HISTORICAL_DERIVATIVE_ORDERS_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=DERIVATIVE_TRADES_LIMIT_ID, + limit=DERIVATIVE_TRADES_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=TRANSACTION_BY_HASH_LIMIT_ID, + limit=TRANSACTION_BY_HASH_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=FUNDING_RATES_LIMIT_ID, + limit=FUNDING_RATES_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), + RateLimit( + limit_id=ORACLE_PRICES_LIMIT_ID, + limit=ORACLE_PRICES_LIMIT, + time_interval=1, + linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + ), +] From 71b45b4354c862d9ccb527d91ba9e059c5b91536 Mon Sep 17 00:00:00 2001 From: Petio Petrov Date: Wed, 3 May 2023 13:49:52 +0200 Subject: [PATCH 2/3] (refactor) Reworks the Injective Perpetual rate limits --- .../injective_perpetual_api_data_source.py | 99 ++++++----- .../injective_perpetual_constants.py | 162 ++++++++++-------- 2 files changed, 148 insertions(+), 113 deletions(-) diff --git a/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_api_data_source.py b/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_api_data_source.py index 61a743b0f0..e6ab188942 100644 --- a/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_api_data_source.py +++ b/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_api_data_source.py @@ -291,18 +291,19 @@ async def place_order( order_hash: str = order_hashes.derivative[0] try: - order_result: Dict[str, Any] = await self._get_gateway_instance().clob_perp_place_order( - connector=self._connector_name, - chain=self._chain, - network=self._network, - trading_pair=order.trading_pair, - address=self._account_id, - trade_type=order.trade_type, - order_type=order.order_type, - price=order.price, - size=order.amount, - leverage=order.leverage, - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.TRANSACTION_POST_LIMIT_ID): + order_result: Dict[str, Any] = await self._get_gateway_instance().clob_perp_place_order( + connector=self._connector_name, + chain=self._chain, + network=self._network, + trading_pair=order.trading_pair, + address=self._account_id, + trade_type=order.trade_type, + order_type=order.order_type, + price=order.price, + size=order.amount, + leverage=order.leverage, + ) transaction_hash: Optional[str] = order_result.get("txHash") except Exception: @@ -341,14 +342,15 @@ async def batch_order_create(self, orders_to_create: List[GatewayInFlightOrder]) spot_orders=[], derivative_orders=derivative_orders_to_create ) try: - update_result = await self._get_gateway_instance().clob_perp_batch_order_modify( - connector=self._connector_name, - chain=self._chain, - network=self._network, - address=self._account_id, - orders_to_create=orders_to_create, - orders_to_cancel=[], - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.TRANSACTION_POST_LIMIT_ID): + update_result = await self._get_gateway_instance().clob_perp_batch_order_modify( + connector=self._connector_name, + chain=self._chain, + network=self._network, + address=self._account_id, + orders_to_create=orders_to_create, + orders_to_cancel=[], + ) except Exception: await self._update_account_address_and_create_order_hash_manager() raise @@ -382,14 +384,15 @@ async def batch_order_create(self, orders_to_create: List[GatewayInFlightOrder]) async def cancel_order(self, order: GatewayInFlightOrder) -> Tuple[bool, Optional[Dict[str, Any]]]: await order.get_exchange_order_id() - cancelation_result = await self._get_gateway_instance().clob_perp_cancel_order( - chain=self._chain, - network=self._network, - connector=self._connector_name, - address=self._account_id, - trading_pair=order.trading_pair, - exchange_order_id=order.exchange_order_id, - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.TRANSACTION_POST_LIMIT_ID): + cancelation_result = await self._get_gateway_instance().clob_perp_cancel_order( + chain=self._chain, + network=self._network, + connector=self._connector_name, + address=self._account_id, + trading_pair=order.trading_pair, + exchange_order_id=order.exchange_order_id, + ) transaction_hash: Optional[str] = cancelation_result.get("txHash") if transaction_hash in [None, ""]: @@ -426,14 +429,15 @@ async def batch_order_cancel(self, orders_to_cancel: List[InFlightOrder]) -> Lis if not isinstance(result, asyncio.TimeoutError) ] - update_result = await self._get_gateway_instance().clob_perp_batch_order_modify( - connector=self._connector_name, - chain=self._chain, - network=self._network, - address=self._account_id, - orders_to_create=[], - orders_to_cancel=found_orders_to_cancel, - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.TRANSACTION_POST_LIMIT_ID): + update_result = await self._get_gateway_instance().clob_perp_batch_order_modify( + connector=self._connector_name, + chain=self._chain, + network=self._network, + address=self._account_id, + orders_to_create=[], + orders_to_cancel=found_orders_to_cancel, + ) transaction_hash: Optional[str] = update_result.get("txHash") exception = None @@ -572,9 +576,10 @@ async def fetch_last_fee_payment(self, trading_pair: str) -> Tuple[float, Decima async def _update_account_address_and_create_order_hash_manager(self): if not self._order_placement_lock.locked(): raise RuntimeError("The order-placement lock must be acquired before creating the order hash manager.") - response: Dict[str, Any] = await self._get_gateway_instance().clob_injective_balances( - chain=self._chain, network=self._network, address=self._account_id - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.BALANCES_LIMIT_ID): + response: Dict[str, Any] = await self._get_gateway_instance().clob_injective_balances( + chain=self._chain, network=self._network, address=self._account_id + ) self._account_address: str = response["injectiveAddress"] async with self._throttler.execute_task(limit_id=CONSTANTS.ACCOUNT_LIMIT_ID): @@ -590,7 +595,8 @@ async def _update_account_address_and_create_order_hash_manager(self): *tasks_to_await_submitted_orders_to_be_processed_by_chain, return_exceptions=True # await their processing ) self._order_hash_manager = OrderHashManager(network=self._network_obj, sub_account_id=self._account_id) - await self._order_hash_manager.start() + async with self._throttler.execute_task(limit_id=CONSTANTS.NONCE_LIMIT_ID): + await self._order_hash_manager.start() def _check_markets_initialized(self) -> bool: return ( @@ -777,12 +783,13 @@ async def _request_oracle_price(self, market_info: DerivativeMarketInfo) -> Deci """ According to Injective, Oracle Price refers to mark price. """ - response = await self._client.get_oracle_prices( - base_symbol=market_info.oracle_base, - quote_symbol=market_info.oracle_quote, - oracle_type=market_info.oracle_type, - oracle_scale_factor=0, - ) + async with self._throttler.execute_task(limit_id=CONSTANTS.ORACLE_PRICES_LIMIT_ID): + response = await self._client.get_oracle_prices( + base_symbol=market_info.oracle_base, + quote_symbol=market_info.oracle_quote, + oracle_type=market_info.oracle_type, + oracle_scale_factor=0, + ) return Decimal(response.price) async def _request_last_trade_price(self, trading_pair: str) -> Decimal: diff --git a/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py b/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py index 42c9667e92..78d0073192 100644 --- a/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py +++ b/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py @@ -1,3 +1,4 @@ +import sys from configparser import ConfigParser from decimal import Decimal from typing import Dict, Tuple @@ -91,135 +92,162 @@ def _parse_network_config_to_denom_meta(config: ConfigParser): "devnet": _parse_network_config_to_denom_meta(config=DEVNET_TOKEN_META_CONFIG) } -GLOBAL_LIMIT_ID = "GlobalLimitID" -GLOBAL_LIMIT = 5 +SECOND = 1 +MINUTE = 60 +NO_LIMIT = sys.maxsize +CHAIN_RPC_LIMIT_ID = "ChainRPCLimitID" +CHAIN_RPC_LIMIT = 120 +INDEXER_RPC_LIMIT_ID = "IndexerRPCLimitID" +REST_LIMIT_ID = "RESTLimitID" +REST_LIMIT = 120 +TRANSACTION_POST_LIMIT_ID = "TransactionPostLimitID" +TRANSACTION_POST_LIMIT = REST_LIMIT +BALANCES_LIMIT_ID = "BalancesLimitID" +BALANCES_LIMIT = REST_LIMIT +NONCE_LIMIT_ID = "NonceLimitID" +NONCE_LIMIT = REST_LIMIT PING_LIMIT_ID = "PingLimitID" -PING_LIMIT = GLOBAL_LIMIT ORDER_BOOK_LIMIT_ID = "OrderBookLimitID" -ORDER_BOOK_LIMIT = GLOBAL_LIMIT POSITIONS_LIMIT_ID = "PositionsLimitID" -POSITIONS_LIMIT = GLOBAL_LIMIT ACCOUNT_PORTFOLIO_LIMIT_ID = "AccountPortfolioLimitID" -ACCOUNT_PORTFOLIO_LIMIT = GLOBAL_LIMIT FUNDING_PAYMENT_LIMIT_ID = "GetFundingPaymentLimitID" -FUNDING_PAYMENT_LIMIT = GLOBAL_LIMIT ACCOUNT_LIMIT_ID = "AccountLimitID" -ACCOUNT_LIMIT = GLOBAL_LIMIT SYNC_TIMEOUT_HEIGHT_LIMIT_ID = "SyncTimeoutHeightLimitID" -SYNC_TIMEOUT_HEIGHT_LIMIT = GLOBAL_LIMIT +SYNC_TIMEOUT_HEIGHT_LIMIT = CHAIN_RPC_LIMIT DERIVATIVE_MARKETS_LIMIT_ID = "DerivativeMarketsLimitID" -DERIVATIVE_MARKETS_LIMIT = GLOBAL_LIMIT SINGLE_DERIVATIVE_MARKET_LIMIT_ID = "SingleDerivativeMarketLimitID" -SINGLE_DERIVATIVE_MARKET_LIMIT = GLOBAL_LIMIT SPOT_MARKETS_LIMIT_ID = "SpotMarketsLimitID" -SPOT_MARKETS_LIMIT = GLOBAL_LIMIT HISTORICAL_DERIVATIVE_ORDERS_LIMIT_ID = "HistoricalDerivativeOrdersLimitID" -HISTORICAL_DERIVATIVE_ORDERS_LIMIT = GLOBAL_LIMIT DERIVATIVE_TRADES_LIMIT_ID = "DerivativeTradesLimitID" -DERIVATIVE_TRADES_LIMIT = GLOBAL_LIMIT TRANSACTION_BY_HASH_LIMIT_ID = "TransactionByHashLimitID" -TRANSACTION_BY_HASH_LIMIT = GLOBAL_LIMIT FUNDING_RATES_LIMIT_ID = "FundingRatesLimitID" -FUNDING_RATES_LIMIT = GLOBAL_LIMIT ORACLE_PRICES_LIMIT_ID = "OraclePricesLimitID" -ORACLE_PRICES_LIMIT = GLOBAL_LIMIT RATE_LIMITS = [ - RateLimit(limit_id=GLOBAL_LIMIT_ID, limit=GLOBAL_LIMIT, time_interval=1), - RateLimit( - limit_id=PING_LIMIT_ID, - limit=PING_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + RateLimit(limit_id=CHAIN_RPC_LIMIT_ID, limit=CHAIN_RPC_LIMIT, time_interval=MINUTE), + RateLimit(limit_id=INDEXER_RPC_LIMIT_ID, limit=NO_LIMIT, time_interval=SECOND), + RateLimit(limit_id=REST_LIMIT_ID, limit=REST_LIMIT, time_interval=MINUTE), + RateLimit( + limit_id=TRANSACTION_POST_LIMIT_ID, + limit=TRANSACTION_POST_LIMIT, + time_interval=MINUTE, + linked_limits=[ + LinkedLimitWeightPair( + limit_id=REST_LIMIT_ID, # Gateway uses httpClient to post transactions + weight=1, + ), + ], + ), + RateLimit( + limit_id=BALANCES_LIMIT_ID, + limit=BALANCES_LIMIT, + time_interval=MINUTE, + linked_limits=[ + LinkedLimitWeightPair( + limit_id=REST_LIMIT_ID, # Gateway uses httpClient to post transactions + weight=1, + ), + ], + ), + RateLimit( + limit_id=NONCE_LIMIT_ID, + limit=NONCE_LIMIT, + time_interval=MINUTE, + linked_limits=[ + LinkedLimitWeightPair( + limit_id=REST_LIMIT_ID, # the OrderHashManager issues a REST call to get the account nonce + weight=1, + ), + ], ), RateLimit( limit_id=PING_LIMIT_ID, - limit=PING_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=ORDER_BOOK_LIMIT_ID, - limit=ORDER_BOOK_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=POSITIONS_LIMIT_ID, - limit=POSITIONS_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=ACCOUNT_PORTFOLIO_LIMIT_ID, - limit=ACCOUNT_PORTFOLIO_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=FUNDING_PAYMENT_LIMIT_ID, - limit=FUNDING_PAYMENT_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=ACCOUNT_LIMIT_ID, - limit=ACCOUNT_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=SYNC_TIMEOUT_HEIGHT_LIMIT_ID, limit=SYNC_TIMEOUT_HEIGHT_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + time_interval=MINUTE, + linked_limits=[LinkedLimitWeightPair(limit_id=CHAIN_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=DERIVATIVE_MARKETS_LIMIT_ID, - limit=DERIVATIVE_MARKETS_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=SINGLE_DERIVATIVE_MARKET_LIMIT_ID, - limit=SINGLE_DERIVATIVE_MARKET_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=SPOT_MARKETS_LIMIT_ID, - limit=SPOT_MARKETS_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=HISTORICAL_DERIVATIVE_ORDERS_LIMIT_ID, - limit=HISTORICAL_DERIVATIVE_ORDERS_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=DERIVATIVE_TRADES_LIMIT_ID, - limit=DERIVATIVE_TRADES_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=TRANSACTION_BY_HASH_LIMIT_ID, - limit=TRANSACTION_BY_HASH_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=FUNDING_RATES_LIMIT_ID, - limit=FUNDING_RATES_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), RateLimit( limit_id=ORACLE_PRICES_LIMIT_ID, - limit=ORACLE_PRICES_LIMIT, - time_interval=1, - linked_limits=[LinkedLimitWeightPair(limit_id=GLOBAL_LIMIT_ID, weight=1)], + limit=NO_LIMIT, + time_interval=SECOND, + linked_limits=[LinkedLimitWeightPair(limit_id=INDEXER_RPC_LIMIT_ID, weight=1)], ), ] From a8294e23360a3a4ec544d680d8c2fee7f0016962 Mon Sep 17 00:00:00 2001 From: Petio Petrov Date: Thu, 4 May 2023 14:32:46 +0200 Subject: [PATCH 3/3] (refactor) Reuses global constants in injective_perpetual_constants.py --- hummingbot/connector/constants.py | 1 + .../injective_perpetual/injective_perpetual_constants.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hummingbot/connector/constants.py b/hummingbot/connector/constants.py index 7d03bfdfa2..54f8856f72 100644 --- a/hummingbot/connector/constants.py +++ b/hummingbot/connector/constants.py @@ -3,5 +3,6 @@ NaN = float("nan") s_decimal_NaN = Decimal("nan") s_decimal_0 = Decimal(0) +SECOND = 1 MINUTE = 60 TWELVE_HOURS = MINUTE * 60 * 12 diff --git a/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py b/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py index 78d0073192..28f82419b1 100644 --- a/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py +++ b/hummingbot/connector/gateway/clob_perp/data_sources/injective_perpetual/injective_perpetual_constants.py @@ -10,6 +10,7 @@ testnet_config as TESTNET_TOKEN_META_CONFIG, ) +from hummingbot.connector.constants import MINUTE, SECOND from hummingbot.core.api_throttler.data_types import LinkedLimitWeightPair, RateLimit from hummingbot.core.data_type.common import OrderType, PositionMode, TradeType from hummingbot.core.data_type.in_flight_order import OrderState @@ -92,8 +93,6 @@ def _parse_network_config_to_denom_meta(config: ConfigParser): "devnet": _parse_network_config_to_denom_meta(config=DEVNET_TOKEN_META_CONFIG) } -SECOND = 1 -MINUTE = 60 NO_LIMIT = sys.maxsize CHAIN_RPC_LIMIT_ID = "ChainRPCLimitID" CHAIN_RPC_LIMIT = 120