Skip to content

feat: resilience layer, streaming indicators, public types & cleanup - #90

Merged
cleitonleonel merged 2 commits into
cleitonleonel:masterfrom
victalejo:master
May 14, 2026
Merged

feat: resilience layer, streaming indicators, public types & cleanup#90
cleitonleonel merged 2 commits into
cleitonleonel:masterfrom
victalejo:master

Conversation

@victalejo

Copy link
Copy Markdown
Contributor

Summary

Four orthogonal improvements landed together, each independently testable:

  • ResilienceReconnectPolicy + auto-reconnect with exponential backoff, a stale-connection watchdog, and transparent subscription replay after every reconnect. Verified live: forced socket close → reconnected in 1.5s with candle:EURUSD_otc:60 subscription preserved.
  • Performance — O(1) WebSocket event dispatch (lookup table replacing the if/elif chain in _on_message), incremental streaming indicators (deque-based, O(1) per tick instead of O(n²)), opt-in TTL+LRU cache for get_candles (live: 396ms → 0.0ms on cached call), and orjson returning bytes directly on the hot path.
  • Public types (PEP 561) — py.typed marker + frozen dataclasses: Candle, TradeResult, Balance, ProfileInfo, AssetInfo, ReconnectPolicy, Subscription. Re-exported from pyquotex.* so consumers get real autocomplete and mypy --strict coverage.
  • Cleanuprequirements.txt synced with pyproject.toml (numpy removed; orjson documented as [fast] extra); OptimizedQuotexMixin deprecated and its two broken methods now delegate to the real TradingMixin; sell_option polling replaced with wait_until + hard timeout (TODO closed).

What's new for users

from pyquotex.stable_api import Quotex
from pyquotex import ReconnectPolicy, Candle

# Auto-reconnect with backoff + watchdog (default ON).
policy = ReconnectPolicy(stale_timeout=120, max_delay=15)

async with Quotex(email=..., password=..., reconnect_policy=policy) as q:
    candles = await q.get_candles("EURUSD", None, 3600, 60, use_cache=True)
    typed = [Candle.from_dict(c) for c in candles]

Streaming indicators for live tick loops:

from pyquotex.utils.streaming_indicators import StreamingRSI

rsi = StreamingRSI(period=14)
async for tick in stream:
    value = rsi.update(tick.close)  # O(1) per tick, returns None until warmed up

Backward compatibility

Every previously public method/parameter is preserved. The public-surface
regression test (tests/test_api_surface.py) was regenerated to include the new
opt-in use_cache=False parameter, so future shrinks are still caught.

  • *_optimized methods continue to work; they now emit DeprecationWarning.
  • Auto-reconnect can be disabled with ReconnectPolicy(enabled=False).
  • No breaking changes to existing examples (binary_bot.py, trade_bot.py, etc.).

Test plan

  • pytest tests/90 / 90 offline tests pass on Python 3.13
  • 44 new unit tests added across:
    • tests/test_types.py — Candle/TradeResult/Balance/ProfileInfo/AssetInfo/ReconnectPolicy/Subscription
    • tests/test_streaming_indicators.py — incremental SMA/EMA/RSI/Bollinger; cross-checked against batch implementation
    • tests/test_cache.py — TTLCache LRU eviction, lazy expiration, contains
    • tests/test_json_utils.py — dumps/dumps_bytes/dumps_str roundtrips with both orjson and stdlib paths
    • tests/test_dispatch_table.py — every _h_* control-event handler exercised directly
    • tests/test_reconnect.py — fake WebsocketClient with ConnectionClosed(1006) to verify backoff, replay, and clean close
  • Public API surface snapshot regenerated (scripts/snapshot_api_surface.py)
  • Live smoke against DEMO account (scripts/smoke_demo.py):
    • ✅ Authentication + event-driven balance retrieval: Balance(DEMO): 10448.88 USD
    • get_candles(use_cache=True): first call 396.5 ms → cached call 0.0 ms
    • Candle.from_dict conversion on live OHLC data
    • ✅ Subscription tracking: ['candle:EURUSD_otc:60'] registered automatically
    • Forced socket close → auto-reconnect in 1.5s (open_count=2)
    • ✅ Subscription still tracked post-reconnect; s_authorization re-issued and instruments/list replayed
    • ✅ Async context manager (async with Quotex(...)) closes cleanly

Files of note

File Purpose
pyquotex/types.py New public dataclasses + ReconnectPolicy/Subscription
pyquotex/py.typed PEP 561 marker (also included in wheel/sdist)
pyquotex/ws/client.py Reconnect loop, stale-watchdog, subscription replay
pyquotex/api.py Dispatch table (_control_handlers), subscription tracking helpers
pyquotex/_api/history.py get_candles(..., use_cache=True) opt-in
pyquotex/_api/trading.py sell_option migrated from poll-loop to wait_until
pyquotex/_api/realtime.py Stream subscribe/unsubscribe now registers with API
pyquotex/stable_api.py reconnect_policy= kwarg + __aenter__/__aexit__
pyquotex/utils/streaming_indicators.py New: StreamingSMA/EMA/RSI/Bollinger (O(1)/tick)
pyquotex/utils/cache.py New: TTLCache
pyquotex/utils/json_utils.py Rewritten: dumps_bytes hot path, HAS_ORJSON flag
pyquotex/utils/optimization.py Deprecated; broken methods now delegate to real ones
requirements.txt Synced with pyproject.toml (numpy removed)
tests/fixtures/api_surface.json Regenerated snapshot
scripts/smoke_demo.py End-to-end smoke test against live broker

victalejo added 2 commits May 14, 2026 04:45
- Introduced tests for TTLCache to validate set/get, eviction, expiration, and parameter validation.
- Added tests for dispatch table handlers in QuotexAPI to ensure proper event handling and state management.
- Created tests for json_utils to verify serialization and deserialization functions.
- Implemented tests for the reconnect policy and WebsocketClient to ensure resilience and subscription replay functionality.
- Added tests for streaming indicators (SMA, EMA, RSI, Bollinger Bands) to validate their behavior and correctness.
- Introduced tests for new public dataclasses in pyquotex.types, including Candle, TradeResult, Balance, ProfileInfo, AssetInfo, and ReconnectPolicy.
@cleitonleonel
cleitonleonel merged commit 45cc2cf into cleitonleonel:master May 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants