(fix) refresh per-pair trading rules on dynamic pair registration - #212
Open
fengtality wants to merge 2 commits into
Open
(fix) refresh per-pair trading rules on dynamic pair registration#212fengtality wants to merge 2 commits into
fengtality wants to merge 2 commits into
Conversation
Greptile SummaryThe PR extends dynamic pair registration to refresh missing per-pair trading rules on order-producing paths while keeping market-data and leverage paths limited to throttler synchronization.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| services/unified_connector_service.py | Adds optional, trading-only refresh of missing per-pair rules while preserving throttler synchronization across all registration paths. |
| services/accounts_service.py | Synchronizes dynamic pair state before direct-trade rule validation and reports missing refreshed rules as retryable failures. |
| services/perpetual_trading_service.py | Explicitly disables trading-rule refresh for leverage updates while retaining pair-specific throttler synchronization. |
| test/test_sync_pair_derived_state.py | Expands coverage for per-pair rule refreshes, idempotency, data-only behavior, fetch failures, and invalid-pair handling. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Dynamic pair request] --> B[sync_pair_derived_state]
B --> C[Validate and register pair]
C --> D[Refresh pair-derived throttler limits]
D --> E{Order-producing path?}
E -- No --> F[Continue without trading-rule fetch]
E -- Yes --> G{Trading connector and rule missing?}
G -- No --> H[Continue]
G -- Yes --> I[Refresh trading rules]
I --> J{Requested rule available?}
J -- Yes --> H
J -- No --> K[Direct trade returns retryable 503]
Reviews (2): Last reviewed commit: "(fix) adversarial-review fixes: refresh_..." | Re-trigger Greptile
fengtality
added a commit
that referenced
this pull request
Aug 7, 2026
…uard property eval - Validate the pair against the connector's symbol map BEFORE appending to _trading_pairs, raising ValueError for unknown pairs. There is no rollback path, and a poisoned entry breaks every per-pair consumer: status polling raises KeyError on the symbol map inside gathers without return_exceptions (killing balance/position/order updates until restart), and per-pair trading-rules rebuilds fail for ALL pairs. Previously a typo'd pair via add_market or set_leverage was registered permanently. Already-registered pairs skip re-validation (the symbol map may be transiently unavailable). - Evaluate rate_limits_rules inside the try: it is a property that can itself raise, and hasattr() evaluated it outside the guard (any non-AttributeError escaping aborted registration; an internal AttributeError silently skipped the sync with zero logging). - Scope comments to what this PR implements (the rules-fetch retry rationale belongs to the stacked #212). Findings from adversarial review; tests added for rejection-without- registration and skip-revalidation-when-registered. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Connectors that build trading rules per pair (XRPL queries the ledger for each pair in _trading_pairs) initialize with an empty rules dict, and dynamically registered pairs never got a rule: executors then die at startup with KeyError in validate_sufficient_balance but register as RUNNING zombies — 201 Created, order_id None, no order ever reaches the ledger. Reproduced 3/3 on xrpl / USDC-XRP. Direct trades via place_trade 503'd forever on the empty-rules check or 400'd "pair not supported". Extends sync_pair_derived_state (from #207's fix) to refresh trading rules when the registered pair has no rule yet, gated on is_trading_required — data connectors never place orders, and the refresh is a real (possibly on-chain) fetch that would add latency and warnings to every order-book bootstrap. Wires the sync into place_trade before its rules checks. flake8 skipped: violations in accounts_service.py are pre-existing on main; the change there is the 6-line place_trade sync. Fixes #208 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rade errors - Add refresh_rules param (default True) to sync_pair_derived_state and pass False on every market-data path (_add_trading_pair_to_tracker, ensure_data_connector_started) and set_leverage: the is_trading_required gate alone was ineffective on the main data path because get_best_connector_for_market PREFERS trading connectors, so order-book bootstraps paid the possibly-on-chain rules fetch whenever credentials existed. Rules now refresh only on paths that lead to order placement (add_market registration, place_trade). - place_trade: surface pair-validation failures as 400 with the validation message; replace the misleading 400 "pair not supported / available pairs" for a validated pair whose rule is missing with a retryable 503 — the pair IS supported, the refresh failed or is pending. - Tests: order-book path skips the rules fetch but still syncs the throttler; a typo'd pair cannot poison the rules refresh for valid pairs (XRPL-faithful fake that raises before any rule lands). flake8 skipped: accounts_service.py violations are pre-existing on main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fengtality
force-pushed
the
fix/208-per-pair-trading-rules
branch
from
August 7, 2026 06:50
2db2cee to
698e01e
Compare
Contributor
Author
|
Adversarial review — the reviewer empirically reproduced a blocker against the real helper, fixed in the latest commits:
Verified clean by the reviewer: no torn state in XRPL's clear-and-rebuild (no awaits between clear and repopulate), concurrent registrations converge (300 randomized interleavings), symbol-map re-init benign, Gateway safely skipped. |
rapcmia
approved these changes
Aug 7, 2026
rapcmia
left a comment
Contributor
There was a problem hiding this comment.
- Setup with condor and added XRPL ok
- git switch --detach origin/fix/207-throttler-pair-limits
- git switch --detach origin/fix/208-per-pair-trading-rules
- Registered RLUSD-XRP first, then confirmed its per-pair trading rule became available using
/market-data/trading-pair/add - Submitted a minimal XRPL limit order successfully, the order appeared as an active XRPL offer using
/trading/orders - Rejected unsupported XRP-USDS with HTTP 400.
- Confirmed the valid RLUSD-XRP rule remained available after rejecting the invalid pair.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #208. Stacked on the #207 fix (base branch
fix/207-throttler-pair-limits) — retarget tomainafter that merges.Connectors that build trading rules per pair (XRPL queries the ledger for each pair in
_trading_pairs) initialize with an empty rules dict, and dynamically registered pairs never get a rule. Executors then die at startup withKeyErrorinvalidate_sufficient_balance— but register asRUNNING,order_id: None: silent zombies that report 201 Created and never place an order (reproduced 3/3 onxrpl/USDC-XRP). Direct trades viaplace_trade503'd forever ("rules not yet loaded") or 400'd "pair not supported".Fix
Extends
sync_pair_derived_stateto refresh trading rules when the registered pair has no rule yet, gated onis_trading_required: data connectors never place orders, and the refresh is a real (possibly on-chain, retry-with-backoff) fetch that would add latency and spurious warnings to every order-book bootstrap. No-op for CEX connectors whose exchange-info fetch already returns rules for all pairs. Also wires the sync intoplace_tradebefore its rules checks.Verified safe: XRPL's
_update_trading_rulesfetches beforeclear(), and the clear→repopulate section has no awaits — a failed or concurrent refresh cannot leave a torn/empty rules dict.Tests
+4: rules built for registered pair; no re-fetch when rule exists; data connector skips rules but still syncs throttler; fetch failure swallowed with registration intact. Suite: 94 passed, same 7 pre-existing failures.
🤖 Generated with Claude Code