Qubx 0.7.25__tar.gz → 0.7.35__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.25 → qubx-0.7.35}/PKG-INFO +34 -7
- {qubx-0.7.25 → qubx-0.7.35}/README.md +29 -1
- {qubx-0.7.25 → qubx-0.7.35}/pyproject.toml +6 -5
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/broker.py +52 -12
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/simulator.py +3 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/commands.py +20 -9
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/release.py +9 -10
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/tui.py +0 -3
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/broker.py +176 -37
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/data.py +2 -5
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/ohlc.py +25 -12
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/reader.py +7 -2
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/utils.py +15 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/account.py +159 -109
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/broker.py +108 -174
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/client.py +30 -25
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/constants.py +9 -21
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/data.py +14 -14
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/extensions.py +7 -2
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/factory.py +1 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/handlers/orderbook.py +48 -35
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/nonce.py +9 -1
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/parsers.py +2 -5
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/rate_limits.py +1 -1
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/reader.py +7 -7
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/websocket.py +17 -4
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/account.py +65 -7
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/context.py +41 -9
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/errors.py +3 -1
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/interfaces.py +185 -37
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/metrics.py +6 -1
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/market.py +14 -11
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/processing.py +36 -5
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/subscription.py +1 -1
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/trading.py +101 -51
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/utils.pyi +2 -1
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/readers.py +22 -4
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/transformers.py +39 -6
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/formatters/base.py +6 -5
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/formatters/incremental.py +24 -20
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/formatters/target_position.py +7 -2
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/gathering/simplest.py +21 -7
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/pandaz/__init__.py +18 -16
- {qubx-0.7.25/src/qubx/math → qubx-0.7.35/src/qubx/pandaz}/stats.py +41 -16
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/pandaz/ta.py +2 -2
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restarts/state_resolvers.py +1 -1
- qubx-0.7.35/src/qubx/state/__init__.py +14 -0
- qubx-0.7.35/src/qubx/state/dummy.py +35 -0
- qubx-0.7.35/src/qubx/state/redis.py +170 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/ta/indicators.pxd +14 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/ta/indicators.pyx +102 -1
- qubx-0.7.35/src/qubx/templates/__init__.py +5 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/base.py +41 -42
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/riskctrl.py +2 -2
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/misc.py +9 -4
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/_jupyter_runner.pyt +2 -2
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/configs.py +63 -1
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/factory.py +60 -25
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/runner.py +23 -14
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/__init__.py +3 -1
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/app.py +5 -1
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/init_code.py +4 -3
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/websocket_manager.py +5 -2
- qubx-0.7.25/src/qubx/math/__init__.py +0 -3
- qubx-0.7.25/src/qubx/templates/__init__.py +0 -5
- {qubx-0.7.25 → qubx-0.7.35}/LICENSE +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/build.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/_nb_magic.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/account.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/data.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/management.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/ome.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/optimization.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/runner.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/sentinels.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/simulated_data.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/simulated_exchange.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/transfers.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/utils.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/deploy.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/misc.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/account.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exceptions.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/factory.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/tardis/data.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/tardis/utils.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/handlers/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/handlers/base.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/handlers/stats.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/handlers/trades.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/instruments.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/utils.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/basics.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/detectors/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/detectors/delisting.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/detectors/stale.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/exceptions.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/helpers.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/initializer.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/loggers.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/lookups.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/universe.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/utils.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/series.pxd +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/series.pyi +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/series.pyx +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/utils.pyx +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/composite.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/containers.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/helpers.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/hft.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/registry.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/storage.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/storages/csv.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/storages/questdb.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/storages/utils.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/tardis.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/base.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/composite.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/csv.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/indicator.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/inmemory.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/prometheus.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/questdb.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/composite.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/formatters/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/formatters/slack.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/redis_streams.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/slack.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/health/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/health/base.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/health/dummy.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/loggers/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/loggers/csv.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/loggers/factory.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/loggers/inmemory.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/loggers/mongo.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/notifications/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/notifications/composite.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/notifications/slack.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/notifications/throttler.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/pandaz/utils.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/_build.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/crypto-fees.ini +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restarts/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restarts/time_finders.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/balance.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/factory.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/interfaces.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/position.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/signal.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/state.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/utils.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/ta/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/ta/indicators.pyi +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/accounts.toml.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/config.yml.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/jlive.sh.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/template.yml +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/__init__.py.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/config.yml.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/strategy.py.j2 +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/template.yml +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/advanced.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/composite.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/sizers.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/charting/lookinglass.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/charting/mpl_helpers.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/charting/orderbook.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/collections.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/hft/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/hft/numba_utils.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/hft/orderbook.pyi +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/hft/orderbook.pyx +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/marketdata/ccxt.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/marketdata/dukas.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/nonce.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/ntp.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/numbers_utils.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/orderbook.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/dashboard.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/data.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/interfaces.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/questdb.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/rate_limiter.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/ringbuffer.pxd +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/ringbuffer.pyi +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/ringbuffer.pyx +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/accounts.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/kernel_service.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/handlers.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/kernel.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/styles.tcss +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/slack.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/throttler.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/time.py +0 -0
- {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/version.py +0 -0
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Qubx
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.35
|
|
4
4
|
Summary: Qubx - Quantitative Trading Framework
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Dmitry Marienko
|
|
7
7
|
Author-email: dmitry.marienko@xlydian.com
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.11,<4.0
|
|
9
9
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
@@ -43,15 +42,15 @@ Requires-Dist: python-binance (>=1.0.19,<2.0.0)
|
|
|
43
42
|
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
|
44
43
|
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
45
44
|
Requires-Dist: qubx-bitfinex-api (>=3.0.7,<4.0.0)
|
|
46
|
-
Requires-Dist: qubx-lighter-api (>=0.1.
|
|
45
|
+
Requires-Dist: qubx-lighter-api (>=0.1.5,<0.2.0)
|
|
47
46
|
Requires-Dist: questdb (>=2.0.3,<3.0.0)
|
|
48
47
|
Requires-Dist: redis (>=5.2.1,<6.0.0)
|
|
49
48
|
Requires-Dist: rich (>=13.9.4,<14.0.0)
|
|
50
|
-
Requires-Dist: scikit-learn (>=1.
|
|
49
|
+
Requires-Dist: scikit-learn (>=1.8.0)
|
|
51
50
|
Requires-Dist: scipy (>=1.12.0,<2.0.0)
|
|
52
51
|
Requires-Dist: sortedcontainers (>=2.4.0,<3.0.0)
|
|
53
52
|
Requires-Dist: stackprinter (>=0.2.10,<0.3.0)
|
|
54
|
-
Requires-Dist: statsmodels (>=0.14.
|
|
53
|
+
Requires-Dist: statsmodels (>=0.14.6,<0.15.0)
|
|
55
54
|
Requires-Dist: tabulate (>=0.9.0,<0.10.0)
|
|
56
55
|
Requires-Dist: textual-autocomplete (>=4.0.0,<5.0.0)
|
|
57
56
|
Requires-Dist: textual-serve (>=1.0.0,<2.0.0)
|
|
@@ -70,20 +69,33 @@ Description-Content-Type: text/markdown
|
|
|
70
69
|
```
|
|
71
70
|
⠀⠀⡰⡖⠒⠒⢒⢦⠀⠀
|
|
72
71
|
⠀⢠⠃⠈⢆⣀⣎⣀⣱⡀ QUBX | Quantitative Backtesting Environment
|
|
73
|
-
⠀⢳⠒⠒⡞⠚⡄⠀⡰⠁ (c)
|
|
72
|
+
⠀⢳⠒⠒⡞⠚⡄⠀⡰⠁ (c) 2026, by xLydian
|
|
74
73
|
⠀⠀⠱⣜⣀⣀⣈⣦⠃⠀⠀⠀
|
|
75
74
|
```
|
|
76
75
|
|
|
77
76
|
Qubx is a next-generation quantitative trading framework designed for efficient backtesting and live trading. Built with Python, it offers a robust environment for developing, testing, and deploying trading strategies.
|
|
78
77
|
|
|
78
|
+
**Qubx is under active development.** We are continuously improving the framework and will update our documentation in the coming days/weeks. This will include comprehensive end-to-end examples for running simulations and live trading.
|
|
79
|
+
|
|
80
|
+
### Supported Data Types
|
|
81
|
+
|
|
82
|
+
Qubx supports a wide range of market data:
|
|
83
|
+
- OHLC (candlestick data)
|
|
84
|
+
- L2 Orderbook
|
|
85
|
+
- Liquidations
|
|
86
|
+
- Funding rates
|
|
87
|
+
- And more...
|
|
88
|
+
|
|
79
89
|
## Quick Start
|
|
80
90
|
|
|
81
91
|
### 1. Install Dependencies
|
|
92
|
+
|
|
82
93
|
```bash
|
|
83
94
|
poetry install
|
|
84
95
|
```
|
|
85
96
|
|
|
86
97
|
### 2. Create a Strategy
|
|
98
|
+
|
|
87
99
|
```bash
|
|
88
100
|
# Create a simple strategy template (default)
|
|
89
101
|
poetry run qubx init
|
|
@@ -93,6 +105,7 @@ poetry run qubx init --name my_strategy --symbols BTCUSDT,ETHUSDT
|
|
|
93
105
|
```
|
|
94
106
|
|
|
95
107
|
### 3. Run Your Strategy
|
|
108
|
+
|
|
96
109
|
```bash
|
|
97
110
|
cd my_strategy
|
|
98
111
|
|
|
@@ -104,6 +117,7 @@ poetry run qubx run config.yml --paper
|
|
|
104
117
|
```
|
|
105
118
|
|
|
106
119
|
### Available Templates
|
|
120
|
+
|
|
107
121
|
```bash
|
|
108
122
|
# List available strategy templates
|
|
109
123
|
poetry run qubx init --list-templates
|
|
@@ -113,6 +127,7 @@ poetry run qubx init --template project --name my_project
|
|
|
113
127
|
```
|
|
114
128
|
|
|
115
129
|
### Strategy Development Workflow
|
|
130
|
+
|
|
116
131
|
1. **Initialize**: `poetry run qubx init` - Create strategy from template
|
|
117
132
|
2. **Develop**: Edit `strategy.py` to implement your trading logic
|
|
118
133
|
3. **Test**: `poetry run qubx run config.yml --paper` - Run in paper mode
|
|
@@ -193,6 +208,18 @@ just test
|
|
|
193
208
|
- Build package: `just build`
|
|
194
209
|
- Run verbose tests: `just test-verbose`
|
|
195
210
|
|
|
211
|
+
## In Production
|
|
212
|
+
|
|
213
|
+
Qubx powers the [AllegedAlpha](https://app.lighter.xyz/public-pools/281474976625478) public pool on Lighter. Public pools allow users to deposit funds from their blockchain wallet into a smart contract. The pool operator manages the trading strategy, and a performance fee is taken from profits (X: [@allegedalpha](https://x.com/allegedalpha)).
|
|
214
|
+
|
|
215
|
+
## About xLydian
|
|
216
|
+
|
|
217
|
+
Qubx is developed by [xLydian](https://xlydian.com/).
|
|
218
|
+
|
|
219
|
+
- Website: [xlydian.com](https://xlydian.com/)
|
|
220
|
+
- X: [@xLydian_xyz](https://x.com/xLydian_xyz)
|
|
221
|
+
- Contact: [info@xlydian.com](mailto:info@xlydian.com)
|
|
222
|
+
|
|
196
223
|
## Contributing
|
|
197
224
|
|
|
198
225
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -5,20 +5,33 @@
|
|
|
5
5
|
```
|
|
6
6
|
⠀⠀⡰⡖⠒⠒⢒⢦⠀⠀
|
|
7
7
|
⠀⢠⠃⠈⢆⣀⣎⣀⣱⡀ QUBX | Quantitative Backtesting Environment
|
|
8
|
-
⠀⢳⠒⠒⡞⠚⡄⠀⡰⠁ (c)
|
|
8
|
+
⠀⢳⠒⠒⡞⠚⡄⠀⡰⠁ (c) 2026, by xLydian
|
|
9
9
|
⠀⠀⠱⣜⣀⣀⣈⣦⠃⠀⠀⠀
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
Qubx is a next-generation quantitative trading framework designed for efficient backtesting and live trading. Built with Python, it offers a robust environment for developing, testing, and deploying trading strategies.
|
|
13
13
|
|
|
14
|
+
**Qubx is under active development.** We are continuously improving the framework and will update our documentation in the coming days/weeks. This will include comprehensive end-to-end examples for running simulations and live trading.
|
|
15
|
+
|
|
16
|
+
### Supported Data Types
|
|
17
|
+
|
|
18
|
+
Qubx supports a wide range of market data:
|
|
19
|
+
- OHLC (candlestick data)
|
|
20
|
+
- L2 Orderbook
|
|
21
|
+
- Liquidations
|
|
22
|
+
- Funding rates
|
|
23
|
+
- And more...
|
|
24
|
+
|
|
14
25
|
## Quick Start
|
|
15
26
|
|
|
16
27
|
### 1. Install Dependencies
|
|
28
|
+
|
|
17
29
|
```bash
|
|
18
30
|
poetry install
|
|
19
31
|
```
|
|
20
32
|
|
|
21
33
|
### 2. Create a Strategy
|
|
34
|
+
|
|
22
35
|
```bash
|
|
23
36
|
# Create a simple strategy template (default)
|
|
24
37
|
poetry run qubx init
|
|
@@ -28,6 +41,7 @@ poetry run qubx init --name my_strategy --symbols BTCUSDT,ETHUSDT
|
|
|
28
41
|
```
|
|
29
42
|
|
|
30
43
|
### 3. Run Your Strategy
|
|
44
|
+
|
|
31
45
|
```bash
|
|
32
46
|
cd my_strategy
|
|
33
47
|
|
|
@@ -39,6 +53,7 @@ poetry run qubx run config.yml --paper
|
|
|
39
53
|
```
|
|
40
54
|
|
|
41
55
|
### Available Templates
|
|
56
|
+
|
|
42
57
|
```bash
|
|
43
58
|
# List available strategy templates
|
|
44
59
|
poetry run qubx init --list-templates
|
|
@@ -48,6 +63,7 @@ poetry run qubx init --template project --name my_project
|
|
|
48
63
|
```
|
|
49
64
|
|
|
50
65
|
### Strategy Development Workflow
|
|
66
|
+
|
|
51
67
|
1. **Initialize**: `poetry run qubx init` - Create strategy from template
|
|
52
68
|
2. **Develop**: Edit `strategy.py` to implement your trading logic
|
|
53
69
|
3. **Test**: `poetry run qubx run config.yml --paper` - Run in paper mode
|
|
@@ -128,6 +144,18 @@ just test
|
|
|
128
144
|
- Build package: `just build`
|
|
129
145
|
- Run verbose tests: `just test-verbose`
|
|
130
146
|
|
|
147
|
+
## In Production
|
|
148
|
+
|
|
149
|
+
Qubx powers the [AllegedAlpha](https://app.lighter.xyz/public-pools/281474976625478) public pool on Lighter. Public pools allow users to deposit funds from their blockchain wallet into a smart contract. The pool operator manages the trading strategy, and a performance fee is taken from profits (X: [@allegedalpha](https://x.com/allegedalpha)).
|
|
150
|
+
|
|
151
|
+
## About xLydian
|
|
152
|
+
|
|
153
|
+
Qubx is developed by [xLydian](https://xlydian.com/).
|
|
154
|
+
|
|
155
|
+
- Website: [xlydian.com](https://xlydian.com/)
|
|
156
|
+
- X: [@xLydian_xyz](https://x.com/xLydian_xyz)
|
|
157
|
+
- Contact: [info@xlydian.com](mailto:info@xlydian.com)
|
|
158
|
+
|
|
131
159
|
## Contributing
|
|
132
160
|
|
|
133
161
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "Qubx"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.35"
|
|
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"
|
|
@@ -32,13 +32,13 @@ generate-setup-file = false
|
|
|
32
32
|
qubx = "qubx.cli.commands:main"
|
|
33
33
|
|
|
34
34
|
[tool.poetry.dependencies]
|
|
35
|
-
python = ">=3.
|
|
35
|
+
python = ">=3.11,<4.0"
|
|
36
36
|
numpy = "^1.26.3"
|
|
37
37
|
pandas = "^2.2.2"
|
|
38
38
|
pyarrow = "^15.0.0"
|
|
39
39
|
scipy = "^1.12.0"
|
|
40
|
-
scikit-learn = "
|
|
41
|
-
statsmodels = "^0.14.
|
|
40
|
+
scikit-learn = ">=1.8.0"
|
|
41
|
+
statsmodels = "^0.14.6"
|
|
42
42
|
numba = "^0.59.1"
|
|
43
43
|
sortedcontainers = "^2.4.0"
|
|
44
44
|
ntplib = "^0.4.0"
|
|
@@ -78,7 +78,7 @@ textual-autocomplete = "^4.0.0"
|
|
|
78
78
|
textual-serve = "^1.0.0"
|
|
79
79
|
rich = "^13.9.4"
|
|
80
80
|
jinja2 = "^3.1.0"
|
|
81
|
-
qubx-lighter-api = "^0.1.
|
|
81
|
+
qubx-lighter-api = "^0.1.5"
|
|
82
82
|
uvloop = "^0.22.1"
|
|
83
83
|
|
|
84
84
|
[tool.ruff.lint]
|
|
@@ -120,6 +120,7 @@ jinja2 = "3.1.5"
|
|
|
120
120
|
mike = "2.1.3"
|
|
121
121
|
mkdocs-jupyter = "0.25.1"
|
|
122
122
|
debugpy = "^1.8.12"
|
|
123
|
+
py-spy = "^0.4.1"
|
|
123
124
|
|
|
124
125
|
[tool.poetry.group.test.dependencies]
|
|
125
126
|
pytest-asyncio = "^0.24.0"
|
|
@@ -51,29 +51,51 @@ class SimulatedBroker(IBroker):
|
|
|
51
51
|
)
|
|
52
52
|
return report.order
|
|
53
53
|
|
|
54
|
-
def send_order_async(self, request: OrderRequest) -> None:
|
|
54
|
+
def send_order_async(self, request: OrderRequest) -> str | None:
|
|
55
55
|
"""Submit order asynchronously (same as sync in simulation)."""
|
|
56
56
|
self.send_order(request)
|
|
57
|
+
return request.client_id
|
|
57
58
|
|
|
58
|
-
def
|
|
59
|
+
def _validate_order_ids(self, order_id: str | None, client_order_id: str | None) -> None:
|
|
60
|
+
if (order_id is None and client_order_id is None) or (order_id is not None and client_order_id is not None):
|
|
61
|
+
raise ValueError("Exactly one of order_id or client_order_id must be provided")
|
|
62
|
+
|
|
63
|
+
def _resolve_order_id(self, order_id: str | None, client_order_id: str | None) -> str | None:
|
|
64
|
+
if order_id is not None:
|
|
65
|
+
return order_id
|
|
66
|
+
if client_order_id is not None:
|
|
67
|
+
order = self._account.find_order_by_client_id(client_order_id)
|
|
68
|
+
return order.id if order is not None else None
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
def cancel_order(self, order_id: str | None = None, client_order_id: str | None = None) -> bool:
|
|
59
72
|
"""Cancel an order synchronously and return success status."""
|
|
73
|
+
self._validate_order_ids(order_id, client_order_id)
|
|
74
|
+
resolved_id = self._resolve_order_id(order_id, client_order_id)
|
|
75
|
+
if resolved_id is None:
|
|
76
|
+
return False
|
|
60
77
|
try:
|
|
61
|
-
self._send_execution_report(order_update := self._exchange.cancel_order(
|
|
78
|
+
self._send_execution_report(order_update := self._exchange.cancel_order(resolved_id))
|
|
62
79
|
return order_update is not None
|
|
63
80
|
except OrderNotFound:
|
|
64
81
|
# Order was already cancelled or doesn't exist
|
|
65
|
-
logger.debug(f"Order {
|
|
82
|
+
logger.debug(f"Order {resolved_id} not found")
|
|
66
83
|
return False
|
|
67
84
|
|
|
68
|
-
def cancel_order_async(self, order_id: str) -> None:
|
|
85
|
+
def cancel_order_async(self, order_id: str | None = None, client_order_id: str | None = None) -> None:
|
|
69
86
|
"""Cancel an order asynchronously (fire-and-forget)."""
|
|
70
|
-
|
|
71
|
-
self.
|
|
87
|
+
self._validate_order_ids(order_id, client_order_id)
|
|
88
|
+
resolved_id = self._resolve_order_id(order_id, client_order_id)
|
|
89
|
+
if resolved_id is None:
|
|
90
|
+
return
|
|
91
|
+
self.cancel_order(order_id=resolved_id)
|
|
72
92
|
|
|
73
93
|
def cancel_orders(self, instrument: Instrument) -> None:
|
|
74
94
|
raise NotImplementedError("Not implemented yet")
|
|
75
95
|
|
|
76
|
-
def update_order(
|
|
96
|
+
def update_order(
|
|
97
|
+
self, price: float, amount: float, order_id: str | None = None, client_order_id: str | None = None
|
|
98
|
+
) -> Order:
|
|
77
99
|
"""Update an existing limit order using cancel+recreate strategy.
|
|
78
100
|
|
|
79
101
|
Args:
|
|
@@ -88,19 +110,25 @@ class SimulatedBroker(IBroker):
|
|
|
88
110
|
OrderNotFound: If the order is not found
|
|
89
111
|
BadRequest: If the order is not a limit order
|
|
90
112
|
"""
|
|
113
|
+
self._validate_order_ids(order_id, client_order_id)
|
|
114
|
+
resolved_id = self._resolve_order_id(order_id, client_order_id)
|
|
115
|
+
if resolved_id is None:
|
|
116
|
+
raise OrderNotFound(f"Order {order_id or client_order_id} not found")
|
|
117
|
+
|
|
91
118
|
# Get the existing order from account
|
|
92
119
|
active_orders = self._account.get_orders()
|
|
93
|
-
existing_order = active_orders.get(
|
|
120
|
+
existing_order = active_orders.get(resolved_id)
|
|
94
121
|
if not existing_order:
|
|
95
|
-
raise OrderNotFound(f"Order {
|
|
122
|
+
raise OrderNotFound(f"Order {resolved_id} not found")
|
|
96
123
|
|
|
97
124
|
# Validate that it's a limit order
|
|
98
125
|
if existing_order.type != "LIMIT":
|
|
99
126
|
raise BadRequest(
|
|
100
|
-
f"Order {
|
|
127
|
+
f"Order {resolved_id} is not a limit order (type: {existing_order.type}). "
|
|
128
|
+
"Only limit orders can be updated."
|
|
101
129
|
)
|
|
102
130
|
|
|
103
|
-
self.cancel_order(order_id)
|
|
131
|
+
self.cancel_order(order_id=resolved_id)
|
|
104
132
|
|
|
105
133
|
request = OrderRequest(
|
|
106
134
|
instrument=existing_order.instrument,
|
|
@@ -109,6 +137,7 @@ class SimulatedBroker(IBroker):
|
|
|
109
137
|
order_type="LIMIT",
|
|
110
138
|
side=existing_order.side,
|
|
111
139
|
time_in_force=existing_order.time_in_force or "gtc",
|
|
140
|
+
client_id=existing_order.client_id,
|
|
112
141
|
options={},
|
|
113
142
|
)
|
|
114
143
|
|
|
@@ -116,6 +145,17 @@ class SimulatedBroker(IBroker):
|
|
|
116
145
|
|
|
117
146
|
return updated_order
|
|
118
147
|
|
|
148
|
+
def update_order_async(
|
|
149
|
+
self, price: float, amount: float, order_id: str | None = None, client_order_id: str | None = None
|
|
150
|
+
) -> str | None:
|
|
151
|
+
"""Update order asynchronously (same as sync in simulation)."""
|
|
152
|
+
self._validate_order_ids(order_id, client_order_id)
|
|
153
|
+
resolved_id = self._resolve_order_id(order_id, client_order_id)
|
|
154
|
+
if resolved_id is None:
|
|
155
|
+
return None
|
|
156
|
+
updated_order = self.update_order(order_id=resolved_id, price=price, amount=amount)
|
|
157
|
+
return updated_order.client_id
|
|
158
|
+
|
|
119
159
|
def _send_execution_report(self, report: SimulatedExecutionReport | None):
|
|
120
160
|
if report is None:
|
|
121
161
|
return
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from importlib.metadata import version
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
import click
|
|
@@ -8,6 +9,7 @@ from qubx import QubxLogConfig, logger
|
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@click.group()
|
|
12
|
+
@click.version_option(version=version("qubx"), prog_name="qubx")
|
|
11
13
|
@click.option(
|
|
12
14
|
"--debug",
|
|
13
15
|
"-d",
|
|
@@ -101,6 +103,9 @@ def main(debug: bool, debug_port: int, log_level: str):
|
|
|
101
103
|
"--restore", "-r", is_flag=True, default=False, help="Restore strategy state from previous run.", show_default=True
|
|
102
104
|
)
|
|
103
105
|
@click.option("--no-color", is_flag=True, default=False, help="Disable colored logging output.", show_default=True)
|
|
106
|
+
@click.option(
|
|
107
|
+
"--dev", is_flag=True, default=False, help="Enable dev mode (adds ~/projects to path).", show_default=True
|
|
108
|
+
)
|
|
104
109
|
def run(
|
|
105
110
|
config_file: Path,
|
|
106
111
|
account_file: Path | None,
|
|
@@ -115,6 +120,7 @@ def run(
|
|
|
115
120
|
connect: Path | None,
|
|
116
121
|
restore: bool,
|
|
117
122
|
no_color: bool,
|
|
123
|
+
dev: bool,
|
|
118
124
|
):
|
|
119
125
|
"""
|
|
120
126
|
Starts the strategy with the given configuration file. If paper mode is enabled, account is not required.
|
|
@@ -133,16 +139,16 @@ def run(
|
|
|
133
139
|
click.echo("Error: --jupyter and --textual cannot be used together.", err=True)
|
|
134
140
|
raise click.Abort()
|
|
135
141
|
|
|
142
|
+
if dev:
|
|
143
|
+
add_project_to_system_path() # Adds ~/projects in dev mode
|
|
144
|
+
add_project_to_system_path(config_file.parent)
|
|
145
|
+
|
|
136
146
|
# Handle --kernel-only mode
|
|
137
147
|
if kernel_only:
|
|
138
148
|
import asyncio
|
|
139
149
|
|
|
140
150
|
from qubx.utils.runner.kernel_service import KernelService
|
|
141
151
|
|
|
142
|
-
add_project_to_system_path()
|
|
143
|
-
add_project_to_system_path(str(config_file.parent.parent))
|
|
144
|
-
add_project_to_system_path(str(config_file.parent))
|
|
145
|
-
|
|
146
152
|
click.echo("Starting persistent kernel...")
|
|
147
153
|
connection_file = asyncio.run(KernelService.start(config_file, account_file, paper, restore))
|
|
148
154
|
click.echo(click.style("✓ Kernel started successfully!", fg="green", bold=True))
|
|
@@ -167,15 +173,20 @@ def run(
|
|
|
167
173
|
click.echo("Kernel stopped.")
|
|
168
174
|
return
|
|
169
175
|
|
|
170
|
-
add_project_to_system_path()
|
|
171
|
-
add_project_to_system_path(str(config_file.parent.parent))
|
|
172
|
-
add_project_to_system_path(str(config_file.parent))
|
|
173
|
-
|
|
174
176
|
if jupyter:
|
|
175
177
|
run_strategy_yaml_in_jupyter(config_file, account_file, paper, restore)
|
|
176
178
|
elif textual:
|
|
177
179
|
run_strategy_yaml_in_textual(
|
|
178
|
-
config_file,
|
|
180
|
+
config_file,
|
|
181
|
+
account_file,
|
|
182
|
+
paper,
|
|
183
|
+
restore,
|
|
184
|
+
textual_dev,
|
|
185
|
+
textual_web,
|
|
186
|
+
textual_port,
|
|
187
|
+
textual_host,
|
|
188
|
+
connect,
|
|
189
|
+
dev,
|
|
179
190
|
)
|
|
180
191
|
else:
|
|
181
192
|
logo()
|
|
@@ -463,6 +463,7 @@ def release_strategy(
|
|
|
463
463
|
git_info=_git_info,
|
|
464
464
|
pyproject_root=pyproject_root,
|
|
465
465
|
output_dir=output_dir,
|
|
466
|
+
config_file=config_file,
|
|
466
467
|
)
|
|
467
468
|
except ValueError as e:
|
|
468
469
|
logger.error(f"<r>{str(e)}</r>")
|
|
@@ -475,6 +476,7 @@ def create_released_pack(
|
|
|
475
476
|
git_info: ReleaseInfo,
|
|
476
477
|
pyproject_root: str,
|
|
477
478
|
output_dir: str,
|
|
479
|
+
config_file: str,
|
|
478
480
|
):
|
|
479
481
|
"""
|
|
480
482
|
Create a release package for a strategy.
|
|
@@ -484,7 +486,7 @@ def create_released_pack(
|
|
|
484
486
|
git_info: Git release information
|
|
485
487
|
pyproject_root: Path to the pyproject.toml root directory
|
|
486
488
|
output_dir: Output directory for the release package
|
|
487
|
-
|
|
489
|
+
config_file: Path to the original config file to copy
|
|
488
490
|
"""
|
|
489
491
|
logger.info(f"Creating release pack for {git_info.tag} ...")
|
|
490
492
|
|
|
@@ -497,8 +499,8 @@ def create_released_pack(
|
|
|
497
499
|
_copy_strategy_file(sc.path, pyproject_root, release_dir)
|
|
498
500
|
_copy_dependencies(sc.path, pyproject_root, release_dir)
|
|
499
501
|
|
|
500
|
-
# Save configuration
|
|
501
|
-
_save_strategy_config(
|
|
502
|
+
# Save configuration (copy original file to preserve comments and structure)
|
|
503
|
+
_save_strategy_config(config_file, release_dir)
|
|
502
504
|
|
|
503
505
|
# Create metadata
|
|
504
506
|
_create_metadata(stg_name, git_info, release_dir)
|
|
@@ -512,14 +514,11 @@ def create_released_pack(
|
|
|
512
514
|
logger.info(f"Created release pack: {os.path.join(output_dir, git_info.tag)}.zip")
|
|
513
515
|
|
|
514
516
|
|
|
515
|
-
def _save_strategy_config(
|
|
516
|
-
"""
|
|
517
|
+
def _save_strategy_config(config_file: str, release_dir: str) -> None:
|
|
518
|
+
"""Copy the original strategy configuration file to preserve comments and structure."""
|
|
517
519
|
config_path = os.path.join(release_dir, "config.yml")
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
config_dict = strategy_config.model_dump()
|
|
521
|
-
yaml.safe_dump(config_dict, fs, sort_keys=False, indent=2)
|
|
522
|
-
logger.debug(f"Saved strategy config to {config_path}")
|
|
520
|
+
shutil.copy2(config_file, config_path)
|
|
521
|
+
logger.debug(f"Copied strategy config to {config_path}")
|
|
523
522
|
|
|
524
523
|
|
|
525
524
|
def _copy_strategy_file(strategy_path: str, pyproject_root: str, release_dir: str) -> None:
|