Qubx 1.1.1.dev1__tar.gz → 1.1.1.dev2__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-1.1.1.dev1 → qubx-1.1.1.dev2}/PKG-INFO +1 -1
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/_version.py +2 -2
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/management.py +42 -12
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/commands.py +1 -10
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/tui.py +5 -9
- qubx-1.1.1.dev2/src/qubx/templates/repo/.claude/skills/simulation-explorer/SKILL.md +196 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/.gitignore +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/LICENSE +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/README.md +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/pyproject.toml +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/scripts/build.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/_nb_magic.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/account.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/broker.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/data.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/iteratedstream.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/ome.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/optimization.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/runner.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/sentinels.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/simulated_data.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/simulated_exchange.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/simulator.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/transfers.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/utils.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/deploy.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/misc.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/release.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/resolver.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/s3.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/theme.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/user_config.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/config.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/account.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/broker.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/data.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exceptions.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/okx/account.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/okx/broker.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/factory.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/utils.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/registry.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/tardis/data.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/tardis/utils.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/account.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/basics.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/context.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/detectors/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/detectors/delisting.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/detectors/stale.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/errors.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/exceptions.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/helpers.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/initializer.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/interfaces.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/loggers.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/lookups.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/metrics.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/market.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/processing.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/subscription.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/trading.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/universe.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/utils.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/series.pxd +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/series.pyi +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/series.pyx +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/utils.pyi +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/utils.pyx +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/cache.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/containers.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/guards.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/registry.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storage.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/ccxt.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/csv.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/handy.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/multi.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/questdb.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/stub.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/utils.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/transformers.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/base.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/composite.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/csv.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/indicator.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/inmemory.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/prometheus.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/questdb.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/composite.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/formatters/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/formatters/base.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/formatters/incremental.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/formatters/slack.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/formatters/target_position.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/redis_streams.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/slack.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/gathering/simplest.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/health/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/health/base.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/health/dummy.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/health/server.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/loggers/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/loggers/csv.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/loggers/factory.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/loggers/inmemory.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/loggers/mongo.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/notifications/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/notifications/composite.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/notifications/slack.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/notifications/throttler.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/pandaz/stats.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/pandaz/ta.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/pandaz/utils.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/plugins/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/plugins/loader.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/_build.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/crypto-fees.ini +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restarts/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restarts/state_resolvers.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restarts/time_finders.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/balance.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/factory.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/interfaces.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/position.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/signal.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/state.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/utils.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/state/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/state/dummy.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/state/redis.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/ta/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/ta/indicators.pxd +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/ta/indicators.pyi +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/ta/indicators.pyx +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/base.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/accounts.toml.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/config.yml.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/jlive.sh.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/template.yml +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.gitignore.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.python-version +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.vscode/launch.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.vscode/settings.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.vscode/tasks.json +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/CLAUDE.md.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/README.md.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/configs/.gitkeep +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/justfile.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/pyproject.toml.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/research/.gitkeep +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/template.yml +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/tests/test_strategy.py.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/__init__.py.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/config.yml.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/strategy.py.j2 +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/template.yml +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/advanced.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/composite.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/riskctrl.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/sizers.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/charting/lookinglass.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/charting/mpl_helpers.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/charting/orderbook.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/collections.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/hft/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/hft/numba_utils.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/hft/orderbook.pyi +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/hft/orderbook.pyx +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/marketdata/ccxt.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/marketdata/dukas.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/misc.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/nonce.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/ntp.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/numbers_utils.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/orderbook.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/dashboard.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/data.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/interfaces.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/questdb.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/rate_limiter.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/results.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/ringbuffer.pxd +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/ringbuffer.pyi +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/ringbuffer.pyx +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/accounts.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/configs.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/factory.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/kernel_service.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/runner.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/app.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/handlers.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/init_code.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/kernel.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/styles.tcss +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/s3.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/slack.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/throttler.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/time.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/websocket_manager.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
- {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/tests/strategies/obi_trader/src/obi_trader/models/obi_trader.py +0 -0
|
@@ -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 = '1.1.1.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 1, 1, '
|
|
31
|
+
__version__ = version = '1.1.1.dev2'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 1, 1, 'dev2')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -50,7 +50,7 @@ import pandas as pd
|
|
|
50
50
|
from qubx.core.metrics import TradingSessionResult
|
|
51
51
|
from qubx.utils.misc import blue, cyan, green, magenta, red, yellow
|
|
52
52
|
from qubx.utils.results import SimulationResultsSaver
|
|
53
|
-
from qubx.utils.s3 import S3Client, is_cloud_path
|
|
53
|
+
from qubx.utils.s3 import S3Client, is_account_uri, is_cloud_path
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
class BacktestStorage:
|
|
@@ -105,8 +105,11 @@ class BacktestStorage:
|
|
|
105
105
|
Initialize BacktestStorage.
|
|
106
106
|
|
|
107
107
|
Args:
|
|
108
|
-
base_path: Root path for backtest storage
|
|
109
|
-
|
|
108
|
+
base_path: Root path for backtest storage. Supports:
|
|
109
|
+
- Local directory: ``"/backtests/"``
|
|
110
|
+
- S3 URI: ``"s3://bucket/path"``
|
|
111
|
+
- Account URI: ``"r2:backtests"`` (resolved via ~/.qubx/config.json)
|
|
112
|
+
storage_options: Cloud storage credentials dict. Overrides account lookup.
|
|
110
113
|
"""
|
|
111
114
|
try:
|
|
112
115
|
import duckdb
|
|
@@ -117,6 +120,13 @@ class BacktestStorage:
|
|
|
117
120
|
"duckdb is required for BacktestStorage. "
|
|
118
121
|
"Install with: pip install 'qubx[storage]' or pip install duckdb"
|
|
119
122
|
)
|
|
123
|
+
|
|
124
|
+
# Resolve account URI (e.g., "r2:backtests") to S3 path + credentials
|
|
125
|
+
if is_account_uri(base_path) and storage_options is None:
|
|
126
|
+
client, s3_path = S3Client.from_uri(base_path)
|
|
127
|
+
storage_options = client.storage_options
|
|
128
|
+
base_path = f"s3://{s3_path}"
|
|
129
|
+
|
|
120
130
|
self.base_path = base_path.rstrip("/") + "/"
|
|
121
131
|
self._is_cloud = is_cloud_path(base_path)
|
|
122
132
|
|
|
@@ -134,17 +144,37 @@ class BacktestStorage:
|
|
|
134
144
|
self._conn.execute("INSTALL httpfs; LOAD httpfs;")
|
|
135
145
|
|
|
136
146
|
opts = self._storage_options or {}
|
|
137
|
-
if "key" in opts:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
if not opts or "key" not in opts:
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
# Resolve region
|
|
151
|
+
region = "auto"
|
|
152
|
+
if "client_kwargs" in opts:
|
|
153
|
+
region = opts["client_kwargs"].get("region_name", "auto")
|
|
154
|
+
elif "region" in opts:
|
|
155
|
+
region = opts["region"]
|
|
156
|
+
|
|
157
|
+
# Build CREATE SECRET with scope matching our base_path so it takes
|
|
158
|
+
# priority over any pre-existing broader secrets (e.g. from duckdb config).
|
|
159
|
+
scope = self.base_path.rstrip("/")
|
|
160
|
+
endpoint_clause = ""
|
|
161
|
+
url_style_clause = ""
|
|
141
162
|
if "endpoint_url" in opts:
|
|
142
163
|
endpoint = opts["endpoint_url"].removeprefix("https://").removeprefix("http://")
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
164
|
+
endpoint_clause = f"ENDPOINT '{endpoint}',"
|
|
165
|
+
url_style_clause = "URL_STYLE 'path',"
|
|
166
|
+
|
|
167
|
+
self._conn.execute(f"""
|
|
168
|
+
CREATE OR REPLACE SECRET qubx_s3 (
|
|
169
|
+
TYPE s3,
|
|
170
|
+
KEY_ID '{opts['key']}',
|
|
171
|
+
SECRET '{opts['secret']}',
|
|
172
|
+
{endpoint_clause}
|
|
173
|
+
{url_style_clause}
|
|
174
|
+
REGION '{region}',
|
|
175
|
+
SCOPE '{scope}'
|
|
176
|
+
)
|
|
177
|
+
""")
|
|
148
178
|
|
|
149
179
|
def _glob(self, filename: str) -> str:
|
|
150
180
|
"""
|
|
@@ -501,22 +501,13 @@ def browse(results_path: str | None):
|
|
|
501
501
|
qubx browse s3://my-bucket/backtests/
|
|
502
502
|
"""
|
|
503
503
|
from qubx.config import get_settings
|
|
504
|
-
from qubx.utils.s3 import S3Client, is_account_uri, is_cloud_path
|
|
505
504
|
|
|
506
505
|
from .tui import run_backtest_browser
|
|
507
506
|
|
|
508
507
|
if results_path is None:
|
|
509
508
|
results_path = get_settings().default_browse_path or "results"
|
|
510
509
|
|
|
511
|
-
|
|
512
|
-
if is_account_uri(results_path):
|
|
513
|
-
client, s3_path = S3Client.from_uri(results_path)
|
|
514
|
-
storage_options = client.storage_options
|
|
515
|
-
results_path = f"s3://{s3_path}"
|
|
516
|
-
elif not is_cloud_path(results_path):
|
|
517
|
-
results_path = os.path.abspath(os.path.expanduser(results_path))
|
|
518
|
-
|
|
519
|
-
run_backtest_browser(results_path, storage_options=storage_options)
|
|
510
|
+
run_backtest_browser(results_path)
|
|
520
511
|
|
|
521
512
|
|
|
522
513
|
@main.command()
|
|
@@ -450,12 +450,10 @@ class BacktestResultsTree(Tree[BacktestTreeNode]):
|
|
|
450
450
|
def __init__(
|
|
451
451
|
self,
|
|
452
452
|
root_path: str,
|
|
453
|
-
storage_options: dict | None = None,
|
|
454
453
|
on_loaded: "callable | None" = None,
|
|
455
454
|
):
|
|
456
455
|
self.root_path = root_path
|
|
457
|
-
self.
|
|
458
|
-
self.storage = BacktestStorage(root_path, storage_options=storage_options)
|
|
456
|
+
self.storage = BacktestStorage(root_path)
|
|
459
457
|
self._root_data = BacktestTreeNode("Backtest Results", root_path, self.storage)
|
|
460
458
|
self._on_loaded = on_loaded
|
|
461
459
|
super().__init__(self._root_data.name, data=self._root_data)
|
|
@@ -1073,10 +1071,9 @@ class BacktestBrowserApp(App):
|
|
|
1073
1071
|
}
|
|
1074
1072
|
"""
|
|
1075
1073
|
|
|
1076
|
-
def __init__(self, root_path: str
|
|
1074
|
+
def __init__(self, root_path: str):
|
|
1077
1075
|
super().__init__()
|
|
1078
1076
|
self.root_path = root_path
|
|
1079
|
-
self.storage_options = storage_options
|
|
1080
1077
|
self.current_results: list[dict[str, Any]] = []
|
|
1081
1078
|
self.current_storage: BacktestStorage | None = None
|
|
1082
1079
|
self._selected_result: dict[str, Any] | None = None
|
|
@@ -1099,7 +1096,7 @@ class BacktestBrowserApp(App):
|
|
|
1099
1096
|
with Horizontal():
|
|
1100
1097
|
with Vertical(id="tree-container", classes="box"):
|
|
1101
1098
|
yield Label("Backtest Results Tree")
|
|
1102
|
-
yield BacktestResultsTree(self.root_path
|
|
1099
|
+
yield BacktestResultsTree(self.root_path)
|
|
1103
1100
|
|
|
1104
1101
|
with Vertical(id="content-container", classes="box"):
|
|
1105
1102
|
with Horizontal(id="controls"):
|
|
@@ -1273,7 +1270,6 @@ class BacktestBrowserApp(App):
|
|
|
1273
1270
|
|
|
1274
1271
|
new_tree = BacktestResultsTree(
|
|
1275
1272
|
self.root_path,
|
|
1276
|
-
storage_options=self.storage_options,
|
|
1277
1273
|
on_loaded=self._restore_tree_state,
|
|
1278
1274
|
)
|
|
1279
1275
|
tree_container.mount(new_tree)
|
|
@@ -1564,10 +1560,10 @@ class BacktestBrowserApp(App):
|
|
|
1564
1560
|
self.copy_to_clipboard(str(load_id))
|
|
1565
1561
|
self.notify(f"Copied: {load_id}", timeout=3)
|
|
1566
1562
|
|
|
1567
|
-
def run_backtest_browser(root_path: str
|
|
1563
|
+
def run_backtest_browser(root_path: str):
|
|
1568
1564
|
"""Run the backtest browser TUI application"""
|
|
1569
1565
|
try:
|
|
1570
|
-
app = BacktestBrowserApp(root_path
|
|
1566
|
+
app = BacktestBrowserApp(root_path)
|
|
1571
1567
|
app.run()
|
|
1572
1568
|
except Exception as e:
|
|
1573
1569
|
logger.error(f"Failed to start backtest browser: {e}")
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: simulation-explorer
|
|
3
|
+
description: Use when loading, comparing, or debugging backtest simulation results — tearsheets, execution logs, portfolio analysis, log inspection, and diagnosing data issues.
|
|
4
|
+
argument-hint: [backtest_path_or_paths] [question]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Simulation Explorer
|
|
8
|
+
|
|
9
|
+
You help load, compare, analyze, and debug backtest simulation results produced by `qubx simulate`.
|
|
10
|
+
|
|
11
|
+
## Loading Results
|
|
12
|
+
|
|
13
|
+
### From Python (notebook or script):
|
|
14
|
+
```python
|
|
15
|
+
from qubx.backtester.management import BacktestStorage
|
|
16
|
+
from qubx.core.metrics import tearsheet
|
|
17
|
+
|
|
18
|
+
# Account URI — uses named S3 account from ~/.qubx/config.json
|
|
19
|
+
bs = BacktestStorage("r2:backtests")
|
|
20
|
+
|
|
21
|
+
# Also supports direct S3 or local paths
|
|
22
|
+
bs = BacktestStorage("s3://my-bucket/backtests")
|
|
23
|
+
bs = BacktestStorage("results") # local directory
|
|
24
|
+
|
|
25
|
+
# Load a specific run
|
|
26
|
+
r = bs.load("STRATEGY_CLASS/run_name/YYYYMMDD_HHMMSS")
|
|
27
|
+
|
|
28
|
+
# Compare multiple runs
|
|
29
|
+
r1 = bs.load("STRATEGY_CLASS/run1/20260320_120000")
|
|
30
|
+
r2 = bs.load("STRATEGY_CLASS/run2/20260320_130000")
|
|
31
|
+
tearsheet([r1, r2])
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### From CLI:
|
|
35
|
+
```bash
|
|
36
|
+
# Browse results interactively (account URI)
|
|
37
|
+
uv run qubx browse r2:backtests
|
|
38
|
+
|
|
39
|
+
# Also works with S3 or local paths
|
|
40
|
+
uv run qubx browse s3://my-bucket/backtests
|
|
41
|
+
uv run qubx browse results/
|
|
42
|
+
|
|
43
|
+
# Query results with filters
|
|
44
|
+
uv run qubx backtests r2:backtests -w "sharpe > 1.5" -n 10
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Result Object API
|
|
48
|
+
|
|
49
|
+
A loaded result `r` provides:
|
|
50
|
+
|
|
51
|
+
| Property | Description |
|
|
52
|
+
|----------|-------------|
|
|
53
|
+
| `r.tearsheet()` | Full performance report |
|
|
54
|
+
| `r.equity` | Equity curve (Series) |
|
|
55
|
+
| `r.portfolio_log` | Portfolio state over time (DataFrame) |
|
|
56
|
+
| `r.executions_log` | All executed trades (DataFrame) |
|
|
57
|
+
| `r.signals_log` | All signals generated (DataFrame) |
|
|
58
|
+
| `r.emitter_data` | Custom emitter data (if `enable_inmemory_emitter: true`) |
|
|
59
|
+
|
|
60
|
+
### Common analysis patterns:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
# Equity curve
|
|
64
|
+
r.equity.plot()
|
|
65
|
+
|
|
66
|
+
# Positions over time
|
|
67
|
+
r.portfolio_log.filter(like="_Pos")
|
|
68
|
+
|
|
69
|
+
# Funding PnL per asset
|
|
70
|
+
r.portfolio_log.filter(like="_Funding")
|
|
71
|
+
|
|
72
|
+
# Funding PnL breakdown
|
|
73
|
+
fp = r.get_funding_per_asset()
|
|
74
|
+
fp.iloc[-1].sort_values(ascending=False).head(15).plot(kind="bar")
|
|
75
|
+
|
|
76
|
+
# Trade list
|
|
77
|
+
r.executions_log
|
|
78
|
+
|
|
79
|
+
# Compare funding between runs
|
|
80
|
+
from qubx.pandaz.utils import scols
|
|
81
|
+
fp1 = r1.get_funding_per_asset()
|
|
82
|
+
fp2 = r2.get_funding_per_asset()
|
|
83
|
+
scols(
|
|
84
|
+
fp1["BTC"].rename("run1"),
|
|
85
|
+
fp2["BTC"].rename("run2"),
|
|
86
|
+
).plot()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Comparing Two Runs
|
|
90
|
+
|
|
91
|
+
### Tearsheet comparison:
|
|
92
|
+
```python
|
|
93
|
+
tearsheet([r1, r2])
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Execution diff — find trades in one run but not the other:
|
|
97
|
+
```python
|
|
98
|
+
df1 = r1.executions_log.set_index(["symbol", "exchange"], append=True)
|
|
99
|
+
df2 = r2.executions_log.set_index(["symbol", "exchange"], append=True)
|
|
100
|
+
df_diff = df2[~df2.index.isin(df1.index)]
|
|
101
|
+
len(df_diff) # number of different executions
|
|
102
|
+
df_diff.head()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Debugging via Log Files
|
|
106
|
+
|
|
107
|
+
Simulation logs are stored alongside results when run with `-L` flag:
|
|
108
|
+
```
|
|
109
|
+
STRATEGY_CLASS/run_name/YYYYMMDD_HHMMSS/
|
|
110
|
+
├── run_name.log # simulation log
|
|
111
|
+
├── run_name.yml # config used
|
|
112
|
+
├── portfolio.parquet
|
|
113
|
+
├── executions.parquet
|
|
114
|
+
├── signals.parquet
|
|
115
|
+
└── ...
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Log analysis patterns:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Find selector activity (pair entries/exits)
|
|
122
|
+
grep "selectors" /path/to/run.log
|
|
123
|
+
|
|
124
|
+
# Compare selector lines between two runs
|
|
125
|
+
diff <(grep "selectors" run1.log) <(grep "selectors" run2.log)
|
|
126
|
+
|
|
127
|
+
# Find first divergence point
|
|
128
|
+
paste <(grep "selectors" run1.log) <(grep "selectors" run2.log) | awk -F'\t' '$1 != $2 {print NR; exit}'
|
|
129
|
+
|
|
130
|
+
# Check funding payment data
|
|
131
|
+
grep "\[FP\]" /path/to/run.log | head -20
|
|
132
|
+
|
|
133
|
+
# Check errors
|
|
134
|
+
grep "❌" /path/to/run.log
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Running Simulations
|
|
138
|
+
|
|
139
|
+
### From CLI:
|
|
140
|
+
```bash
|
|
141
|
+
# Basic simulation
|
|
142
|
+
uv run qubx simulate configs/my_strategy.yaml -o s3://backtests -L
|
|
143
|
+
|
|
144
|
+
# Override dates
|
|
145
|
+
uv run qubx simulate configs/my_strategy.yaml -s 2026-01-01 -e 2026-03-01 -o s3://backtests -L
|
|
146
|
+
|
|
147
|
+
# Custom run name
|
|
148
|
+
uv run qubx simulate configs/my_strategy.yaml -o s3://backtests -n "baseline_v1" -L
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### From notebook (quick iteration):
|
|
152
|
+
```python
|
|
153
|
+
from qubx.utils.runner.runner import simulate_config
|
|
154
|
+
|
|
155
|
+
results = simulate_config(
|
|
156
|
+
"configs/my_strategy.yaml",
|
|
157
|
+
start="2026-01-15",
|
|
158
|
+
stop="2026-02-01",
|
|
159
|
+
# Override any strategy parameter:
|
|
160
|
+
max_pairs=3,
|
|
161
|
+
min_entry_ev=0,
|
|
162
|
+
)
|
|
163
|
+
r = results[0]
|
|
164
|
+
r.tearsheet()
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Output Paths
|
|
168
|
+
|
|
169
|
+
Results are organized as:
|
|
170
|
+
```
|
|
171
|
+
<output>/<strategy_class>/<config_name>/<YYYYMMDD_HHMMSS>/
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
- Local output: `-o results` or `-o /tmp/my_test`
|
|
175
|
+
- Account URI: `-o r2:backtests` (uses named S3 account from `~/.qubx/config.json`)
|
|
176
|
+
- S3 output: `-o s3://backtests`
|
|
177
|
+
|
|
178
|
+
## Common Debugging Scenarios
|
|
179
|
+
|
|
180
|
+
### Results differ between runs with same config
|
|
181
|
+
1. Compare tearsheets: `tearsheet([r1, r2])`
|
|
182
|
+
2. Find first selector divergence in logs
|
|
183
|
+
3. Check if OHLC data or funding payment data differs at the divergence point
|
|
184
|
+
4. Look for cache-related issues if prefetch is enabled
|
|
185
|
+
|
|
186
|
+
### Strategy not trading
|
|
187
|
+
1. Check if `on_event` fires: look for selector log lines
|
|
188
|
+
2. Check `on_fit` timing: look for universe/pair discovery logs
|
|
189
|
+
3. Verify funding payment data is available
|
|
190
|
+
4. Check if `on_start` was called (requires market data for initial instruments)
|
|
191
|
+
|
|
192
|
+
### Unexpected PnL
|
|
193
|
+
1. Check `r.portfolio_log.filter(like="_Funding")` for funding PnL
|
|
194
|
+
2. Check `r.portfolio_log.filter(like="_Pos")` for position changes
|
|
195
|
+
3. Compare `r.executions_log` with expected trades
|
|
196
|
+
4. Check spread PnL vs funding PnL breakdown
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py
RENAMED
|
File without changes
|
{qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py
RENAMED
|
File without changes
|
{qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py
RENAMED
|
File without changes
|
{qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|