Qubx 0.7.40.dev8__tar.gz → 0.7.40.dev9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/PKG-INFO +1 -2
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/pyproject.toml +0 -1
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/_version.py +2 -2
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/account.py +0 -1
- qubx-0.7.40.dev9/src/qubx/connectors/__init__.py +26 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/account.py +33 -13
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/broker.py +27 -5
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/connection_manager.py +1 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/data.py +23 -19
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchange_manager.py +1 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +1 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/factory.py +22 -19
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/subscription_orchestrator.py +1 -0
- qubx-0.7.40.dev9/src/qubx/connectors/registry.py +224 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/tardis/data.py +20 -29
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/metrics.py +13 -3
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/utils.pyx +3 -3
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/registry.py +38 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/loggers/inmemory.py +4 -2
- qubx-0.7.40.dev9/src/qubx/plugins/__init__.py +7 -0
- qubx-0.7.40.dev9/src/qubx/plugins/loader.py +155 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/trackers/sizers.py +1 -1
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/configs.py +11 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/factory.py +51 -10
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/runner.py +93 -209
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/__init__.py +0 -82
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/account.py +0 -607
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/broker.py +0 -737
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/client.py +0 -467
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/constants.py +0 -117
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/data.py +0 -725
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/extensions.py +0 -253
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/factory.py +0 -240
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/handlers/__init__.py +0 -13
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/handlers/base.py +0 -116
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/handlers/orderbook.py +0 -387
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/handlers/stats.py +0 -352
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/handlers/trades.py +0 -146
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/instruments.py +0 -60
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/nonce.py +0 -29
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/parsers.py +0 -654
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/rate_limits.py +0 -96
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/reader.py +0 -453
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/utils.py +0 -303
- qubx-0.7.40.dev8/src/qubx/connectors/xlighter/websocket.py +0 -503
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/.gitignore +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/LICENSE +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/README.md +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/hatch_build.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/_nb_magic.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/broker.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/data.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/management.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/ome.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/optimization.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/runner.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/sentinels.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/simulated_data.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/simulated_exchange.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/simulator.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/transfers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/backtester/utils.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/cli/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/cli/commands.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/cli/deploy.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/cli/misc.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/cli/release.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/cli/tui.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exceptions.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/reader.py +1 -1
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/utils.py +2 -2
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/tardis/utils.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/account.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/basics.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/context.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/detectors/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/detectors/delisting.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/detectors/stale.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/errors.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/exceptions.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/helpers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/initializer.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/interfaces.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/loggers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/lookups.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/mixins/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/mixins/market.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/mixins/processing.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/mixins/subscription.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/mixins/trading.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/mixins/universe.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/mixins/utils.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/series.pxd +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/series.pyi +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/series.pyx +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/core/utils.pyi +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/composite.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/containers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/helpers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/hft.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/readers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/storage.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/storages/csv.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/storages/questdb.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/storages/utils.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/tardis.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/data/transformers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/emitters/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/emitters/base.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/emitters/composite.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/emitters/csv.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/emitters/indicator.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/emitters/inmemory.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/emitters/prometheus.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/emitters/questdb.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/exporters/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/exporters/composite.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/exporters/formatters/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/exporters/formatters/base.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/exporters/formatters/incremental.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/exporters/formatters/slack.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/exporters/formatters/target_position.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/exporters/redis_streams.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/exporters/slack.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/gathering/simplest.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/health/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/health/base.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/health/dummy.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/loggers/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/loggers/csv.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/loggers/factory.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/loggers/mongo.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/notifications/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/notifications/composite.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/notifications/slack.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/notifications/throttler.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/pandaz/stats.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/pandaz/ta.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/pandaz/utils.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/_build.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/crypto-fees.ini +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restarts/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restarts/state_resolvers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restarts/time_finders.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restorers/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restorers/balance.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restorers/factory.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restorers/interfaces.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restorers/position.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restorers/signal.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restorers/state.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/restorers/utils.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/state/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/state/dummy.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/state/redis.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/ta/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/ta/indicators.pxd +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/ta/indicators.pyi +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/ta/indicators.pyx +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/base.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/project/accounts.toml.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/project/config.yml.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/project/jlive.sh.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/project/template.yml +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/simple/__init__.py.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/simple/config.yml.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/simple/strategy.py.j2 +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/templates/simple/template.yml +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/trackers/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/trackers/advanced.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/trackers/composite.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/trackers/riskctrl.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/charting/lookinglass.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/charting/mpl_helpers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/charting/orderbook.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/collections.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/hft/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/hft/numba_utils.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/hft/orderbook.pyi +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/hft/orderbook.pyx +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/marketdata/ccxt.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/marketdata/dukas.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/misc.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/nonce.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/ntp.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/numbers_utils.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/orderbook.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/plotting/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/plotting/dashboard.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/plotting/data.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/plotting/interfaces.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/questdb.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/rate_limiter.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/ringbuffer.pxd +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/ringbuffer.pyi +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/ringbuffer.pyx +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/accounts.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/kernel_service.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/app.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/handlers.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/init_code.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/kernel.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/styles.tcss +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/slack.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/throttler.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/time.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/utils/websocket_manager.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
- {qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/tests/strategies/obi_trader/src/obi_trader/models/obi_trader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Qubx
|
|
3
|
-
Version: 0.7.40.
|
|
3
|
+
Version: 0.7.40.dev9
|
|
4
4
|
Summary: Qubx - Quantitative Trading Framework
|
|
5
5
|
Project-URL: homepage, https://xlydian.com
|
|
6
6
|
Project-URL: repository, https://github.com/xLydianSoftware/Qubx
|
|
@@ -39,7 +39,6 @@ Requires-Dist: python-binance<2,>=1.0.19
|
|
|
39
39
|
Requires-Dist: python-dotenv<2,>=1.0.0
|
|
40
40
|
Requires-Dist: pyyaml<7,>=6.0.2
|
|
41
41
|
Requires-Dist: qubx-bitfinex-api<4,>=3.0.7
|
|
42
|
-
Requires-Dist: qubx-lighter-api<1,>=0.1.5
|
|
43
42
|
Requires-Dist: questdb<3,>=2.0.3
|
|
44
43
|
Requires-Dist: redis<6,>=5.2.1
|
|
45
44
|
Requires-Dist: rich<14,>=13.9.4
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.7.40.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 7, 40, '
|
|
31
|
+
__version__ = version = '0.7.40.dev9'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 7, 40, 'dev9')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -42,7 +42,6 @@ class SimulatedAccountProcessor(BasicAccountProcessor):
|
|
|
42
42
|
self._channel = channel
|
|
43
43
|
|
|
44
44
|
if restored_state is not None:
|
|
45
|
-
# Convert list of AssetBalance to dict for internal storage
|
|
46
45
|
for balance in restored_state.balances:
|
|
47
46
|
self._balances[balance.currency] = balance
|
|
48
47
|
for instrument, position in restored_state.positions.items():
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Qubx connectors package.
|
|
3
|
+
|
|
4
|
+
This module provides the connector registry and ensures all built-in connectors are registered.
|
|
5
|
+
|
|
6
|
+
Note: Paper trading connectors (SimulatedAccountProcessor, SimulatedBroker) are NOT registered
|
|
7
|
+
with the registry - they are created directly by the backtester and paper trading runner.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Re-export the registry for convenience
|
|
11
|
+
from qubx.connectors.registry import ( # noqa: F401
|
|
12
|
+
ConnectorRegistry,
|
|
13
|
+
account_processor,
|
|
14
|
+
broker,
|
|
15
|
+
data_provider,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Import connector modules to trigger decorator registration
|
|
19
|
+
from qubx.connectors.ccxt.account import CcxtAccountProcessor # noqa: F401
|
|
20
|
+
from qubx.connectors.ccxt.broker import CcxtBroker # noqa: F401
|
|
21
|
+
from qubx.connectors.ccxt.data import CcxtDataProvider # noqa: F401
|
|
22
|
+
from qubx.connectors.tardis.data import TardisDataProvider # noqa: F401
|
|
23
|
+
|
|
24
|
+
# Note: Paper trading connectors (SimulatedAccountProcessor, SimulatedBroker) are
|
|
25
|
+
# NOT registered with the registry - they are created directly by the backtester
|
|
26
|
+
# and paper trading runner.
|
|
@@ -2,14 +2,15 @@ import asyncio
|
|
|
2
2
|
import concurrent.futures
|
|
3
3
|
from asyncio.exceptions import CancelledError
|
|
4
4
|
from collections import defaultdict
|
|
5
|
-
from typing import Awaitable, Callable
|
|
5
|
+
from typing import TYPE_CHECKING, Awaitable, Callable
|
|
6
6
|
|
|
7
|
+
import ccxt.pro as cxp
|
|
7
8
|
import numpy as np
|
|
8
9
|
import pandas as pd
|
|
9
|
-
|
|
10
|
-
import ccxt.pro as cxp
|
|
11
10
|
from ccxt import ExchangeClosedByUser, ExchangeError, ExchangeNotAvailable, NetworkError
|
|
11
|
+
|
|
12
12
|
from qubx import logger
|
|
13
|
+
from qubx.connectors.registry import account_processor
|
|
13
14
|
from qubx.core.account import BasicAccountProcessor
|
|
14
15
|
from qubx.core.basics import (
|
|
15
16
|
CtrlChannel,
|
|
@@ -42,7 +43,11 @@ from .utils import (
|
|
|
42
43
|
instrument_to_ccxt_symbol,
|
|
43
44
|
)
|
|
44
45
|
|
|
46
|
+
if TYPE_CHECKING:
|
|
47
|
+
from qubx.utils.runner.accounts import AccountConfigurationManager
|
|
48
|
+
|
|
45
49
|
|
|
50
|
+
@account_processor("ccxt")
|
|
46
51
|
class CcxtAccountProcessor(BasicAccountProcessor):
|
|
47
52
|
"""
|
|
48
53
|
Subscribes to account information from the exchange.
|
|
@@ -70,14 +75,16 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
70
75
|
|
|
71
76
|
def __init__(
|
|
72
77
|
self,
|
|
73
|
-
|
|
74
|
-
exchange_manager: ExchangeManager,
|
|
78
|
+
exchange_name: str,
|
|
75
79
|
channel: CtrlChannel,
|
|
76
80
|
time_provider: ITimeProvider,
|
|
77
|
-
|
|
78
|
-
health_monitor: IHealthMonitor,
|
|
79
|
-
exchange: str,
|
|
81
|
+
account_manager: "AccountConfigurationManager",
|
|
80
82
|
tcc: TransactionCostsCalculator,
|
|
83
|
+
health_monitor: IHealthMonitor,
|
|
84
|
+
data_provider=None,
|
|
85
|
+
restored_state: RestoredState | None = None,
|
|
86
|
+
read_only: bool = False,
|
|
87
|
+
loop: asyncio.AbstractEventLoop | None = None,
|
|
81
88
|
balance_interval: str = "30Sec",
|
|
82
89
|
position_interval: str = "30Sec",
|
|
83
90
|
subscription_interval: str = "10Sec",
|
|
@@ -86,14 +93,27 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
86
93
|
max_position_restore_days: int = 5,
|
|
87
94
|
max_retries: int = 10,
|
|
88
95
|
connection_timeout: int = 30,
|
|
89
|
-
|
|
90
|
-
restored_state: RestoredState | None = None,
|
|
96
|
+
**kwargs,
|
|
91
97
|
):
|
|
98
|
+
from qubx.connectors.ccxt.factory import get_ccxt_exchange_manager
|
|
99
|
+
|
|
100
|
+
creds = account_manager.get_exchange_credentials(exchange_name)
|
|
101
|
+
|
|
102
|
+
exchange_manager = get_ccxt_exchange_manager(
|
|
103
|
+
exchange=exchange_name,
|
|
104
|
+
use_testnet=creds.testnet,
|
|
105
|
+
api_key=creds.api_key,
|
|
106
|
+
secret=creds.secret,
|
|
107
|
+
health_monitor=health_monitor,
|
|
108
|
+
time_provider=time_provider,
|
|
109
|
+
loop=loop,
|
|
110
|
+
)
|
|
111
|
+
|
|
92
112
|
super().__init__(
|
|
93
|
-
account_id=
|
|
113
|
+
account_id=exchange_name,
|
|
94
114
|
time_provider=time_provider,
|
|
95
|
-
base_currency=base_currency,
|
|
96
|
-
exchange=
|
|
115
|
+
base_currency=creds.base_currency,
|
|
116
|
+
exchange=exchange_name,
|
|
97
117
|
tcc=tcc,
|
|
98
118
|
health_monitor=health_monitor,
|
|
99
119
|
initial_capital=0,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import traceback
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
import pandas as pd
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
6
4
|
|
|
7
5
|
import ccxt
|
|
6
|
+
import pandas as pd
|
|
8
7
|
from ccxt.base.errors import ExchangeError
|
|
8
|
+
|
|
9
9
|
from qubx import logger
|
|
10
|
+
from qubx.connectors.registry import broker
|
|
10
11
|
from qubx.core.basics import (
|
|
11
12
|
CtrlChannel,
|
|
12
13
|
Instrument,
|
|
@@ -27,25 +28,46 @@ from qubx.utils.misc import AsyncThreadLoop
|
|
|
27
28
|
from .exchange_manager import ExchangeManager
|
|
28
29
|
from .utils import ccxt_convert_order_info, instrument_to_ccxt_symbol
|
|
29
30
|
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
from qubx.utils.runner.accounts import AccountConfigurationManager
|
|
33
|
+
|
|
30
34
|
|
|
35
|
+
@broker("ccxt")
|
|
31
36
|
class CcxtBroker(IBroker):
|
|
32
37
|
_exchange_manager: ExchangeManager
|
|
33
38
|
|
|
34
39
|
def __init__(
|
|
35
40
|
self,
|
|
36
|
-
|
|
41
|
+
exchange_name: str,
|
|
37
42
|
channel: CtrlChannel,
|
|
38
43
|
time_provider: ITimeProvider,
|
|
39
44
|
account: IAccountProcessor,
|
|
40
45
|
data_provider: IDataProvider,
|
|
46
|
+
account_manager: "AccountConfigurationManager",
|
|
47
|
+
health_monitor,
|
|
48
|
+
loop: asyncio.AbstractEventLoop | None = None,
|
|
41
49
|
cancel_timeout: int = 30,
|
|
42
50
|
cancel_retry_interval: int = 2,
|
|
43
51
|
max_cancel_retries: int = 10,
|
|
44
52
|
enable_create_order_ws: bool = False,
|
|
45
53
|
enable_cancel_order_ws: bool = False,
|
|
46
54
|
enable_edit_order_ws: bool = False,
|
|
55
|
+
enable_mm: bool = False,
|
|
56
|
+
**kwargs,
|
|
47
57
|
):
|
|
48
|
-
|
|
58
|
+
from qubx.connectors.ccxt.factory import get_ccxt_exchange_manager
|
|
59
|
+
|
|
60
|
+
creds = account_manager.get_exchange_credentials(exchange_name)
|
|
61
|
+
self._exchange_manager = get_ccxt_exchange_manager(
|
|
62
|
+
exchange=exchange_name,
|
|
63
|
+
use_testnet=creds.testnet,
|
|
64
|
+
api_key=creds.api_key,
|
|
65
|
+
secret=creds.secret,
|
|
66
|
+
time_provider=time_provider,
|
|
67
|
+
enable_mm=enable_mm,
|
|
68
|
+
health_monitor=health_monitor,
|
|
69
|
+
loop=loop,
|
|
70
|
+
)
|
|
49
71
|
self.ccxt_exchange_id = str(self._exchange_manager.exchange.name)
|
|
50
72
|
self.channel = channel
|
|
51
73
|
self.time_provider = time_provider
|
|
@@ -15,6 +15,7 @@ from typing import Awaitable, Callable
|
|
|
15
15
|
from ccxt import ExchangeClosedByUser, ExchangeError, ExchangeNotAvailable, NetworkError
|
|
16
16
|
from ccxt.async_support.base.ws.client import Client as _WsClient
|
|
17
17
|
from ccxt.pro import Exchange
|
|
18
|
+
|
|
18
19
|
from qubx import logger
|
|
19
20
|
from qubx.core.basics import CtrlChannel
|
|
20
21
|
from qubx.utils.misc import AsyncThreadLoop
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import re
|
|
2
3
|
from collections import defaultdict
|
|
3
|
-
from typing import Dict, List, Optional, Set, Tuple
|
|
4
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
|
|
4
5
|
|
|
5
6
|
import pandas as pd
|
|
6
7
|
|
|
7
8
|
# CCXT exceptions are now handled in ConnectionManager
|
|
8
9
|
from qubx import logger
|
|
9
10
|
from qubx.connectors.ccxt.utils import ccxt_convert_timeframe_to_exchange_format
|
|
11
|
+
from qubx.connectors.registry import data_provider
|
|
10
12
|
from qubx.core.basics import CtrlChannel, DataType, Instrument, ITimeProvider
|
|
11
13
|
from qubx.core.helpers import BasicScheduler
|
|
12
14
|
from qubx.core.interfaces import IDataProvider, IHealthMonitor
|
|
@@ -22,38 +24,49 @@ from .subscription_manager import SubscriptionManager
|
|
|
22
24
|
from .subscription_orchestrator import SubscriptionOrchestrator
|
|
23
25
|
from .warmup_service import WarmupService
|
|
24
26
|
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from qubx.utils.runner.accounts import AccountConfigurationManager
|
|
25
29
|
|
|
30
|
+
|
|
31
|
+
@data_provider("ccxt")
|
|
26
32
|
class CcxtDataProvider(IDataProvider):
|
|
27
33
|
time_provider: ITimeProvider
|
|
28
34
|
_exchange_manager: ExchangeManager
|
|
29
35
|
_scheduler: BasicScheduler | None = None
|
|
30
|
-
|
|
31
|
-
# Core state - still needed
|
|
32
36
|
_last_quotes: dict[Instrument, Optional[Quote]]
|
|
33
37
|
_warmup_timeout: int
|
|
34
38
|
|
|
35
39
|
def __init__(
|
|
36
40
|
self,
|
|
37
|
-
|
|
41
|
+
exchange_name: str,
|
|
38
42
|
time_provider: ITimeProvider,
|
|
39
43
|
channel: CtrlChannel,
|
|
44
|
+
health_monitor: IHealthMonitor,
|
|
45
|
+
account_manager: "AccountConfigurationManager",
|
|
46
|
+
loop: asyncio.AbstractEventLoop | None = None,
|
|
40
47
|
max_ws_retries: int = 10,
|
|
41
48
|
warmup_timeout: int = 120,
|
|
42
|
-
|
|
49
|
+
**kwargs,
|
|
43
50
|
):
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
from qubx.connectors.ccxt.factory import get_ccxt_exchange_manager
|
|
52
|
+
|
|
53
|
+
settings = account_manager.get_exchange_settings(exchange_name)
|
|
54
|
+
self._exchange_manager = get_ccxt_exchange_manager(
|
|
55
|
+
exchange=exchange_name,
|
|
56
|
+
use_testnet=settings.testnet,
|
|
57
|
+
health_monitor=health_monitor,
|
|
58
|
+
time_provider=time_provider,
|
|
59
|
+
loop=loop,
|
|
60
|
+
**kwargs,
|
|
61
|
+
)
|
|
46
62
|
|
|
47
63
|
self.time_provider = time_provider
|
|
48
64
|
self.channel = channel
|
|
49
65
|
self.max_ws_retries = max_ws_retries
|
|
50
66
|
self._warmup_timeout = warmup_timeout
|
|
51
67
|
self._health_monitor = health_monitor
|
|
52
|
-
|
|
53
|
-
# Core components - access exchange directly via exchange_manager.exchange
|
|
54
68
|
self._exchange_id = str(self._exchange_manager.exchange.name)
|
|
55
69
|
|
|
56
|
-
# Initialize composed components
|
|
57
70
|
self._subscription_manager = SubscriptionManager()
|
|
58
71
|
self._connection_manager = ConnectionManager(
|
|
59
72
|
exchange_id=self._exchange_id,
|
|
@@ -67,15 +80,11 @@ class CcxtDataProvider(IDataProvider):
|
|
|
67
80
|
connection_manager=self._connection_manager,
|
|
68
81
|
exchange_manager=self._exchange_manager,
|
|
69
82
|
)
|
|
70
|
-
|
|
71
|
-
# Data type handler factory for clean separation of data processing logic
|
|
72
83
|
self._data_type_handler_factory = DataTypeHandlerFactory(
|
|
73
84
|
data_provider=self,
|
|
74
85
|
exchange_manager=self._exchange_manager,
|
|
75
86
|
exchange_id=self._exchange_id,
|
|
76
87
|
)
|
|
77
|
-
|
|
78
|
-
# Warmup service for handling historical data warmup
|
|
79
88
|
self._warmup_service = WarmupService(
|
|
80
89
|
handler_factory=self._data_type_handler_factory,
|
|
81
90
|
channel=channel,
|
|
@@ -83,14 +92,9 @@ class CcxtDataProvider(IDataProvider):
|
|
|
83
92
|
exchange_manager=self._exchange_manager,
|
|
84
93
|
warmup_timeout=warmup_timeout,
|
|
85
94
|
)
|
|
86
|
-
|
|
87
|
-
# Quote caching for synthetic quote generation
|
|
88
95
|
self._last_quotes = defaultdict(lambda: None)
|
|
89
96
|
|
|
90
|
-
# Start ExchangeManager monitoring
|
|
91
97
|
self._exchange_manager.start_monitoring()
|
|
92
|
-
|
|
93
|
-
# Register recreation callback for automatic resubscription
|
|
94
98
|
self._exchange_manager.register_recreation_callback(self._handle_exchange_recreation)
|
|
95
99
|
|
|
96
100
|
logger.info(f"<yellow>{self._exchange_id}</yellow> Initialized")
|
{qubx-0.7.40.dev8 → qubx-0.7.40.dev9}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py
RENAMED
|
@@ -4,6 +4,7 @@ from typing import Any, Dict, List, Optional
|
|
|
4
4
|
import ccxt.pro as cxp
|
|
5
5
|
from ccxt.async_support.base.ws.client import Client
|
|
6
6
|
from ccxt.base.errors import ExchangeError, InvalidOrder
|
|
7
|
+
|
|
7
8
|
from qubx import logger
|
|
8
9
|
|
|
9
10
|
from ...adapters.polling_adapter import PollingConfig, PollingToWebSocketAdapter
|
|
@@ -3,6 +3,7 @@ from threading import Thread
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
5
|
import ccxt.pro as cxp
|
|
6
|
+
|
|
6
7
|
from qubx.connectors.ccxt.broker import CcxtBroker
|
|
7
8
|
from qubx.core.basics import CtrlChannel
|
|
8
9
|
from qubx.core.interfaces import IAccountProcessor, IBroker, IDataProvider, IHealthMonitor, ITimeProvider
|
|
@@ -79,6 +80,9 @@ def get_ccxt_exchange(
|
|
|
79
80
|
return ccxt_exchange
|
|
80
81
|
|
|
81
82
|
|
|
83
|
+
_exchange_manager_cache: dict[tuple, ExchangeManager] = {}
|
|
84
|
+
|
|
85
|
+
|
|
82
86
|
def get_ccxt_exchange_manager(
|
|
83
87
|
exchange: str,
|
|
84
88
|
health_monitor: IHealthMonitor,
|
|
@@ -94,37 +98,28 @@ def get_ccxt_exchange_manager(
|
|
|
94
98
|
Get a CCXT exchange with automatic stability management.
|
|
95
99
|
|
|
96
100
|
Returns ExchangeManager wrapper that handles exchange recreation
|
|
97
|
-
during data stall scenarios via self-monitoring.
|
|
101
|
+
during data stall scenarios via self-monitoring. Instances are cached
|
|
102
|
+
by (exchange, api_key, secret, use_testnet) to enable sharing across
|
|
103
|
+
data provider, account processor, and broker.
|
|
104
|
+
"""
|
|
105
|
+
cache_key = (exchange.lower(), api_key, secret, use_testnet, check_interval_seconds)
|
|
98
106
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
api_key (str, optional): The API key. Default is None.
|
|
102
|
-
secret (str, optional): The API secret. Default is None.
|
|
103
|
-
loop (asyncio.AbstractEventLoop, optional): Event loop. Default is None.
|
|
104
|
-
use_testnet (bool): Use testnet/sandbox mode. Default is False.
|
|
105
|
-
check_interval_seconds (float): How often to check for stalls. Default is 30.0.
|
|
106
|
-
**kwargs: Additional parameters for exchange configuration.
|
|
107
|
+
if cache_key in _exchange_manager_cache:
|
|
108
|
+
return _exchange_manager_cache[cache_key]
|
|
107
109
|
|
|
108
|
-
Returns:
|
|
109
|
-
ExchangeManager wrapping the CCXT Exchange
|
|
110
|
-
"""
|
|
111
|
-
# Prepare factory parameters for ExchangeManager recreation
|
|
112
110
|
factory_params = {
|
|
113
111
|
"exchange": exchange,
|
|
114
112
|
"api_key": api_key,
|
|
115
113
|
"secret": secret,
|
|
116
|
-
"loop":
|
|
114
|
+
"loop": None,
|
|
117
115
|
"use_testnet": use_testnet,
|
|
118
|
-
**{k: v for k, v in kwargs.items() if k != "check_interval_seconds"},
|
|
119
116
|
}
|
|
120
117
|
|
|
121
|
-
# Create raw CCXT exchange using public factory method
|
|
122
118
|
ccxt_exchange = get_ccxt_exchange(
|
|
123
|
-
exchange=exchange, api_key=api_key, secret=secret, loop=
|
|
119
|
+
exchange=exchange, api_key=api_key, secret=secret, loop=None, use_testnet=use_testnet
|
|
124
120
|
)
|
|
125
121
|
|
|
126
|
-
|
|
127
|
-
return ExchangeManager(
|
|
122
|
+
manager = ExchangeManager(
|
|
128
123
|
exchange_name=exchange,
|
|
129
124
|
factory_params=factory_params,
|
|
130
125
|
health_monitor=health_monitor,
|
|
@@ -133,6 +128,14 @@ def get_ccxt_exchange_manager(
|
|
|
133
128
|
check_interval_seconds=check_interval_seconds,
|
|
134
129
|
)
|
|
135
130
|
|
|
131
|
+
_exchange_manager_cache[cache_key] = manager
|
|
132
|
+
return manager
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def clear_exchange_manager_cache() -> None:
|
|
136
|
+
"""Clear the exchange manager cache. Useful for testing."""
|
|
137
|
+
_exchange_manager_cache.clear()
|
|
138
|
+
|
|
136
139
|
|
|
137
140
|
def get_ccxt_broker(
|
|
138
141
|
exchange_name: str,
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Registry for data providers, account processors, and brokers.
|
|
3
|
+
|
|
4
|
+
This module provides a registry pattern for connectors, allowing plugins
|
|
5
|
+
to register custom connectors using decorators (like readers and storages).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Callable, Type, TypeVar
|
|
9
|
+
|
|
10
|
+
from qubx import logger
|
|
11
|
+
from qubx.core.interfaces import IAccountProcessor, IBroker, IDataProvider
|
|
12
|
+
|
|
13
|
+
T = TypeVar("T")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ConnectorRegistry:
|
|
17
|
+
"""
|
|
18
|
+
Registry for connector classes (data providers, account processors, brokers).
|
|
19
|
+
|
|
20
|
+
This registry allows plugins to register their own connector implementations
|
|
21
|
+
using decorators, making them available for use in strategy configurations.
|
|
22
|
+
|
|
23
|
+
Classes are registered and instantiated with standardized constructor arguments.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
_data_providers: dict[str, Type[IDataProvider]] = {}
|
|
27
|
+
_account_processors: dict[str, Type[IAccountProcessor]] = {}
|
|
28
|
+
_brokers: dict[str, Type[IBroker]] = {}
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def register_data_provider(cls, name: str) -> Callable[[Type[T]], Type[T]]:
|
|
32
|
+
"""
|
|
33
|
+
Decorator to register a data provider class.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
name: The name to register the data provider under (e.g., "ccxt", "tardis")
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
A decorator function that registers the class
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def decorator(provider_cls: Type[T]) -> Type[T]:
|
|
43
|
+
cls._data_providers[name.lower()] = provider_cls # type: ignore
|
|
44
|
+
logger.debug(f"Registered data provider: {name}")
|
|
45
|
+
return provider_cls
|
|
46
|
+
|
|
47
|
+
return decorator
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def register_account_processor(cls, name: str) -> Callable[[Type[T]], Type[T]]:
|
|
51
|
+
"""
|
|
52
|
+
Decorator to register an account processor class.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
name: The name to register the account processor under
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
A decorator function that registers the class
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def decorator(processor_cls: Type[T]) -> Type[T]:
|
|
62
|
+
cls._account_processors[name.lower()] = processor_cls # type: ignore
|
|
63
|
+
logger.debug(f"Registered account processor: {name}")
|
|
64
|
+
return processor_cls
|
|
65
|
+
|
|
66
|
+
return decorator
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def register_broker(cls, name: str) -> Callable[[Type[T]], Type[T]]:
|
|
70
|
+
"""
|
|
71
|
+
Decorator to register a broker class.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
name: The name to register the broker under
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
A decorator function that registers the class
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def decorator(broker_cls: Type[T]) -> Type[T]:
|
|
81
|
+
cls._brokers[name.lower()] = broker_cls # type: ignore
|
|
82
|
+
logger.debug(f"Registered broker: {name}")
|
|
83
|
+
return broker_cls
|
|
84
|
+
|
|
85
|
+
return decorator
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def get_data_provider(cls, name: str, **kwargs: Any) -> IDataProvider:
|
|
89
|
+
"""
|
|
90
|
+
Get a data provider instance by name.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
name: The name of the data provider
|
|
94
|
+
**kwargs: Arguments to pass to the constructor
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
An instance of the data provider
|
|
98
|
+
|
|
99
|
+
Raises:
|
|
100
|
+
ValueError: If the data provider is not found
|
|
101
|
+
"""
|
|
102
|
+
provider_cls = cls._data_providers.get(name.lower())
|
|
103
|
+
if provider_cls is None:
|
|
104
|
+
raise ValueError(
|
|
105
|
+
f"Data provider '{name}' is not registered. "
|
|
106
|
+
f"Available: {list(cls._data_providers.keys())}"
|
|
107
|
+
)
|
|
108
|
+
return provider_cls(**kwargs)
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
def get_account_processor(cls, name: str, **kwargs: Any) -> IAccountProcessor:
|
|
112
|
+
"""
|
|
113
|
+
Get an account processor instance by name.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
name: The name of the account processor
|
|
117
|
+
**kwargs: Arguments to pass to the constructor
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
An instance of the account processor
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
ValueError: If the account processor is not found
|
|
124
|
+
"""
|
|
125
|
+
processor_cls = cls._account_processors.get(name.lower())
|
|
126
|
+
if processor_cls is None:
|
|
127
|
+
raise ValueError(
|
|
128
|
+
f"Account processor '{name}' is not registered. "
|
|
129
|
+
f"Available: {list(cls._account_processors.keys())}"
|
|
130
|
+
)
|
|
131
|
+
return processor_cls(**kwargs)
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def get_broker(cls, name: str, **kwargs: Any) -> IBroker:
|
|
135
|
+
"""
|
|
136
|
+
Get a broker instance by name.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
name: The name of the broker
|
|
140
|
+
**kwargs: Arguments to pass to the constructor
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
An instance of the broker
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
ValueError: If the broker is not found
|
|
147
|
+
"""
|
|
148
|
+
broker_cls = cls._brokers.get(name.lower())
|
|
149
|
+
if broker_cls is None:
|
|
150
|
+
raise ValueError(
|
|
151
|
+
f"Broker '{name}' is not registered. "
|
|
152
|
+
f"Available: {list(cls._brokers.keys())}"
|
|
153
|
+
)
|
|
154
|
+
return broker_cls(**kwargs)
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def is_data_provider_registered(cls, name: str) -> bool:
|
|
158
|
+
"""Check if a data provider is registered."""
|
|
159
|
+
return name.lower() in cls._data_providers
|
|
160
|
+
|
|
161
|
+
@classmethod
|
|
162
|
+
def is_account_processor_registered(cls, name: str) -> bool:
|
|
163
|
+
"""Check if an account processor is registered."""
|
|
164
|
+
return name.lower() in cls._account_processors
|
|
165
|
+
|
|
166
|
+
@classmethod
|
|
167
|
+
def is_broker_registered(cls, name: str) -> bool:
|
|
168
|
+
"""Check if a broker is registered."""
|
|
169
|
+
return name.lower() in cls._brokers
|
|
170
|
+
|
|
171
|
+
@classmethod
|
|
172
|
+
def get_all_data_providers(cls) -> dict[str, Type[IDataProvider]]:
|
|
173
|
+
"""Get all registered data provider classes."""
|
|
174
|
+
return cls._data_providers.copy()
|
|
175
|
+
|
|
176
|
+
@classmethod
|
|
177
|
+
def get_all_account_processors(cls) -> dict[str, Type[IAccountProcessor]]:
|
|
178
|
+
"""Get all registered account processor classes."""
|
|
179
|
+
return cls._account_processors.copy()
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def get_all_brokers(cls) -> dict[str, Type[IBroker]]:
|
|
183
|
+
"""Get all registered broker classes."""
|
|
184
|
+
return cls._brokers.copy()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# Convenience decorators
|
|
188
|
+
def data_provider(name: str) -> Callable[[Type[T]], Type[T]]:
|
|
189
|
+
"""
|
|
190
|
+
Decorator for registering a data provider class.
|
|
191
|
+
|
|
192
|
+
Usage:
|
|
193
|
+
@data_provider("my_exchange")
|
|
194
|
+
class MyExchangeDataProvider(IDataProvider):
|
|
195
|
+
def __init__(self, exchange_name, time_provider, channel, ...):
|
|
196
|
+
...
|
|
197
|
+
"""
|
|
198
|
+
return ConnectorRegistry.register_data_provider(name)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def account_processor(name: str) -> Callable[[Type[T]], Type[T]]:
|
|
202
|
+
"""
|
|
203
|
+
Decorator for registering an account processor class.
|
|
204
|
+
|
|
205
|
+
Usage:
|
|
206
|
+
@account_processor("my_exchange")
|
|
207
|
+
class MyExchangeAccountProcessor(IAccountProcessor):
|
|
208
|
+
def __init__(self, exchange_name, channel, time_provider, ...):
|
|
209
|
+
...
|
|
210
|
+
"""
|
|
211
|
+
return ConnectorRegistry.register_account_processor(name)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def broker(name: str) -> Callable[[Type[T]], Type[T]]:
|
|
215
|
+
"""
|
|
216
|
+
Decorator for registering a broker class.
|
|
217
|
+
|
|
218
|
+
Usage:
|
|
219
|
+
@broker("my_exchange")
|
|
220
|
+
class MyExchangeBroker(IBroker):
|
|
221
|
+
def __init__(self, exchange_name, channel, time_provider, account, ...):
|
|
222
|
+
...
|
|
223
|
+
"""
|
|
224
|
+
return ConnectorRegistry.register_broker(name)
|