Qubx 0.6.75__tar.gz → 0.6.77__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.
Potentially problematic release.
This version of Qubx might be problematic. Click here for more details.
- {qubx-0.6.75 → qubx-0.6.77}/PKG-INFO +1 -1
- {qubx-0.6.75 → qubx-0.6.77}/pyproject.toml +1 -1
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/account.py +18 -17
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/subscription_manager.py +51 -30
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/account.py +7 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/context.py +15 -5
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/interfaces.py +50 -10
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/loggers.py +2 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/mixins/processing.py +55 -5
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/mixins/trading.py +20 -14
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/mixins/universe.py +13 -2
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/emitters/indicator.py +8 -3
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/emitters/questdb.py +1 -1
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/exporters/formatters/incremental.py +8 -8
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restarts/state_resolvers.py +4 -4
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restorers/signal.py +2 -2
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/runner/_jupyter_runner.pyt +9 -9
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/runner/runner.py +2 -1
- {qubx-0.6.75 → qubx-0.6.77}/LICENSE +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/README.md +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/build.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/_nb_magic.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/account.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/broker.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/data.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/management.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/ome.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/optimization.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/runner.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/sentinels.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/simulated_data.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/simulated_exchange.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/simulator.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/backtester/utils.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/cli/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/cli/commands.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/cli/deploy.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/cli/misc.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/cli/release.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/cli/tui.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/broker.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/data.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exceptions.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/factory.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/reader.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/utils.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/tardis/data.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/connectors/tardis/utils.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/basics.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/deque.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/errors.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/exceptions.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/helpers.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/initializer.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/lookups.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/metrics.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/mixins/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/mixins/market.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/mixins/subscription.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/mixins/utils.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/series.pxd +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/series.pyi +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/series.pyx +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/stale_data_detector.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/utils.pyi +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/core/utils.pyx +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/data/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/data/composite.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/data/helpers.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/data/hft.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/data/readers.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/data/registry.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/data/tardis.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/emitters/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/emitters/base.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/emitters/composite.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/emitters/csv.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/emitters/inmemory.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/emitters/prometheus.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/exporters/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/exporters/composite.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/exporters/formatters/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/exporters/formatters/base.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/exporters/formatters/slack.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/exporters/redis_streams.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/exporters/slack.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/features/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/features/core.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/features/orderbook.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/features/price.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/features/trades.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/features/utils.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/gathering/simplest.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/health/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/health/base.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/loggers/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/loggers/csv.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/loggers/factory.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/loggers/inmemory.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/loggers/mongo.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/math/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/math/stats.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/notifications/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/notifications/composite.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/notifications/slack.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/notifications/throttler.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/pandaz/ta.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/pandaz/utils.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/_build.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/crypto-fees.ini +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restarts/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restarts/time_finders.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restorers/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restorers/balance.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restorers/factory.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restorers/interfaces.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restorers/position.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restorers/state.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/restorers/utils.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/ta/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/ta/indicators.pxd +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/ta/indicators.pyi +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/ta/indicators.pyx +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/base.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/project/accounts.toml.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/project/config.yml.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/project/jlive.sh.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/project/template.yml +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/simple/__init__.py.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/simple/config.yml.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/simple/strategy.py.j2 +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/templates/simple/template.yml +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/trackers/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/trackers/advanced.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/trackers/composite.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/trackers/riskctrl.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/trackers/sizers.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/charting/lookinglass.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/charting/mpl_helpers.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/charting/orderbook.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/collections.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/marketdata/ccxt.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/marketdata/dukas.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/misc.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/ntp.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/numbers_utils.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/orderbook.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/plotting/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/plotting/dashboard.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/plotting/data.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/plotting/interfaces.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/questdb.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/runner/__init__.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/runner/accounts.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/runner/configs.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/runner/factory.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/time.py +0 -0
- {qubx-0.6.75 → qubx-0.6.77}/src/qubx/utils/version.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "Qubx"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.77"
|
|
8
8
|
description = "Qubx - Quantitative Trading Framework"
|
|
9
9
|
authors = [ "Dmitry Marienko <dmitry.marienko@xlydian.com>", "Yuriy Arabskyy <yuriy.arabskyy@xlydian.com>",]
|
|
10
10
|
readme = "README.md"
|
|
@@ -28,6 +28,7 @@ from qubx.utils.marketdata.ccxt import ccxt_symbol_to_instrument
|
|
|
28
28
|
from qubx.utils.misc import AsyncThreadLoop
|
|
29
29
|
|
|
30
30
|
from .exceptions import CcxtSymbolNotRecognized
|
|
31
|
+
from .exchange_manager import ExchangeManager
|
|
31
32
|
from .utils import (
|
|
32
33
|
ccxt_convert_balance,
|
|
33
34
|
ccxt_convert_deal_info,
|
|
@@ -46,7 +47,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
46
47
|
Subscribes to account information from the exchange.
|
|
47
48
|
"""
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
exchange_manager: ExchangeManager
|
|
50
51
|
channel: CtrlChannel
|
|
51
52
|
base_currency: str
|
|
52
53
|
balance_interval: str
|
|
@@ -69,7 +70,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
69
70
|
def __init__(
|
|
70
71
|
self,
|
|
71
72
|
account_id: str,
|
|
72
|
-
|
|
73
|
+
exchange_manager: ExchangeManager,
|
|
73
74
|
channel: CtrlChannel,
|
|
74
75
|
time_provider: ITimeProvider,
|
|
75
76
|
base_currency: str,
|
|
@@ -90,7 +91,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
90
91
|
tcc=tcc,
|
|
91
92
|
initial_capital=0,
|
|
92
93
|
)
|
|
93
|
-
self.
|
|
94
|
+
self.exchange_manager = exchange_manager
|
|
94
95
|
self.channel = channel
|
|
95
96
|
self.max_retries = max_retries
|
|
96
97
|
self.balance_interval = balance_interval
|
|
@@ -99,7 +100,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
99
100
|
self.open_order_interval = open_order_interval
|
|
100
101
|
self.open_order_backoff = open_order_backoff
|
|
101
102
|
self.max_position_restore_days = max_position_restore_days
|
|
102
|
-
self._loop = AsyncThreadLoop(exchange.asyncio_loop)
|
|
103
|
+
self._loop = AsyncThreadLoop(exchange_manager.exchange.asyncio_loop)
|
|
103
104
|
self._is_running = False
|
|
104
105
|
self._polling_tasks = {}
|
|
105
106
|
self._polling_to_init = defaultdict(bool)
|
|
@@ -125,7 +126,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
125
126
|
|
|
126
127
|
self._is_running = True
|
|
127
128
|
|
|
128
|
-
if not self.exchange.isSandboxModeEnabled:
|
|
129
|
+
if not self.exchange_manager.exchange.isSandboxModeEnabled:
|
|
129
130
|
# - start polling tasks
|
|
130
131
|
self._polling_tasks["balance"] = self._loop.submit(
|
|
131
132
|
self._poller("balance", self._update_balance, self.balance_interval)
|
|
@@ -178,8 +179,8 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
178
179
|
|
|
179
180
|
def _get_instrument_for_currency(self, currency: str) -> Instrument:
|
|
180
181
|
symbol = f"{currency}/{self.base_currency}"
|
|
181
|
-
market = self.exchange.market(symbol)
|
|
182
|
-
exchange_name = self.exchange.name
|
|
182
|
+
market = self.exchange_manager.exchange.market(symbol)
|
|
183
|
+
exchange_name = self.exchange_manager.exchange.name
|
|
183
184
|
assert exchange_name is not None
|
|
184
185
|
return ccxt_symbol_to_instrument(exchange_name, market)
|
|
185
186
|
|
|
@@ -267,7 +268,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
267
268
|
|
|
268
269
|
async def _update_balance(self) -> None:
|
|
269
270
|
"""Fetch and update balances from exchange"""
|
|
270
|
-
balances_raw = await self.exchange.fetch_balance()
|
|
271
|
+
balances_raw = await self.exchange_manager.exchange.fetch_balance()
|
|
271
272
|
balances = ccxt_convert_balance(balances_raw)
|
|
272
273
|
current_balances = self.get_balances()
|
|
273
274
|
|
|
@@ -292,8 +293,8 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
292
293
|
|
|
293
294
|
async def _update_positions(self) -> None:
|
|
294
295
|
# fetch and update positions from exchange
|
|
295
|
-
ccxt_positions = await self.exchange.fetch_positions()
|
|
296
|
-
positions = ccxt_convert_positions(ccxt_positions, self.exchange.name, self.exchange.markets) # type: ignore
|
|
296
|
+
ccxt_positions = await self.exchange_manager.exchange.fetch_positions()
|
|
297
|
+
positions = ccxt_convert_positions(ccxt_positions, self.exchange_manager.exchange.name, self.exchange_manager.exchange.markets) # type: ignore
|
|
297
298
|
# update required instruments that we need to subscribe to
|
|
298
299
|
self._required_instruments.update([p.instrument for p in positions])
|
|
299
300
|
# update positions
|
|
@@ -358,7 +359,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
358
359
|
if _fetch_instruments:
|
|
359
360
|
logger.debug(f"Fetching missing tickers for {_fetch_instruments}")
|
|
360
361
|
_fetch_symbols = [instrument_to_ccxt_symbol(instr) for instr in _fetch_instruments]
|
|
361
|
-
tickers: dict[str, dict] = await self.exchange.fetch_tickers(_fetch_symbols)
|
|
362
|
+
tickers: dict[str, dict] = await self.exchange_manager.exchange.fetch_tickers(_fetch_symbols)
|
|
362
363
|
for symbol, ticker in tickers.items():
|
|
363
364
|
instr = _symbol_to_instrument.get(symbol)
|
|
364
365
|
if instr is not None:
|
|
@@ -457,7 +458,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
457
458
|
|
|
458
459
|
async def _cancel_order(order: Order) -> None:
|
|
459
460
|
try:
|
|
460
|
-
await self.exchange.cancel_order(order.id, symbol=instrument_to_ccxt_symbol(order.instrument))
|
|
461
|
+
await self.exchange_manager.exchange.cancel_order(order.id, symbol=instrument_to_ccxt_symbol(order.instrument))
|
|
461
462
|
logger.debug(
|
|
462
463
|
f" :: [SYNC] Canceled {order.id} {order.instrument.symbol} {order.side} {order.quantity} @ {order.price} ({order.status})"
|
|
463
464
|
)
|
|
@@ -475,7 +476,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
475
476
|
) -> dict[str, Order]:
|
|
476
477
|
_start_ms = self._get_start_time_in_ms(days_before) if limit is None else None
|
|
477
478
|
_ccxt_symbol = instrument_to_ccxt_symbol(instrument)
|
|
478
|
-
_fetcher = self.exchange.fetch_open_orders if is_open else self.exchange.fetch_orders
|
|
479
|
+
_fetcher = self.exchange_manager.exchange.fetch_open_orders if is_open else self.exchange_manager.exchange.fetch_orders
|
|
479
480
|
_raw_orders = await _fetcher(_ccxt_symbol, since=_start_ms, limit=limit)
|
|
480
481
|
_orders = [ccxt_convert_order_info(instrument, o) for o in _raw_orders]
|
|
481
482
|
_id_to_order = {o.id: o for o in _orders}
|
|
@@ -484,7 +485,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
484
485
|
async def _fetch_deals(self, instrument: Instrument, days_before: int = 30) -> list[Deal]:
|
|
485
486
|
_start_ms = self._get_start_time_in_ms(days_before)
|
|
486
487
|
_ccxt_symbol = instrument_to_ccxt_symbol(instrument)
|
|
487
|
-
deals_data = await self.exchange.fetch_my_trades(_ccxt_symbol, since=_start_ms)
|
|
488
|
+
deals_data = await self.exchange_manager.exchange.fetch_my_trades(_ccxt_symbol, since=_start_ms)
|
|
488
489
|
deals: list[Deal] = [ccxt_convert_deal_info(o) for o in deals_data]
|
|
489
490
|
return sorted(deals, key=lambda x: x.time) if deals else []
|
|
490
491
|
|
|
@@ -530,9 +531,9 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
530
531
|
_symbol_to_instrument = {}
|
|
531
532
|
|
|
532
533
|
async def _watch_executions():
|
|
533
|
-
exec = await self.exchange.watch_orders()
|
|
534
|
+
exec = await self.exchange_manager.exchange.watch_orders()
|
|
534
535
|
for report in exec:
|
|
535
|
-
instrument = ccxt_find_instrument(report["symbol"], self.exchange, _symbol_to_instrument)
|
|
536
|
+
instrument = ccxt_find_instrument(report["symbol"], self.exchange_manager.exchange, _symbol_to_instrument)
|
|
536
537
|
order = ccxt_convert_order_info(instrument, report)
|
|
537
538
|
deals = ccxt_extract_deals_from_exec(report)
|
|
538
539
|
channel.send((instrument, "order", order, False))
|
|
@@ -541,7 +542,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
541
542
|
|
|
542
543
|
await self._listen_to_stream(
|
|
543
544
|
subscriber=_watch_executions,
|
|
544
|
-
exchange=self.exchange,
|
|
545
|
+
exchange=self.exchange_manager.exchange,
|
|
545
546
|
channel=channel,
|
|
546
547
|
name=name,
|
|
547
548
|
)
|
|
@@ -8,7 +8,7 @@ separating subscription concerns from connection management and data handling.
|
|
|
8
8
|
from collections import defaultdict
|
|
9
9
|
from typing import Dict, List, Set
|
|
10
10
|
|
|
11
|
-
from qubx.core.basics import Instrument
|
|
11
|
+
from qubx.core.basics import DataType, Instrument
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class SubscriptionManager:
|
|
@@ -37,7 +37,7 @@ class SubscriptionManager:
|
|
|
37
37
|
|
|
38
38
|
# Symbol to instrument mapping for quick lookups
|
|
39
39
|
self._symbol_to_instrument: dict[str, Instrument] = {}
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
# Individual stream mappings: {subscription_type: {instrument: stream_name}}
|
|
42
42
|
self._individual_streams: dict[str, dict[Instrument, str]] = defaultdict(dict)
|
|
43
43
|
|
|
@@ -125,7 +125,7 @@ class SubscriptionManager:
|
|
|
125
125
|
|
|
126
126
|
# Clean up name mapping
|
|
127
127
|
self._sub_to_name.pop(subscription_type, None)
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
# Clean up individual stream mappings
|
|
130
130
|
self._individual_streams.pop(subscription_type, None)
|
|
131
131
|
|
|
@@ -163,15 +163,21 @@ class SubscriptionManager:
|
|
|
163
163
|
"""
|
|
164
164
|
if instrument is not None:
|
|
165
165
|
# Return subscriptions (both active and pending) that contain this instrument
|
|
166
|
-
active = [
|
|
167
|
-
|
|
166
|
+
active = [
|
|
167
|
+
sub
|
|
168
|
+
for sub, instrs in self._subscriptions.items()
|
|
169
|
+
if instrument in instrs and self._sub_connection_ready.get(sub, False)
|
|
170
|
+
]
|
|
168
171
|
pending = [sub for sub, instrs in self._pending_subscriptions.items() if instrument in instrs]
|
|
169
172
|
return list(set(active + pending))
|
|
170
173
|
|
|
171
174
|
# Return all subscription types that have any instruments (both active and pending)
|
|
172
175
|
# Only include active subscriptions if connection is ready
|
|
173
|
-
active = [
|
|
174
|
-
|
|
176
|
+
active = [
|
|
177
|
+
sub
|
|
178
|
+
for sub, instruments in self._subscriptions.items()
|
|
179
|
+
if instruments and self._sub_connection_ready.get(sub, False)
|
|
180
|
+
]
|
|
175
181
|
pending = [sub for sub, instruments in self._pending_subscriptions.items() if instruments]
|
|
176
182
|
return list(set(active + pending))
|
|
177
183
|
|
|
@@ -211,17 +217,24 @@ class SubscriptionManager:
|
|
|
211
217
|
|
|
212
218
|
Args:
|
|
213
219
|
instrument: Instrument to check
|
|
214
|
-
subscription_type:
|
|
220
|
+
subscription_type: Base or full subscription type (e.g., "orderbook" or "orderbook(0.0, 20)")
|
|
215
221
|
|
|
216
222
|
Returns:
|
|
217
223
|
True if subscription is active (not just pending)
|
|
218
224
|
"""
|
|
219
|
-
#
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
+
# Get the base type for comparison
|
|
226
|
+
base_type = DataType.from_str(subscription_type)[0]
|
|
227
|
+
|
|
228
|
+
# Check if any subscription with matching base type contains the instrument and is ready
|
|
229
|
+
for stored_sub_type, instruments in self._subscriptions.items():
|
|
230
|
+
if (
|
|
231
|
+
DataType.from_str(stored_sub_type)[0] == base_type
|
|
232
|
+
and instrument in instruments
|
|
233
|
+
and self._sub_connection_ready.get(stored_sub_type, False)
|
|
234
|
+
):
|
|
235
|
+
return True
|
|
236
|
+
|
|
237
|
+
return False
|
|
225
238
|
|
|
226
239
|
def has_pending_subscription(self, instrument: Instrument, subscription_type: str) -> bool:
|
|
227
240
|
"""
|
|
@@ -229,16 +242,24 @@ class SubscriptionManager:
|
|
|
229
242
|
|
|
230
243
|
Args:
|
|
231
244
|
instrument: Instrument to check
|
|
232
|
-
subscription_type:
|
|
245
|
+
subscription_type: Base or full subscription type (e.g., "orderbook" or "orderbook(0.0, 20)")
|
|
233
246
|
|
|
234
247
|
Returns:
|
|
235
248
|
True if subscription is pending (connection being established)
|
|
236
249
|
"""
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
)
|
|
250
|
+
# Get the base type for comparison
|
|
251
|
+
base_type = DataType.from_str(subscription_type)[0]
|
|
252
|
+
|
|
253
|
+
# Check if any pending subscription with matching base type contains the instrument and is not ready
|
|
254
|
+
for stored_sub_type, instruments in self._pending_subscriptions.items():
|
|
255
|
+
if (
|
|
256
|
+
DataType.from_str(stored_sub_type)[0] == base_type
|
|
257
|
+
and instrument in instruments
|
|
258
|
+
and not self._sub_connection_ready.get(stored_sub_type, False)
|
|
259
|
+
):
|
|
260
|
+
return True
|
|
261
|
+
|
|
262
|
+
return False
|
|
242
263
|
|
|
243
264
|
def get_all_subscribed_instruments(self) -> Set[Instrument]:
|
|
244
265
|
"""
|
|
@@ -262,14 +283,14 @@ class SubscriptionManager:
|
|
|
262
283
|
True if connection is established and ready
|
|
263
284
|
"""
|
|
264
285
|
return self._sub_connection_ready.get(subscription_type, False)
|
|
265
|
-
|
|
286
|
+
|
|
266
287
|
def has_subscription_type(self, subscription_type: str) -> bool:
|
|
267
288
|
"""
|
|
268
289
|
Check if a subscription type exists (has any instruments).
|
|
269
|
-
|
|
290
|
+
|
|
270
291
|
Args:
|
|
271
292
|
subscription_type: Full subscription type (e.g., "ohlc(1m)")
|
|
272
|
-
|
|
293
|
+
|
|
273
294
|
Returns:
|
|
274
295
|
True if subscription type has any instruments
|
|
275
296
|
"""
|
|
@@ -283,33 +304,33 @@ class SubscriptionManager:
|
|
|
283
304
|
Dictionary mapping symbols to instruments
|
|
284
305
|
"""
|
|
285
306
|
return self._symbol_to_instrument.copy()
|
|
286
|
-
|
|
307
|
+
|
|
287
308
|
def set_individual_streams(self, subscription_type: str, streams: dict[Instrument, str]) -> None:
|
|
288
309
|
"""
|
|
289
310
|
Store individual stream mappings for a subscription type.
|
|
290
|
-
|
|
311
|
+
|
|
291
312
|
Args:
|
|
292
313
|
subscription_type: Full subscription type (e.g., "ohlc(1m)")
|
|
293
314
|
streams: Dictionary mapping instrument to stream name
|
|
294
315
|
"""
|
|
295
316
|
self._individual_streams[subscription_type] = streams
|
|
296
|
-
|
|
317
|
+
|
|
297
318
|
def get_individual_streams(self, subscription_type: str) -> dict[Instrument, str]:
|
|
298
319
|
"""
|
|
299
320
|
Get individual stream mappings for a subscription type.
|
|
300
|
-
|
|
321
|
+
|
|
301
322
|
Args:
|
|
302
323
|
subscription_type: Full subscription type (e.g., "ohlc(1m)")
|
|
303
|
-
|
|
324
|
+
|
|
304
325
|
Returns:
|
|
305
326
|
Dictionary mapping instrument to stream name
|
|
306
327
|
"""
|
|
307
328
|
return self._individual_streams.get(subscription_type, {})
|
|
308
|
-
|
|
329
|
+
|
|
309
330
|
def clear_individual_streams(self, subscription_type: str) -> None:
|
|
310
331
|
"""
|
|
311
332
|
Clear individual stream mappings for a subscription type.
|
|
312
|
-
|
|
333
|
+
|
|
313
334
|
Args:
|
|
314
335
|
subscription_type: Full subscription type (e.g., "ohlc(1m)")
|
|
315
336
|
"""
|
|
@@ -75,6 +75,9 @@ class BasicAccountProcessor(IAccountProcessor):
|
|
|
75
75
|
def get_positions(self, exchange: str | None = None) -> dict[Instrument, Position]:
|
|
76
76
|
return self._positions
|
|
77
77
|
|
|
78
|
+
def get_fees_calculator(self, exchange: str | None = None) -> TransactionCostsCalculator:
|
|
79
|
+
return self._tcc
|
|
80
|
+
|
|
78
81
|
def get_position(self, instrument: Instrument) -> Position:
|
|
79
82
|
_pos = self._positions.get(instrument)
|
|
80
83
|
if _pos is None:
|
|
@@ -443,6 +446,10 @@ class CompositeAccountProcessor(IAccountProcessor):
|
|
|
443
446
|
exch = self._get_exchange(exchange)
|
|
444
447
|
return self._account_processors[exch].position_report()
|
|
445
448
|
|
|
449
|
+
def get_fees_calculator(self, exchange: str | None = None) -> TransactionCostsCalculator:
|
|
450
|
+
exch = self._get_exchange(exchange)
|
|
451
|
+
return self._account_processors[exch].get_fees_calculator()
|
|
452
|
+
|
|
446
453
|
########################################################
|
|
447
454
|
# Leverage information
|
|
448
455
|
########################################################
|
|
@@ -15,6 +15,7 @@ from qubx.core.basics import (
|
|
|
15
15
|
Order,
|
|
16
16
|
OrderRequest,
|
|
17
17
|
Position,
|
|
18
|
+
RestoredState,
|
|
18
19
|
Signal,
|
|
19
20
|
TargetPosition,
|
|
20
21
|
Timestamped,
|
|
@@ -112,6 +113,7 @@ class StrategyContext(IStrategyContext):
|
|
|
112
113
|
strategy_name: str | None = None,
|
|
113
114
|
strategy_state: StrategyState | None = None,
|
|
114
115
|
health_monitor: IHealthMonitor | None = None,
|
|
116
|
+
restored_state: RestoredState | None = None,
|
|
115
117
|
) -> None:
|
|
116
118
|
self.account = account
|
|
117
119
|
self.strategy = self.__instantiate_strategy(strategy, config)
|
|
@@ -138,6 +140,7 @@ class StrategyContext(IStrategyContext):
|
|
|
138
140
|
self._lifecycle_notifier = lifecycle_notifier
|
|
139
141
|
self._strategy_state = strategy_state if strategy_state is not None else StrategyState()
|
|
140
142
|
self._strategy_name = strategy_name if strategy_name is not None else strategy.__class__.__name__
|
|
143
|
+
self._restored_state = restored_state
|
|
141
144
|
|
|
142
145
|
self._health_monitor = health_monitor or DummyHealthMonitor()
|
|
143
146
|
self.health = self._health_monitor
|
|
@@ -150,6 +153,8 @@ class StrategyContext(IStrategyContext):
|
|
|
150
153
|
if __position_gathering is None:
|
|
151
154
|
__position_gathering = position_gathering if position_gathering is not None else SimplePositionGatherer()
|
|
152
155
|
|
|
156
|
+
__warmup_position_gathering = SimplePositionGatherer()
|
|
157
|
+
|
|
153
158
|
self._subscription_manager = SubscriptionManager(
|
|
154
159
|
data_providers=self._data_providers,
|
|
155
160
|
default_base_subscription=DataType.ORDERBOOK
|
|
@@ -175,9 +180,10 @@ class StrategyContext(IStrategyContext):
|
|
|
175
180
|
time_provider=self,
|
|
176
181
|
account=self.account,
|
|
177
182
|
position_gathering=__position_gathering,
|
|
183
|
+
warmup_position_gathering=__warmup_position_gathering,
|
|
178
184
|
)
|
|
179
185
|
self._trading_manager = TradingManager(
|
|
180
|
-
|
|
186
|
+
context=self,
|
|
181
187
|
brokers=self._brokers,
|
|
182
188
|
account=self.account,
|
|
183
189
|
strategy_name=self._strategy_name,
|
|
@@ -192,6 +198,7 @@ class StrategyContext(IStrategyContext):
|
|
|
192
198
|
account=self.account,
|
|
193
199
|
position_tracker=__position_tracker,
|
|
194
200
|
position_gathering=__position_gathering,
|
|
201
|
+
warmup_position_gathering=__warmup_position_gathering,
|
|
195
202
|
universe_manager=self._universe_manager,
|
|
196
203
|
cache=self._cache,
|
|
197
204
|
scheduler=self._scheduler,
|
|
@@ -455,11 +462,11 @@ class StrategyContext(IStrategyContext):
|
|
|
455
462
|
) -> Order:
|
|
456
463
|
return self._trading_manager.set_target_position(instrument, target, price, **options)
|
|
457
464
|
|
|
458
|
-
def close_position(self, instrument: Instrument) -> None:
|
|
459
|
-
return self._trading_manager.close_position(instrument)
|
|
465
|
+
def close_position(self, instrument: Instrument, without_signals: bool = False) -> None:
|
|
466
|
+
return self._trading_manager.close_position(instrument, without_signals)
|
|
460
467
|
|
|
461
|
-
def close_positions(self, market_type: MarketType | None = None) -> None:
|
|
462
|
-
return self._trading_manager.close_positions(market_type)
|
|
468
|
+
def close_positions(self, market_type: MarketType | None = None, without_signals: bool = False) -> None:
|
|
469
|
+
return self._trading_manager.close_positions(market_type, without_signals)
|
|
463
470
|
|
|
464
471
|
def cancel_order(self, order_id: str, exchange: str | None = None) -> None:
|
|
465
472
|
return self._trading_manager.cancel_order(order_id, exchange)
|
|
@@ -585,6 +592,9 @@ class StrategyContext(IStrategyContext):
|
|
|
585
592
|
def get_warmup_orders(self) -> dict[Instrument, list[Order]]:
|
|
586
593
|
return self._warmup_orders if self._warmup_orders is not None else {}
|
|
587
594
|
|
|
595
|
+
def get_restored_state(self) -> RestoredState | None:
|
|
596
|
+
return self._restored_state
|
|
597
|
+
|
|
588
598
|
# private methods
|
|
589
599
|
def __process_incoming_data_loop(self, channel: CtrlChannel):
|
|
590
600
|
logger.info("[StrategyContext] :: Start processing market data")
|
|
@@ -36,6 +36,7 @@ from qubx.core.basics import (
|
|
|
36
36
|
Signal,
|
|
37
37
|
TargetPosition,
|
|
38
38
|
Timestamped,
|
|
39
|
+
TransactionCostsCalculator,
|
|
39
40
|
TriggerEvent,
|
|
40
41
|
dt_64,
|
|
41
42
|
td_64,
|
|
@@ -153,6 +154,17 @@ class IAccountViewer:
|
|
|
153
154
|
"""
|
|
154
155
|
...
|
|
155
156
|
|
|
157
|
+
def get_fees_calculator(self, exchange: str | None = None) -> TransactionCostsCalculator:
|
|
158
|
+
"""Get the fees calculator.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
exchange: The exchange to get the fees calculator for
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
TransactionCostsCalculator: The transaction costs calculator
|
|
165
|
+
"""
|
|
166
|
+
...
|
|
167
|
+
|
|
156
168
|
@property
|
|
157
169
|
def positions(self) -> dict[Instrument, Position]:
|
|
158
170
|
"""[Deprecated: Use get_positions()] Get all current positions.
|
|
@@ -673,15 +685,16 @@ class ITradingManager:
|
|
|
673
685
|
"""
|
|
674
686
|
...
|
|
675
687
|
|
|
676
|
-
def close_position(self, instrument: Instrument) -> None:
|
|
688
|
+
def close_position(self, instrument: Instrument, without_signals: bool = False) -> None:
|
|
677
689
|
"""Close position for an instrument.
|
|
678
690
|
|
|
679
691
|
Args:
|
|
680
692
|
instrument: The instrument to close position for
|
|
693
|
+
without_signals: If True, trade submitted instead of emitting signal
|
|
681
694
|
"""
|
|
682
695
|
...
|
|
683
696
|
|
|
684
|
-
def close_positions(self, market_type: MarketType | None = None,
|
|
697
|
+
def close_positions(self, market_type: MarketType | None = None, without_signals: bool = False) -> None:
|
|
685
698
|
"""Close all positions."""
|
|
686
699
|
...
|
|
687
700
|
|
|
@@ -1200,6 +1213,10 @@ class IStrategyContext(
|
|
|
1200
1213
|
"""Get the list of exchanges."""
|
|
1201
1214
|
return []
|
|
1202
1215
|
|
|
1216
|
+
def get_restored_state(self) -> "RestoredState | None":
|
|
1217
|
+
"""Get the restored state."""
|
|
1218
|
+
return None
|
|
1219
|
+
|
|
1203
1220
|
|
|
1204
1221
|
class IPositionGathering:
|
|
1205
1222
|
"""
|
|
@@ -1226,6 +1243,28 @@ class IPositionGathering:
|
|
|
1226
1243
|
|
|
1227
1244
|
def on_execution_report(self, ctx: IStrategyContext, instrument: Instrument, deal: Deal): ...
|
|
1228
1245
|
|
|
1246
|
+
def update(self, ctx: IStrategyContext, instrument: Instrument, update: Timestamped) -> None:
|
|
1247
|
+
"""
|
|
1248
|
+
Position gatherer is being updated by new market data.
|
|
1249
|
+
|
|
1250
|
+
Args:
|
|
1251
|
+
ctx: Strategy context object
|
|
1252
|
+
instrument: The instrument for which market data was updated
|
|
1253
|
+
update: The market data update (Quote, Trade, Bar, etc.)
|
|
1254
|
+
"""
|
|
1255
|
+
pass
|
|
1256
|
+
|
|
1257
|
+
def restore_from_target_positions(self, ctx: IStrategyContext, target_positions: list[TargetPosition]) -> None:
|
|
1258
|
+
"""
|
|
1259
|
+
Restore gatherer state from target positions.
|
|
1260
|
+
|
|
1261
|
+
Args:
|
|
1262
|
+
ctx: Strategy context object
|
|
1263
|
+
target_positions: List of target positions to restore gatherer state from
|
|
1264
|
+
"""
|
|
1265
|
+
# Default implementation - subclasses can override if needed
|
|
1266
|
+
pass
|
|
1267
|
+
|
|
1229
1268
|
|
|
1230
1269
|
class IPositionSizer:
|
|
1231
1270
|
"""Interface for calculating target positions from signals."""
|
|
@@ -1307,15 +1346,16 @@ class PositionsTracker:
|
|
|
1307
1346
|
"""
|
|
1308
1347
|
...
|
|
1309
1348
|
|
|
1310
|
-
def
|
|
1349
|
+
def restore_position_from_signals(self, ctx: IStrategyContext, signals: list[Signal]) -> None:
|
|
1311
1350
|
"""
|
|
1312
|
-
Restore
|
|
1351
|
+
Restore tracker state from signals.
|
|
1313
1352
|
|
|
1314
1353
|
Args:
|
|
1315
|
-
|
|
1316
|
-
|
|
1354
|
+
ctx: Strategy context object
|
|
1355
|
+
signals: List of signals to restore tracker state from
|
|
1317
1356
|
"""
|
|
1318
|
-
|
|
1357
|
+
# Default implementation - subclasses can override
|
|
1358
|
+
pass
|
|
1319
1359
|
|
|
1320
1360
|
|
|
1321
1361
|
@dataclass
|
|
@@ -1350,12 +1390,12 @@ class HealthMetrics:
|
|
|
1350
1390
|
@runtime_checkable
|
|
1351
1391
|
class IDataArrivalListener(Protocol):
|
|
1352
1392
|
"""Interface for components that want to be notified of data arrivals."""
|
|
1353
|
-
|
|
1393
|
+
|
|
1354
1394
|
def on_data_arrival(self, event_type: str, event_time: dt_64) -> None:
|
|
1355
1395
|
"""Called when new data arrives.
|
|
1356
|
-
|
|
1396
|
+
|
|
1357
1397
|
Args:
|
|
1358
|
-
event_type: Type of data event (e.g., "ohlcv:BTC/USDT:1m")
|
|
1398
|
+
event_type: Type of data event (e.g., "ohlcv:BTC/USDT:1m")
|
|
1359
1399
|
event_time: Timestamp of the data event
|
|
1360
1400
|
"""
|
|
1361
1401
|
...
|
|
@@ -244,6 +244,7 @@ class SignalsAndTargetsLogger(_BaseIntervalDumper):
|
|
|
244
244
|
"entry_price": t.entry_price,
|
|
245
245
|
"take_price": t.take_price,
|
|
246
246
|
"stop_price": t.stop_price,
|
|
247
|
+
"options": t.options,
|
|
247
248
|
}
|
|
248
249
|
for t in self._targets
|
|
249
250
|
]
|
|
@@ -262,6 +263,7 @@ class SignalsAndTargetsLogger(_BaseIntervalDumper):
|
|
|
262
263
|
"group": s.group,
|
|
263
264
|
"comment": s.comment,
|
|
264
265
|
"service": s.is_service,
|
|
266
|
+
"options": s.options,
|
|
265
267
|
}
|
|
266
268
|
for s in self._signals
|
|
267
269
|
]
|