fix(stable_api): four correctness bugs — open_pending, sell_option, worker index collision, follow_candle spam#87
Merged
cleitonleonel merged 2 commits intocleitonleonel:masterfrom May 2, 2026
Conversation
…get_historical_candles and start_candles_one_stream --- open_pending while/else (closes cleitonleonel#83) --- The while/else construct caused instruments_follow() to fire on the failure path (loop condition False == pending_id never set) and be silently skipped on the success path. Replace with an explicit if-check after the loop so instruments_follow is called only when pending_id is actually present. --- sell_option race condition (closes cleitonleonel#84) --- sold_options_respond was reset to None AFTER the WebSocket request was sent. On fast connections the response can arrive before the reset, wiping the data and causing a guaranteed TimeoutError. Move the sentinel reset to before the send, consistent with how candles_data and other sentinels are handled in the codebase. --- get_historical_candles worker index collision (closes cleitonleonel#85) --- Each worker generated its request index as int(time.time() * 1000) + worker_id Two workers (or two iterations within the same worker) executing in the same millisecond produce identical indices. The event registry keys responses on this index, so workers steal each other's data silently. Replace with a module-level monotonic itertools.count seeded from the current millisecond so every request gets a globally unique index within the process. --- start_candles_one_stream follow_candle spam (closes cleitonleonel#86) --- follow_candle() (a WebSocket subscribe request) was called on every 0.2 s poll iteration, sending up to 100 subscribe messages per stream setup. Move the single subscribe call to before the loop; the loop body now only polls candle_generated_check and sleeps.
…te API reference
app.py — 27 commands now cover every public stable_api method:
Connection & Account (6)
login, balance, server-time, set-demo-balance, settings, test-all
Assets & Payouts (3)
assets, payout, payout-asset
Candle & Market Data (8)
candles, candles-v2, candles-deep, history-line, candle-info,
realtime-price, realtime-sentiment, realtime-candle
Trading (6)
buy, sell, pending, check, result, signals
History & Indicators (2)
history, indicator
Monitoring & Strategy (2)
monitor, strategy
New commands not previously in app.py:
server-time → get_server_time()
set-demo-balance → edit_practice_balance()
settings → store_settings_apply()
payout → get_payment()
payout-asset → get_payout_by_asset()
candles-v2 → get_candle_v2()
history-line → get_history_line()
candle-info → opening_closing_current_candle()
realtime-price → start_realtime_price() + get_realtime_price()
realtime-sentiment → start_realtime_sentiment() + get_realtime_sentiment()
realtime-candle → start_realtime_candle()
pending → open_pending()
result → get_result()
signals → start_signals_data() + get_signal_data()
indicator → calculate_indicator()
Other improvements:
- Shared _add_account_flags / _add_asset_flag parser helpers (DRY)
- _print_candles_table() shared table renderer for all candle commands
- _save_candles_csv() for candles-deep --output flag
- connect_with_retry() reused across all commands
- COMMAND_MAP dict dispatch (replaces if/elif chain)
- --demo/--live are now mutually exclusive groups
docs/en/API_REFERENCE.md — new complete API reference:
- All 52 public methods documented with signatures, params, return types
- CLI command table (27 commands)
- Error handling guide
- 8 complete usage examples (connect, RSI, trade+check, deep history,
live indicator, pending order, asset scan, CSV export)
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.
Problem
Four independent but related correctness bugs in
stable_api.py, all discovered during a code review after PR #82. Each bug is documented in a dedicated issue.Changes
1.
open_pending— invertedwhile/elselogic (closes #83)Python's
while/elseruns theelseblock when the loop condition becomesFalse— i.e. whenpending_idwas never set (timeout or disconnect). The original code calledinstruments_follow()there, meaning:instruments_followwas skipped entirelyFix: replace
while/elsewith an explicitif self.api.pending_id is not Nonecheck after the loop.2.
sell_option— race condition wipes WebSocket response (closes #84)sold_options_respondwas reset toNoneafter the request was sent. On low-latency connections the WS response arrives before the reset line executes, gets silently overwritten, and the poll loop raisesTimeoutErrorevery time.Fix: move the sentinel reset to before
send— consistent with howcandles_dataand other sentinels are handled elsewhere in the codebase.3.
get_historical_candles— worker index collision (closes #85)Workers computed:
Two workers (or two iterations within the same worker) running in the same millisecond produce identical indices. The event registry keys responses on this index, so workers silently steal each other's data — wrong historical candles returned with no error raised.
Fix: a module-level
itertools.countseeded from the current millisecond timestamp guarantees a globally unique index per request within the process.4.
start_candles_one_stream—follow_candlespam (closes #86)follow_candle()(a WebSocket subscribe request) was called inside the 0.2 s polling loop, sending up to 100 subscribe messages for a single stream setup. This directly contradicts the README warning about excessive requests causing bans.Fix: move the single subscribe call before the loop; the loop body now only polls
candle_generated_checkand sleeps.Files Changed
pyquotex/stable_api.pyImpact / Risks
stable_api.py