Qubx 1.0.6.dev7__tar.gz → 1.1.1.dev1__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.0.6.dev7 → qubx-1.1.1.dev1}/PKG-INFO +1 -1
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/_version.py +2 -2
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/management.py +81 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/commands.py +159 -29
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/tui.py +382 -93
- qubx-1.1.1.dev1/src/qubx/cli/user_config.py +20 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/base.py +15 -15
- qubx-1.1.1.dev1/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +334 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +2229 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +235 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +49 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/.gitignore.j2 +22 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/.python-version +1 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/.vscode/launch.json +94 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/.vscode/settings.json +17 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/.vscode/tasks.json +144 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/CLAUDE.md.j2 +47 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/README.md.j2 +22 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/justfile.j2 +18 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/pyproject.toml.j2 +38 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +5 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +23 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +22 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/template.yml +29 -0
- qubx-1.1.1.dev1/src/qubx/templates/repo/tests/test_strategy.py.j2 +7 -0
- qubx-1.1.1.dev1/src/qubx/utils/plotting/renderers/plotly.py +0 -0
- qubx-1.1.1.dev1/src/qubx/utils/runner/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/s3.py +9 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/.gitignore +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/LICENSE +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/README.md +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/pyproject.toml +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/scripts/build.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/_nb_magic.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/account.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/broker.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/data.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/iteratedstream.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/ome.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/optimization.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/runner.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/sentinels.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/simulated_data.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/simulated_exchange.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/simulator.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/transfers.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/utils.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/deploy.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/misc.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/release.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/resolver.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/s3.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/theme.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/config.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/account.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/broker.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/data.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exceptions.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/okx/account.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/okx/broker.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/factory.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/utils.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/registry.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/tardis/data.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/tardis/utils.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/account.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/basics.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/context.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/detectors/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/detectors/delisting.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/detectors/stale.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/errors.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/exceptions.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/helpers.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/initializer.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/interfaces.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/loggers.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/lookups.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/metrics.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/market.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/processing.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/subscription.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/trading.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/universe.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/utils.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/series.pxd +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/series.pyi +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/series.pyx +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/utils.pyi +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/utils.pyx +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/cache.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/containers.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/guards.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/registry.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storage.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/ccxt.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/csv.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/handy.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/multi.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/questdb.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/stub.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/utils.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/transformers.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/base.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/composite.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/csv.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/indicator.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/inmemory.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/prometheus.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/questdb.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/composite.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/formatters/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/formatters/base.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/formatters/incremental.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/formatters/slack.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/formatters/target_position.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/redis_streams.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/slack.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/gathering/simplest.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/health/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/health/base.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/health/dummy.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/health/server.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/loggers/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/loggers/csv.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/loggers/factory.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/loggers/inmemory.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/loggers/mongo.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/notifications/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/notifications/composite.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/notifications/slack.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/notifications/throttler.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/pandaz/stats.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/pandaz/ta.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/pandaz/utils.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/plugins/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/plugins/loader.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/_build.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/crypto-fees.ini +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restarts/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restarts/state_resolvers.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restarts/time_finders.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/balance.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/factory.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/interfaces.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/position.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/signal.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/state.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/utils.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/state/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/state/dummy.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/state/redis.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/ta/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/ta/indicators.pxd +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/ta/indicators.pyi +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/ta/indicators.pyx +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/accounts.toml.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/config.yml.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/jlive.sh.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/template.yml +0 -0
- /qubx-1.0.6.dev7/src/qubx/utils/plotting/__init__.py → /qubx-1.1.1.dev1/src/qubx/templates/repo/configs/.gitkeep +0 -0
- /qubx-1.0.6.dev7/src/qubx/utils/plotting/renderers/__init__.py → /qubx-1.1.1.dev1/src/qubx/templates/repo/research/.gitkeep +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/__init__.py.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/config.yml.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/strategy.py.j2 +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/template.yml +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/advanced.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/composite.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/riskctrl.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/sizers.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/charting/lookinglass.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/charting/mpl_helpers.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/charting/orderbook.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/collections.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/hft/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/hft/numba_utils.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/hft/orderbook.pyi +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/hft/orderbook.pyx +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/marketdata/ccxt.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/marketdata/dukas.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/misc.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/nonce.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/ntp.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/numbers_utils.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/orderbook.py +0 -0
- {qubx-1.0.6.dev7/src/qubx/utils/runner → qubx-1.1.1.dev1/src/qubx/utils/plotting}/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/plotting/dashboard.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/plotting/data.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/plotting/interfaces.py +0 -0
- /qubx-1.0.6.dev7/src/qubx/utils/plotting/renderers/plotly.py → /qubx-1.1.1.dev1/src/qubx/utils/plotting/renderers/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/questdb.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/rate_limiter.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/results.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/ringbuffer.pxd +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/ringbuffer.pyi +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/ringbuffer.pyx +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/accounts.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/configs.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/factory.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/kernel_service.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/runner.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/app.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/handlers.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/init_code.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/kernel.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/styles.tcss +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/slack.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/throttler.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/time.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/websocket_manager.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
- {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/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.
|
|
32
|
-
__version_tuple__ = version_tuple = (1,
|
|
31
|
+
__version__ = version = '1.1.1.dev1'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 1, 1, 'dev1')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -502,6 +502,87 @@ class BacktestStorage:
|
|
|
502
502
|
return []
|
|
503
503
|
return df["backtest_id"].tolist()
|
|
504
504
|
|
|
505
|
+
def delete(self, backtest_id: str) -> None:
|
|
506
|
+
"""
|
|
507
|
+
Delete a single backtest run directory.
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
backtest_id: Relative path within base_path,
|
|
511
|
+
e.g. "my_strategy/Nimble/20240301_120000"
|
|
512
|
+
"""
|
|
513
|
+
path = f"{self.base_path}{backtest_id.strip('/')}/"
|
|
514
|
+
if self._is_cloud:
|
|
515
|
+
S3Client(storage_options=self._storage_options).rm(path, recursive=True)
|
|
516
|
+
else:
|
|
517
|
+
import shutil
|
|
518
|
+
|
|
519
|
+
shutil.rmtree(path, ignore_errors=True)
|
|
520
|
+
self._reset_conn()
|
|
521
|
+
|
|
522
|
+
def delete_group(self, group_path: str) -> None:
|
|
523
|
+
"""
|
|
524
|
+
Delete an entire group directory (class-level or config-level).
|
|
525
|
+
|
|
526
|
+
Args:
|
|
527
|
+
group_path: Relative path within base_path,
|
|
528
|
+
e.g. "my_strategy/Nimble" (config) or "Nimble" (class)
|
|
529
|
+
"""
|
|
530
|
+
path = f"{self.base_path}{group_path.strip('/')}/"
|
|
531
|
+
if self._is_cloud:
|
|
532
|
+
S3Client(storage_options=self._storage_options).rm(path, recursive=True)
|
|
533
|
+
else:
|
|
534
|
+
import shutil
|
|
535
|
+
|
|
536
|
+
shutil.rmtree(path, ignore_errors=True)
|
|
537
|
+
self._reset_conn()
|
|
538
|
+
|
|
539
|
+
def _reset_conn(self) -> None:
|
|
540
|
+
"""Reset DuckDB connection to clear cached file metadata after mutations."""
|
|
541
|
+
self._conn.close()
|
|
542
|
+
self._conn = self._duckdb.connect()
|
|
543
|
+
if self._is_cloud:
|
|
544
|
+
self._setup_cloud_duckdb()
|
|
545
|
+
|
|
546
|
+
def get_log(self, backtest_id: str, config_name: str | None = None) -> str | None:
|
|
547
|
+
"""
|
|
548
|
+
Read the log file for a backtest run.
|
|
549
|
+
|
|
550
|
+
Args:
|
|
551
|
+
backtest_id: Relative path within base_path.
|
|
552
|
+
config_name: Config name (used for log filename).
|
|
553
|
+
If None, searches for any .log file.
|
|
554
|
+
|
|
555
|
+
Returns:
|
|
556
|
+
Log file content as string, or None if not found.
|
|
557
|
+
"""
|
|
558
|
+
run_dir = f"{self.base_path}{backtest_id.strip('/')}/"
|
|
559
|
+
|
|
560
|
+
if config_name:
|
|
561
|
+
log_path = f"{run_dir}{config_name}.log"
|
|
562
|
+
else:
|
|
563
|
+
try:
|
|
564
|
+
files_df = self._conn.execute(f"SELECT file FROM glob('{run_dir}*.log')").df()
|
|
565
|
+
if files_df.empty:
|
|
566
|
+
return None
|
|
567
|
+
log_path = files_df["file"].iloc[0]
|
|
568
|
+
except Exception:
|
|
569
|
+
return None
|
|
570
|
+
|
|
571
|
+
if self._is_cloud:
|
|
572
|
+
try:
|
|
573
|
+
from qubx.utils.s3 import strip_scheme
|
|
574
|
+
|
|
575
|
+
client = S3Client(storage_options=self._storage_options)
|
|
576
|
+
with client.fs.open_input_stream(strip_scheme(log_path)) as f:
|
|
577
|
+
return f.read().decode("utf-8")
|
|
578
|
+
except Exception:
|
|
579
|
+
return None
|
|
580
|
+
else:
|
|
581
|
+
from pathlib import Path
|
|
582
|
+
|
|
583
|
+
p = Path(log_path)
|
|
584
|
+
return p.read_text(encoding="utf-8", errors="replace") if p.is_file() else None
|
|
585
|
+
|
|
505
586
|
def export_backtests_to_markdown(self, backtest_id: str, path: str, tags: tuple[str] | None = None):
|
|
506
587
|
if tsr := self.load(backtest_id):
|
|
507
588
|
tsr.to_markdown(path, list(tags) if tags else None)
|
|
@@ -496,21 +496,27 @@ def browse(results_path: str | None):
|
|
|
496
496
|
|
|
497
497
|
qubx browse /backtests/momentum/
|
|
498
498
|
|
|
499
|
+
qubx browse r2:backtests
|
|
500
|
+
|
|
499
501
|
qubx browse s3://my-bucket/backtests/
|
|
500
502
|
"""
|
|
501
503
|
from qubx.config import get_settings
|
|
502
|
-
from qubx.utils.s3 import is_cloud_path
|
|
504
|
+
from qubx.utils.s3 import S3Client, is_account_uri, is_cloud_path
|
|
503
505
|
|
|
504
506
|
from .tui import run_backtest_browser
|
|
505
507
|
|
|
506
508
|
if results_path is None:
|
|
507
509
|
results_path = get_settings().default_browse_path or "results"
|
|
508
510
|
|
|
509
|
-
|
|
510
|
-
if
|
|
511
|
+
storage_options: dict | None = None
|
|
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):
|
|
511
517
|
results_path = os.path.abspath(os.path.expanduser(results_path))
|
|
512
518
|
|
|
513
|
-
run_backtest_browser(results_path)
|
|
519
|
+
run_backtest_browser(results_path, storage_options=storage_options)
|
|
514
520
|
|
|
515
521
|
|
|
516
522
|
@main.command()
|
|
@@ -571,6 +577,12 @@ def browse(results_path: str | None):
|
|
|
571
577
|
is_flag=True,
|
|
572
578
|
help="List all available built-in templates",
|
|
573
579
|
)
|
|
580
|
+
@click.option("--description", "-d", type=str, default=None, help="Project description (repo template)")
|
|
581
|
+
@click.option("--author", type=str, default=None, help="Author name (repo template)")
|
|
582
|
+
@click.option("--email", type=str, default=None, help="Author email (repo template)")
|
|
583
|
+
@click.option("--github-org", type=str, default=None, help="GitHub org or username (repo template)")
|
|
584
|
+
@click.option("--create-repo", is_flag=True, default=False, help="Create GitHub repository (repo template)")
|
|
585
|
+
@click.option("--private/--public", default=True, help="Repository visibility (repo template)")
|
|
574
586
|
def init(
|
|
575
587
|
template: str,
|
|
576
588
|
template_path: str | None,
|
|
@@ -580,6 +592,12 @@ def init(
|
|
|
580
592
|
timeframe: str,
|
|
581
593
|
output_dir: str,
|
|
582
594
|
list_templates: bool,
|
|
595
|
+
description: str | None,
|
|
596
|
+
author: str | None,
|
|
597
|
+
email: str | None,
|
|
598
|
+
github_org: str | None,
|
|
599
|
+
create_repo: bool,
|
|
600
|
+
private: bool,
|
|
583
601
|
):
|
|
584
602
|
"""
|
|
585
603
|
Create a new strategy from a template.
|
|
@@ -589,9 +607,14 @@ def init(
|
|
|
589
607
|
- Configuration file for qubx run command
|
|
590
608
|
- Package structure for proper imports
|
|
591
609
|
|
|
610
|
+
Use --template repo for a full repository with CI, tests, and justfile.
|
|
611
|
+
|
|
592
612
|
The generated strategy can be run immediately with:
|
|
593
613
|
uv run qubx run --config config.yml --paper
|
|
594
614
|
"""
|
|
615
|
+
import subprocess
|
|
616
|
+
import sys
|
|
617
|
+
|
|
595
618
|
from qubx.templates import TemplateError, TemplateManager
|
|
596
619
|
|
|
597
620
|
try:
|
|
@@ -604,40 +627,147 @@ def init(
|
|
|
604
627
|
return
|
|
605
628
|
|
|
606
629
|
click.echo("Available templates:")
|
|
607
|
-
for
|
|
608
|
-
|
|
609
|
-
click.echo(f" {
|
|
630
|
+
for tpl_name, metadata in templates.items():
|
|
631
|
+
tpl_desc = metadata.get("description", "No description")
|
|
632
|
+
click.echo(f" {tpl_name:<15} - {tpl_desc}")
|
|
610
633
|
return
|
|
611
634
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
635
|
+
if template == "repo":
|
|
636
|
+
_init_repo(
|
|
637
|
+
manager, name, description, author, email, github_org,
|
|
638
|
+
exchange, symbols, timeframe, output_dir, create_repo, private,
|
|
639
|
+
)
|
|
640
|
+
else:
|
|
641
|
+
strategy_path = manager.generate_strategy(
|
|
642
|
+
template_name=template if not template_path else None,
|
|
643
|
+
template_path=template_path,
|
|
644
|
+
output_dir=output_dir,
|
|
645
|
+
name=name,
|
|
646
|
+
exchange=exchange,
|
|
647
|
+
symbols=symbols,
|
|
648
|
+
timeframe=timeframe,
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
click.echo(f"Strategy '{name}' created successfully!")
|
|
652
|
+
click.echo(f"Location: {strategy_path}")
|
|
653
|
+
click.echo()
|
|
654
|
+
click.echo("To run your strategy:")
|
|
655
|
+
click.echo(f" cd {strategy_path}")
|
|
656
|
+
click.echo(" uv run qubx run config.yml --paper")
|
|
657
|
+
click.echo()
|
|
658
|
+
click.echo("To run in Jupyter mode:")
|
|
659
|
+
click.echo(" ./jpaper.sh")
|
|
632
660
|
|
|
633
661
|
except TemplateError as e:
|
|
634
|
-
click.echo(f"
|
|
662
|
+
click.echo(f"Error: {e}", err=True)
|
|
635
663
|
raise click.Abort()
|
|
636
664
|
except Exception as e:
|
|
637
|
-
click.echo(f"
|
|
665
|
+
click.echo(f"Unexpected error: {e}", err=True)
|
|
638
666
|
raise click.Abort()
|
|
639
667
|
|
|
640
668
|
|
|
669
|
+
def _init_repo(
|
|
670
|
+
manager,
|
|
671
|
+
name: str,
|
|
672
|
+
description: str | None,
|
|
673
|
+
author: str | None,
|
|
674
|
+
email: str | None,
|
|
675
|
+
github_org: str | None,
|
|
676
|
+
exchange: str,
|
|
677
|
+
symbols: str,
|
|
678
|
+
timeframe: str,
|
|
679
|
+
output_dir: str,
|
|
680
|
+
create_repo: bool,
|
|
681
|
+
private: bool,
|
|
682
|
+
):
|
|
683
|
+
"""Interactive wizard for creating a full strategy repository."""
|
|
684
|
+
import subprocess
|
|
685
|
+
import sys
|
|
686
|
+
|
|
687
|
+
from qubx.cli.user_config import load_init_config, save_init_config
|
|
688
|
+
from qubx.templates import TemplateError
|
|
689
|
+
|
|
690
|
+
config = load_init_config()
|
|
691
|
+
interactive = sys.stdin.isatty()
|
|
692
|
+
|
|
693
|
+
if interactive:
|
|
694
|
+
click.echo("\n Strategy Repository Setup\n")
|
|
695
|
+
name = click.prompt(" Strategy name", default=name)
|
|
696
|
+
description = click.prompt(" Description", default=description or "")
|
|
697
|
+
author = click.prompt(" Author name", default=author or config.get("author_name", ""))
|
|
698
|
+
email = click.prompt(" Author email", default=email or config.get("author_email", ""))
|
|
699
|
+
github_org = click.prompt(
|
|
700
|
+
" GitHub organization",
|
|
701
|
+
default=github_org or config.get("github_org", ""),
|
|
702
|
+
)
|
|
703
|
+
exchange = click.prompt(" Exchange", default=exchange)
|
|
704
|
+
symbols = click.prompt(" Symbols (comma-separated)", default=symbols)
|
|
705
|
+
timeframe = click.prompt(" Timeframe", default=timeframe)
|
|
706
|
+
if not create_repo:
|
|
707
|
+
create_repo = click.confirm("\n Create GitHub repository?", default=False)
|
|
708
|
+
if create_repo:
|
|
709
|
+
private = click.confirm(" Private repository?", default=True)
|
|
710
|
+
click.echo()
|
|
711
|
+
else:
|
|
712
|
+
description = description or ""
|
|
713
|
+
author = author or config.get("author_name", "")
|
|
714
|
+
email = email or config.get("author_email", "")
|
|
715
|
+
github_org = github_org or config.get("github_org", "")
|
|
716
|
+
|
|
717
|
+
# Save preferences for next time
|
|
718
|
+
save_init_config({
|
|
719
|
+
"author_name": author,
|
|
720
|
+
"author_email": email,
|
|
721
|
+
"github_org": github_org,
|
|
722
|
+
})
|
|
723
|
+
|
|
724
|
+
# Generate project from template
|
|
725
|
+
strategy_path = manager.generate_strategy(
|
|
726
|
+
template_name="repo",
|
|
727
|
+
output_dir=output_dir,
|
|
728
|
+
name=name,
|
|
729
|
+
description=description,
|
|
730
|
+
author_name=author,
|
|
731
|
+
author_email=email,
|
|
732
|
+
github_org=github_org,
|
|
733
|
+
exchange=exchange,
|
|
734
|
+
symbols=symbols,
|
|
735
|
+
timeframe=timeframe,
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
# Initialize git repo with main as default branch
|
|
739
|
+
subprocess.run(["git", "init", "-b", "main"], cwd=strategy_path, capture_output=True)
|
|
740
|
+
subprocess.run(["git", "add", "-A"], cwd=strategy_path, capture_output=True)
|
|
741
|
+
subprocess.run(
|
|
742
|
+
["git", "commit", "-m", "Initial project setup"],
|
|
743
|
+
cwd=strategy_path,
|
|
744
|
+
capture_output=True,
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
click.echo(f"Strategy '{name}' created at {strategy_path}")
|
|
748
|
+
|
|
749
|
+
# Create GitHub repo if requested
|
|
750
|
+
if create_repo and github_org:
|
|
751
|
+
repo_name = f"{github_org}/{name}"
|
|
752
|
+
visibility = "--private" if private else "--public"
|
|
753
|
+
result = subprocess.run(
|
|
754
|
+
["gh", "repo", "create", repo_name, visibility, "--source=.", "--push"],
|
|
755
|
+
cwd=strategy_path,
|
|
756
|
+
capture_output=True,
|
|
757
|
+
text=True,
|
|
758
|
+
)
|
|
759
|
+
if result.returncode == 0:
|
|
760
|
+
click.echo(f"GitHub repository created: {repo_name}")
|
|
761
|
+
else:
|
|
762
|
+
click.echo(f"Failed to create GitHub repo: {result.stderr.strip()}", err=True)
|
|
763
|
+
|
|
764
|
+
click.echo()
|
|
765
|
+
click.echo("Next steps:")
|
|
766
|
+
click.echo(f" cd {strategy_path}")
|
|
767
|
+
click.echo(" just install")
|
|
768
|
+
click.echo(" just test")
|
|
769
|
+
|
|
770
|
+
|
|
641
771
|
@main.group()
|
|
642
772
|
def kernel():
|
|
643
773
|
"""
|