Skip to content

Trading rules missing for dynamically registered pairs on per-pair-rules connectors (KeyError, silent zombie executors on XRPL) #208

Description

@fengtality

Summary

Sibling of #207, same root cause, different pair-derived state left stale. Trading connectors are created with trading_pairs=[], so connectors that build trading rules per pair (XRPL queries tick size / transfer rate / AMM info on-ledger for each pair in self._trading_pairs) initialize with an empty _trading_rules dict. When a pair is later registered dynamically, add_market() initializes the order book but never adds the pair to connector._trading_pairs nor re-runs _update_trading_rules(). Any executor created for that pair then dies at startup:

2026-08-07 05:33:24,767 - hummingbot.core.utils.async_utils - ERROR - Unhandled error in background task: 'USDC-XRP'
Traceback (most recent call last):
  File ".../hummingbot/core/utils/async_utils.py", line 9, in safe_wrapper
    return await c
  File ".../hummingbot/strategy_v2/executors/executor_base.py", line 194, in control_loop
    await self.on_start()
  File ".../hummingbot/strategy_v2/executors/executor_base.py", line 182, in on_start
    await self.validate_sufficient_balance()
  File ".../hummingbot/strategy_v2/executors/order_executor/order_executor.py", line 388, in validate_sufficient_balance
    price_for_validation = self.get_price_for_balance_validation()
  ...
    trading_rule = self._trading_rules[trading_pair]
KeyError: 'USDC-XRP'

Symptoms

Root cause

  • UnifiedConnectorService._create_trading_connector() builds connectors with trading_pairs=[] (services/unified_connector_service.py:695).
  • XRPL's _update_trading_rules() iterates only self._trading_pairs (hummingbot/connector/exchange/xrpl/xrpl_exchange.py, _make_trading_rules_request_impl), so no rules are ever built.
  • The dynamic registration path — ExecutorService._prepare_market()TradingService.add_market() → order-book init in UnifiedConnectorService — touches only the order book tracker. Neither connector._trading_pairs nor trading rules are updated. The base ExchangePyBase.add_trading_pair() likewise only subscribes the order book.
  • The 30-minute _trading_rules_polling_loop cannot self-heal it either, since it re-reads the same empty _trading_pairs.

Most CEX connectors dodge this because one exchange-info request returns rules for all pairs regardless of _trading_pairs. Any connector building rules from self._trading_pairs (XRPL today; others with per-pair rule construction) is affected.

Relationship to #207

Same defect, different victim: #207 is the AsyncThrottler never learning pair-templated rate limits; this is trading rules never being built. Both stem from dynamic pair registration not re-syncing pair-derived connector state, and #207's suggested add_rate_limits() sync does not fix this one (nor vice versa). Suggests a single registration helper that syncs all pair-derived state — _trading_pairs, throttler limits, trading rules — at every registration site, including the data-connector bootstrap path #207 already flags as bypassing registration helpers.

PR to follow addressing both.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions