Qubx 1.1.3.dev17__tar.gz → 1.1.3.dev18__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.3.dev17 → qubx-1.1.3.dev18}/PKG-INFO +1 -1
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/_version.py +2 -2
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/cli/commands.py +41 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/cli/release.py +213 -114
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/configs.py +37 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/.gitignore +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/LICENSE +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/README.md +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/pyproject.toml +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/scripts/build.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/_nb_magic.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/account.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/broker.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/data.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/iteratedstream.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/management.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/ome.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/optimization.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/runner.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/sentinels.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/simulated_data.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/simulated_exchange.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/simulator.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/transfers.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/backtester/utils.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/cli/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/cli/deploy.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/cli/misc.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/cli/resolver.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/cli/s3.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/cli/theme.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/cli/tui.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/cli/user_config.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/config.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/account.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/broker.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/data.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exceptions.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/okx/account.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/okx/broker.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/factory.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/rate_limits.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/utils.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/registry.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/tardis/data.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/tardis/utils.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/control/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/control/builtin.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/control/decorator.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/control/executor.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/control/interfaces.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/control/server.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/control/types.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/account.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/basics.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/context.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/detectors/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/detectors/delisting.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/detectors/stale.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/errors.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/exceptions.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/helpers.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/initializer.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/interfaces.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/loggers.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/lookups.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/metrics.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/mixins/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/mixins/market.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/mixins/processing.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/mixins/subscription.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/mixins/trading.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/mixins/universe.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/mixins/utils.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/series.pxd +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/series.pyi +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/series.pyx +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/utils.pyi +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/core/utils.pyx +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/cache.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/containers.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/guards.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/registry.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/storage.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/storages/ccxt.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/storages/csv.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/storages/handy.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/storages/multi.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/storages/questdb.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/storages/stub.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/storages/utils.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/data/transformers.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/emitters/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/emitters/base.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/emitters/composite.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/emitters/csv.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/emitters/indicator.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/emitters/inmemory.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/emitters/prometheus.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/emitters/questdb.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/exporters/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/exporters/composite.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/exporters/formatters/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/exporters/formatters/base.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/exporters/formatters/incremental.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/exporters/formatters/slack.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/exporters/formatters/target_position.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/exporters/redis_streams.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/exporters/slack.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/gathering/simplest.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/health/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/health/base.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/health/dummy.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/loggers/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/loggers/csv.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/loggers/factory.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/loggers/inmemory.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/loggers/mongo.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/loggers/postgres.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/notifications/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/notifications/composite.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/notifications/slack.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/notifications/throttler.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/pandaz/stats.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/pandaz/ta.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/pandaz/utils.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/plugins/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/plugins/loader.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/rate_limiting/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/rate_limiting/backend.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/rate_limiting/config.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/rate_limiting/engine.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/rate_limiting/ip_resolver.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/rate_limiting/manager.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/rate_limiting/redis_backend.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/_build.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/crypto-fees.ini +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restarts/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restarts/state_resolvers.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restarts/time_finders.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restorers/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restorers/balance.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restorers/factory.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restorers/interfaces.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restorers/position.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restorers/signal.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restorers/state.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/restorers/utils.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/state/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/state/dummy.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/state/redis.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/ta/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/ta/indicators.pxd +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/ta/indicators.pyi +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/ta/indicators.pyx +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/base.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/project/accounts.toml.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/project/config.yml.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/project/jlive.sh.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/project/template.yml +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/.claude/skills/simulation-explorer/SKILL.md +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/.gitignore.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/.python-version +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/.vscode/launch.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/.vscode/settings.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/.vscode/tasks.json +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/CLAUDE.md.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/README.md.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/configs/.gitkeep +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/justfile.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/pyproject.toml.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/research/.gitkeep +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/template.yml +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/repo/tests/test_strategy.py.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/simple/__init__.py.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/simple/config.yml.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/simple/strategy.py.j2 +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/templates/simple/template.yml +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/trackers/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/trackers/advanced.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/trackers/composite.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/trackers/riskctrl.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/trackers/sizers.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/charting/lookinglass.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/charting/mpl_helpers.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/charting/orderbook.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/collections.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/hft/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/hft/numba_utils.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/hft/orderbook.pyi +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/hft/orderbook.pyx +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/marketdata/ccxt.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/marketdata/dukas.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/misc.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/nonce.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/ntp.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/numbers_utils.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/orderbook.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/plotting/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/plotting/dashboard.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/plotting/data.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/plotting/interfaces.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/questdb.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/rate_limiter.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/results.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/ringbuffer.pxd +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/ringbuffer.pyi +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/ringbuffer.pyx +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/accounts.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/factory.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/kernel_service.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/runner.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/app.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/handlers.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/init_code.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/kernel.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/styles.tcss +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/widgets/account_summary.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/s3.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/slack.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/throttler.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/time.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/utils/websocket_manager.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
- {qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/tests/strategies/obi_trader/src/obi_trader/models/obi_trader.py +0 -0
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.1.3.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 1, 3, '
|
|
21
|
+
__version__ = version = '1.1.3.dev18'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 1, 3, 'dev18')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -412,6 +412,47 @@ def release(
|
|
|
412
412
|
)
|
|
413
413
|
|
|
414
414
|
|
|
415
|
+
@main.command("release-from-source")
|
|
416
|
+
@click.option(
|
|
417
|
+
"--config",
|
|
418
|
+
"-c",
|
|
419
|
+
type=click.Path(exists=True, resolve_path=True),
|
|
420
|
+
help="Path to a config YAML file with a release.source section",
|
|
421
|
+
required=True,
|
|
422
|
+
)
|
|
423
|
+
@click.option(
|
|
424
|
+
"--output-dir",
|
|
425
|
+
"-o",
|
|
426
|
+
type=click.Path(exists=False),
|
|
427
|
+
help="Output directory to put zip file.",
|
|
428
|
+
default=".releases",
|
|
429
|
+
show_default=True,
|
|
430
|
+
)
|
|
431
|
+
@click.option(
|
|
432
|
+
"--tag",
|
|
433
|
+
"-t",
|
|
434
|
+
type=click.STRING,
|
|
435
|
+
help="Additional tag for this release",
|
|
436
|
+
required=False,
|
|
437
|
+
)
|
|
438
|
+
def release_from_source_cmd(config: str, output_dir: str, tag: str | None) -> None:
|
|
439
|
+
"""
|
|
440
|
+
Build a release by cloning the source repo specified in the config's release.source section.
|
|
441
|
+
|
|
442
|
+
Used for CI-driven releases where the config lives in a separate repo from the strategy code.
|
|
443
|
+
The config must have a release.source section specifying the GitHub repo and ref.
|
|
444
|
+
"""
|
|
445
|
+
from .release import release_from_source
|
|
446
|
+
|
|
447
|
+
zip_path = release_from_source(
|
|
448
|
+
config_file=config,
|
|
449
|
+
output_dir=output_dir,
|
|
450
|
+
tag=tag,
|
|
451
|
+
)
|
|
452
|
+
if zip_path:
|
|
453
|
+
click.echo(zip_path)
|
|
454
|
+
|
|
455
|
+
|
|
415
456
|
@main.command()
|
|
416
457
|
@click.argument(
|
|
417
458
|
"zip-file",
|
|
@@ -447,6 +447,10 @@ def load_strategy_from_config(config_path: Path, directory: str) -> StrategyInfo
|
|
|
447
447
|
if len(strat_name) > 16 and len(_found_classes) > 1:
|
|
448
448
|
strat_name = _name_leader + generate_name(strat_name, 8)
|
|
449
449
|
|
|
450
|
+
# - fall back to the config name field when no local classes found
|
|
451
|
+
if not strat_name and strategy_config.name:
|
|
452
|
+
strat_name = strategy_config.name.replace(".", "_").replace(" ", "_")
|
|
453
|
+
|
|
450
454
|
return StrategyInfo(name=strat_name, classes=_found_classes, config=strategy_config)
|
|
451
455
|
|
|
452
456
|
except Exception as e:
|
|
@@ -558,6 +562,132 @@ def release_strategy(
|
|
|
558
562
|
logger.opt(colors=False).error(f"Error releasing strategy: {e}")
|
|
559
563
|
|
|
560
564
|
|
|
565
|
+
def release_from_source(
|
|
566
|
+
config_file: str,
|
|
567
|
+
output_dir: str,
|
|
568
|
+
tag: str | None = None,
|
|
569
|
+
) -> str | None:
|
|
570
|
+
"""
|
|
571
|
+
Build a release by cloning the source repo specified in the config's release.source section.
|
|
572
|
+
|
|
573
|
+
This is the entry point for CI-driven releases (e.g., from xrelease GitHub Action).
|
|
574
|
+
It reads the release.source.repo and release.source.ref from the config YAML,
|
|
575
|
+
clones that repo at the specified ref into a temp directory, copies the config
|
|
576
|
+
into the clone, and runs the standard release flow.
|
|
577
|
+
|
|
578
|
+
Args:
|
|
579
|
+
config_file: Path to the strategy config YAML (must have a release.source section)
|
|
580
|
+
output_dir: Directory to write the release ZIP
|
|
581
|
+
tag: Optional tag suffix for the release name
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
Path to the created ZIP file, or None on failure
|
|
585
|
+
"""
|
|
586
|
+
import subprocess
|
|
587
|
+
import tempfile
|
|
588
|
+
|
|
589
|
+
from qubx import QubxLogConfig
|
|
590
|
+
|
|
591
|
+
QubxLogConfig.set_log_level("INFO")
|
|
592
|
+
|
|
593
|
+
try:
|
|
594
|
+
config_path = os.path.abspath(os.path.expanduser(config_file))
|
|
595
|
+
if not is_config_file(config_path):
|
|
596
|
+
raise ValueError(f"Not a valid config file: {config_file}")
|
|
597
|
+
|
|
598
|
+
# Parse the config to extract release.source
|
|
599
|
+
stg_config = load_strategy_config_from_yaml(config_path, resolve_env=False)
|
|
600
|
+
if not stg_config.release or not stg_config.release.source:
|
|
601
|
+
raise ValueError("Config must have a release.source section with repo and ref")
|
|
602
|
+
|
|
603
|
+
source = stg_config.release.source
|
|
604
|
+
repo_url = f"https://github.com/{source.repo}.git"
|
|
605
|
+
ref = source.ref
|
|
606
|
+
|
|
607
|
+
logger.info(f"Cloning {source.repo} at ref '{ref}' ...")
|
|
608
|
+
clone_dir = tempfile.mkdtemp(prefix="qubx-release-")
|
|
609
|
+
|
|
610
|
+
try:
|
|
611
|
+
subprocess.run(
|
|
612
|
+
["git", "clone", "--depth", "1", "--branch", ref, repo_url, clone_dir],
|
|
613
|
+
check=True,
|
|
614
|
+
capture_output=True,
|
|
615
|
+
text=True,
|
|
616
|
+
)
|
|
617
|
+
except subprocess.CalledProcessError:
|
|
618
|
+
# --branch doesn't work with commit SHAs, fall back to full clone + checkout
|
|
619
|
+
logger.info(f"Shallow clone failed for ref '{ref}', trying full clone...")
|
|
620
|
+
shutil.rmtree(clone_dir, ignore_errors=True)
|
|
621
|
+
os.makedirs(clone_dir)
|
|
622
|
+
subprocess.run(
|
|
623
|
+
["git", "clone", repo_url, clone_dir],
|
|
624
|
+
check=True,
|
|
625
|
+
capture_output=True,
|
|
626
|
+
text=True,
|
|
627
|
+
)
|
|
628
|
+
subprocess.run(
|
|
629
|
+
["git", "checkout", ref],
|
|
630
|
+
cwd=clone_dir,
|
|
631
|
+
check=True,
|
|
632
|
+
capture_output=True,
|
|
633
|
+
text=True,
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
# Resolve dependencies in the cloned project
|
|
637
|
+
logger.info("Resolving dependencies in cloned project...")
|
|
638
|
+
subprocess.run(
|
|
639
|
+
["uv", "lock"],
|
|
640
|
+
cwd=clone_dir,
|
|
641
|
+
check=True,
|
|
642
|
+
capture_output=True,
|
|
643
|
+
text=True,
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
# Copy the config into the clone root so release can find it
|
|
647
|
+
config_basename = os.path.basename(config_path)
|
|
648
|
+
cloned_config = os.path.join(clone_dir, config_basename)
|
|
649
|
+
shutil.copy2(config_path, cloned_config)
|
|
650
|
+
|
|
651
|
+
# Run the standard release flow from the cloned project
|
|
652
|
+
logger.info(f"Running release from {clone_dir} ...")
|
|
653
|
+
output_dir = os.path.abspath(output_dir)
|
|
654
|
+
stg_info = load_strategy_from_config(Path(cloned_config), clone_dir)
|
|
655
|
+
pyproject_root = clone_dir
|
|
656
|
+
|
|
657
|
+
git_info = ReleaseInfo(
|
|
658
|
+
tag=generate_tag(stg_info.name, tag),
|
|
659
|
+
commit=ref,
|
|
660
|
+
user=getpass.getuser(),
|
|
661
|
+
time=datetime.now(),
|
|
662
|
+
commited_files=[],
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
create_released_pack(
|
|
666
|
+
stg_info=stg_info,
|
|
667
|
+
git_info=git_info,
|
|
668
|
+
pyproject_root=pyproject_root,
|
|
669
|
+
output_dir=output_dir,
|
|
670
|
+
config_file=cloned_config,
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
# Find the created ZIP
|
|
674
|
+
zip_path = os.path.join(output_dir, f"{git_info.tag}.zip")
|
|
675
|
+
if os.path.exists(zip_path):
|
|
676
|
+
logger.info(f"Release created: {zip_path}")
|
|
677
|
+
return zip_path
|
|
678
|
+
|
|
679
|
+
logger.error("Release ZIP not found after build")
|
|
680
|
+
return None
|
|
681
|
+
|
|
682
|
+
except Exception as e:
|
|
683
|
+
logger.opt(colors=False).error(f"Error in release_from_source: {e}")
|
|
684
|
+
return None
|
|
685
|
+
finally:
|
|
686
|
+
# Clean up temp clone
|
|
687
|
+
if "clone_dir" in locals():
|
|
688
|
+
shutil.rmtree(clone_dir, ignore_errors=True)
|
|
689
|
+
|
|
690
|
+
|
|
561
691
|
def _find_source_root(pyproject_root: str, project_name: str) -> str | None:
|
|
562
692
|
"""Find the source package directory for a project.
|
|
563
693
|
|
|
@@ -592,34 +722,6 @@ def _find_source_root(pyproject_root: str, project_name: str) -> str | None:
|
|
|
592
722
|
return None
|
|
593
723
|
|
|
594
724
|
|
|
595
|
-
def _collect_all_imports(strategy_files: list[str], project_root: str) -> set[str]:
|
|
596
|
-
"""
|
|
597
|
-
Collect all top-level import module names from strategy source files.
|
|
598
|
-
|
|
599
|
-
Scans each file's AST for import/from-import statements and returns
|
|
600
|
-
the set of top-level (first component) module names, excluding stdlib
|
|
601
|
-
and the project's own package.
|
|
602
|
-
|
|
603
|
-
Args:
|
|
604
|
-
strategy_files: List of absolute paths to strategy .py files
|
|
605
|
-
project_root: Project root for resolving relative imports
|
|
606
|
-
|
|
607
|
-
Returns:
|
|
608
|
-
Set of top-level import names (e.g. {"numpy", "cachetools", "qubx"})
|
|
609
|
-
"""
|
|
610
|
-
top_level_modules: set[str] = set()
|
|
611
|
-
|
|
612
|
-
for file_path in strategy_files:
|
|
613
|
-
try:
|
|
614
|
-
# Pass empty what_to_look to get ALL imports (no filter)
|
|
615
|
-
for imp in get_imports(file_path, what_to_look=[], project_root=project_root):
|
|
616
|
-
if imp.module:
|
|
617
|
-
top_level_modules.add(imp.module[0])
|
|
618
|
-
except Exception as e:
|
|
619
|
-
logger.warning(f"Failed to scan imports from {file_path}: {e}")
|
|
620
|
-
|
|
621
|
-
return top_level_modules
|
|
622
|
-
|
|
623
725
|
|
|
624
726
|
def _parse_uv_lock(uv_lock_path: str) -> dict[str, str]:
|
|
625
727
|
"""
|
|
@@ -649,96 +751,48 @@ def _parse_uv_lock(uv_lock_path: str) -> dict[str, str]:
|
|
|
649
751
|
return versions
|
|
650
752
|
|
|
651
753
|
|
|
652
|
-
def
|
|
653
|
-
strategy_files: list[str],
|
|
654
|
-
pyproject_root: str,
|
|
655
|
-
lock_versions: dict[str, str],
|
|
656
|
-
pyproject_data: dict,
|
|
657
|
-
external_imports: set[str] | None = None,
|
|
658
|
-
) -> list[str]:
|
|
754
|
+
def _parse_uv_lock_git_commits(uv_lock_path: str) -> dict[str, str]:
|
|
659
755
|
"""
|
|
660
|
-
|
|
756
|
+
Parse uv.lock and return {normalized_name: commit_sha} for git-sourced packages.
|
|
661
757
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
4. If yes, include that dep with the exact version from uv.lock
|
|
758
|
+
Lock entries look like:
|
|
759
|
+
source = { git = "https://github.com/org/repo?tag=v1.0#abcdef1234..." }
|
|
760
|
+
"""
|
|
761
|
+
import toml
|
|
667
762
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
pyproject_root: Root directory containing pyproject.toml
|
|
671
|
-
lock_versions: Pre-parsed {normalized_name: version} from uv.lock
|
|
672
|
-
pyproject_data: Parsed pyproject.toml dict
|
|
673
|
-
external_imports: Pre-computed set of external top-level import names.
|
|
674
|
-
If provided, skips internal import collection.
|
|
763
|
+
if not os.path.exists(uv_lock_path):
|
|
764
|
+
return {}
|
|
675
765
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
"""
|
|
679
|
-
import importlib.metadata
|
|
766
|
+
with open(uv_lock_path) as f:
|
|
767
|
+
lock_data = toml.load(f)
|
|
680
768
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
all_deps.extend(group_deps)
|
|
692
|
-
|
|
693
|
-
# Step 4: for each declared dep, check if the strategy uses it
|
|
694
|
-
scanned_deps: list[str] = []
|
|
695
|
-
for dep_spec in all_deps:
|
|
696
|
-
# Parse dep spec like "qubx[connectors,db,k8,tui]==1.0.3" or "cachetools>=6.2.1,<7"
|
|
697
|
-
# Extract: package name, extras (if any), version specifiers
|
|
698
|
-
match = re.match(r"^([A-Za-z0-9_.-]+)(\[[^\]]*\])?", dep_spec)
|
|
699
|
-
pkg_name = (
|
|
700
|
-
match.group(1).strip()
|
|
701
|
-
if match
|
|
702
|
-
else dep_spec.split("[")[0].split(">")[0].split("=")[0].split("<")[0].strip()
|
|
703
|
-
)
|
|
704
|
-
extras = match.group(2) or "" if match else ""
|
|
705
|
-
pkg_name_normalized = pkg_name.lower().replace("-", "_")
|
|
769
|
+
commits: dict[str, str] = {}
|
|
770
|
+
for pkg in lock_data.get("package", []):
|
|
771
|
+
source = pkg.get("source", {})
|
|
772
|
+
git_url = source.get("git", "")
|
|
773
|
+
if git_url and "#" in git_url:
|
|
774
|
+
commit_sha = git_url.split("#")[-1]
|
|
775
|
+
name = pkg.get("name", "").lower().replace("-", "_")
|
|
776
|
+
if name and commit_sha:
|
|
777
|
+
commits[name] = commit_sha
|
|
778
|
+
return commits
|
|
706
779
|
|
|
707
|
-
# Determine import names for this package
|
|
708
|
-
import_names: set[str] = set()
|
|
709
|
-
try:
|
|
710
|
-
dist = importlib.metadata.distribution(pkg_name)
|
|
711
|
-
top_level_text = dist.read_text("top_level.txt")
|
|
712
|
-
if top_level_text:
|
|
713
|
-
for line in top_level_text.strip().splitlines():
|
|
714
|
-
import_names.add(line.strip())
|
|
715
|
-
else:
|
|
716
|
-
# Fallback: check RECORD for package directories
|
|
717
|
-
if dist.files:
|
|
718
|
-
for f in dist.files:
|
|
719
|
-
parts = str(f).split("/")
|
|
720
|
-
if len(parts) > 1 and parts[0] and not parts[0].endswith(".dist-info"):
|
|
721
|
-
import_names.add(parts[0].replace("-", "_"))
|
|
722
|
-
if not import_names:
|
|
723
|
-
import_names.add(pkg_name_normalized)
|
|
724
|
-
except importlib.metadata.PackageNotFoundError:
|
|
725
|
-
# Package not installed — use fallback name mapping
|
|
726
|
-
import_names.add(pkg_name_normalized)
|
|
727
|
-
|
|
728
|
-
# Check if any import name is used by strategy
|
|
729
|
-
if import_names & strategy_imports:
|
|
730
|
-
# Pin version from lock file, preserving extras
|
|
731
|
-
version = lock_versions.get(pkg_name_normalized)
|
|
732
|
-
if version:
|
|
733
|
-
pinned = f"{pkg_name}{extras}=={version}"
|
|
734
|
-
else:
|
|
735
|
-
# Fallback to declared spec
|
|
736
|
-
pinned = dep_spec
|
|
737
|
-
scanned_deps.append(pinned)
|
|
738
|
-
logger.debug(f" Matched dep: {pinned} (imports: {import_names & strategy_imports})")
|
|
739
780
|
|
|
740
|
-
|
|
741
|
-
|
|
781
|
+
def _find_uv_git_checkout(commit_sha: str) -> str | None:
|
|
782
|
+
"""
|
|
783
|
+
Find the uv git cache checkout directory for a given commit SHA.
|
|
784
|
+
|
|
785
|
+
uv stores git checkouts at: ~/.cache/uv/git-v0/checkouts/{url_hash}/{commit[:7]}/
|
|
786
|
+
"""
|
|
787
|
+
import glob
|
|
788
|
+
|
|
789
|
+
short = commit_sha[:7]
|
|
790
|
+
uv_cache = os.path.expanduser("~/.cache/uv/git-v0/checkouts")
|
|
791
|
+
matches = glob.glob(os.path.join(uv_cache, "*", short))
|
|
792
|
+
for match in matches:
|
|
793
|
+
if os.path.isdir(match):
|
|
794
|
+
return match
|
|
795
|
+
return None
|
|
742
796
|
|
|
743
797
|
|
|
744
798
|
def _build_strategy_wheel(
|
|
@@ -762,7 +816,7 @@ def _build_strategy_wheel(
|
|
|
762
816
|
"""
|
|
763
817
|
import subprocess
|
|
764
818
|
|
|
765
|
-
wheels_dir = os.path.join(release_dir, "wheels")
|
|
819
|
+
wheels_dir = os.path.abspath(os.path.join(release_dir, "wheels"))
|
|
766
820
|
os.makedirs(wheels_dir, exist_ok=True)
|
|
767
821
|
|
|
768
822
|
logger.info("Building strategy wheel...")
|
|
@@ -981,6 +1035,8 @@ def create_released_pack(
|
|
|
981
1035
|
name = pdep.split(">=")[0].split("==")[0].split("<")[0].split("[")[0].strip().lower()
|
|
982
1036
|
required_packages.add(name)
|
|
983
1037
|
|
|
1038
|
+
git_commits = _parse_uv_lock_git_commits(uv_lock_path)
|
|
1039
|
+
|
|
984
1040
|
bundled_packages: list[str] = []
|
|
985
1041
|
if required_packages:
|
|
986
1042
|
logger.info("Resolving private/local source dependencies...")
|
|
@@ -990,6 +1046,7 @@ def create_released_pack(
|
|
|
990
1046
|
release_dir,
|
|
991
1047
|
required_packages,
|
|
992
1048
|
lock_versions,
|
|
1049
|
+
git_commits,
|
|
993
1050
|
)
|
|
994
1051
|
if bundled_packages:
|
|
995
1052
|
logger.info(f"Bundled {len(bundled_packages)} package(s): {', '.join(bundled_packages)}")
|
|
@@ -1192,12 +1249,14 @@ def _bundle_source_overrides(
|
|
|
1192
1249
|
release_dir: str,
|
|
1193
1250
|
required_packages: set[str],
|
|
1194
1251
|
lock_versions: dict[str, str],
|
|
1252
|
+
git_commits: dict[str, str] | None = None,
|
|
1195
1253
|
) -> list[str]:
|
|
1196
1254
|
"""
|
|
1197
1255
|
For each [tool.uv.sources] entry that is required by this release:
|
|
1198
1256
|
- path source + version on public PyPI → skip (will resolve from PyPI)
|
|
1199
1257
|
- path source + NOT on public PyPI → build wheel from local path and bundle in wheels/
|
|
1200
1258
|
- index source (private registry) → download wheel from that index and bundle in wheels/
|
|
1259
|
+
- git source → build wheel from uv's cached checkout and bundle in wheels/
|
|
1201
1260
|
|
|
1202
1261
|
Uses uv.lock as the single source of truth for package versions.
|
|
1203
1262
|
|
|
@@ -1209,7 +1268,7 @@ def _bundle_source_overrides(
|
|
|
1209
1268
|
if not sources:
|
|
1210
1269
|
return []
|
|
1211
1270
|
|
|
1212
|
-
wheels_dir = os.path.join(release_dir, "wheels")
|
|
1271
|
+
wheels_dir = os.path.abspath(os.path.join(release_dir, "wheels"))
|
|
1213
1272
|
bundled: list[str] = []
|
|
1214
1273
|
|
|
1215
1274
|
for pkg_name, source in sources.items():
|
|
@@ -1280,6 +1339,46 @@ def _bundle_source_overrides(
|
|
|
1280
1339
|
except Exception as e:
|
|
1281
1340
|
logger.opt(colors=False).warning(f" Failed to download wheel for {pkg_name}: {e}")
|
|
1282
1341
|
|
|
1342
|
+
elif "git" in source:
|
|
1343
|
+
if not pkg_ver:
|
|
1344
|
+
logger.warning(f" {pkg_name} not found in uv.lock, skipping bundle")
|
|
1345
|
+
continue
|
|
1346
|
+
|
|
1347
|
+
commit_sha = (git_commits or {}).get(pkg_norm)
|
|
1348
|
+
if not commit_sha:
|
|
1349
|
+
logger.warning(f" {pkg_name}: no git commit found in uv.lock, skipping bundle")
|
|
1350
|
+
continue
|
|
1351
|
+
|
|
1352
|
+
checkout_dir = _find_uv_git_checkout(commit_sha)
|
|
1353
|
+
if not checkout_dir:
|
|
1354
|
+
logger.warning(
|
|
1355
|
+
f" {pkg_name}: git checkout not found in uv cache (commit {commit_sha[:7]}), "
|
|
1356
|
+
"run `uv sync` first to populate the cache"
|
|
1357
|
+
)
|
|
1358
|
+
continue
|
|
1359
|
+
|
|
1360
|
+
logger.info(f" Bundling {pkg_name}=={pkg_ver} from git checkout {checkout_dir} ...")
|
|
1361
|
+
os.makedirs(wheels_dir, exist_ok=True)
|
|
1362
|
+
try:
|
|
1363
|
+
result = subprocess.run(
|
|
1364
|
+
["uv", "build", "--wheel", ".", "--out-dir", wheels_dir],
|
|
1365
|
+
cwd=checkout_dir,
|
|
1366
|
+
check=True,
|
|
1367
|
+
capture_output=True,
|
|
1368
|
+
text=True,
|
|
1369
|
+
)
|
|
1370
|
+
logger.debug(f"uv build stdout: {result.stdout}")
|
|
1371
|
+
for whl in os.listdir(wheels_dir):
|
|
1372
|
+
if whl.lower().startswith(pkg_norm) and "none-any" not in whl:
|
|
1373
|
+
logger.warning(
|
|
1374
|
+
f" {whl} is platform-specific. "
|
|
1375
|
+
"Ensure the container architecture matches the build machine."
|
|
1376
|
+
)
|
|
1377
|
+
bundled.append(pkg_name.lower())
|
|
1378
|
+
logger.info(f" Bundled {pkg_name}=={pkg_ver} from git")
|
|
1379
|
+
except subprocess.CalledProcessError as e:
|
|
1380
|
+
logger.opt(colors=False).warning(f" Failed to build wheel for {pkg_name}: {e.stderr or e}")
|
|
1381
|
+
|
|
1283
1382
|
return bundled
|
|
1284
1383
|
|
|
1285
1384
|
|
|
@@ -1401,7 +1500,7 @@ def generate_tag(strategy_name_id: str, tag_sfx: str | None) -> str:
|
|
|
1401
1500
|
_tn = datetime.now()
|
|
1402
1501
|
tag_s = f".{tag_sfx}" if tag_sfx else ""
|
|
1403
1502
|
_strategy_name_id = strategy_name_id.replace(",", "_")
|
|
1404
|
-
tag = f"
|
|
1503
|
+
tag = f"{_tn.strftime('%Y%m%d%H%M%S')}_{_strategy_name_id}{tag_s}"
|
|
1405
1504
|
return tag
|
|
1406
1505
|
|
|
1407
1506
|
|
|
@@ -228,6 +228,42 @@ class PluginsConfig(StrictBaseModel):
|
|
|
228
228
|
"""Module names to import (for pip-installed packages)."""
|
|
229
229
|
|
|
230
230
|
|
|
231
|
+
class ReleaseSourceConfig(StrictBaseModel):
|
|
232
|
+
"""Source repository for building a release."""
|
|
233
|
+
|
|
234
|
+
repo: str
|
|
235
|
+
"""GitHub org/repo (e.g., 'xLydianSoftware/xincubator')."""
|
|
236
|
+
|
|
237
|
+
ref: str
|
|
238
|
+
"""Git ref to build from — tag, branch, or commit SHA."""
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class ReleasePlatformConfig(StrictBaseModel):
|
|
242
|
+
"""Platform deployment configuration."""
|
|
243
|
+
|
|
244
|
+
name: str
|
|
245
|
+
"""Release name on platform.xlydian.com."""
|
|
246
|
+
|
|
247
|
+
exchanges: list[str] = Field(default_factory=list)
|
|
248
|
+
"""Exchange identifiers (e.g., ['binance'])."""
|
|
249
|
+
|
|
250
|
+
image_tag: str | None = None
|
|
251
|
+
"""Qubx Docker image tag (e.g., '1.1.3.dev16'). Defaults to latest."""
|
|
252
|
+
|
|
253
|
+
tags: list[str] = Field(default_factory=list)
|
|
254
|
+
"""Descriptive tags for the release."""
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class ReleaseConfig(StrictBaseModel):
|
|
258
|
+
"""Configuration for automated release packaging and platform deployment."""
|
|
259
|
+
|
|
260
|
+
source: ReleaseSourceConfig
|
|
261
|
+
"""Source repository and ref to build from."""
|
|
262
|
+
|
|
263
|
+
platform: ReleasePlatformConfig | None = None
|
|
264
|
+
"""Platform deployment settings. If omitted, release is built but not deployed."""
|
|
265
|
+
|
|
266
|
+
|
|
231
267
|
class StrategyConfig(StrictBaseModel):
|
|
232
268
|
name: str | None = None
|
|
233
269
|
description: str | list[str] | None = None
|
|
@@ -238,6 +274,7 @@ class StrategyConfig(StrictBaseModel):
|
|
|
238
274
|
plugins: PluginsConfig | None = None
|
|
239
275
|
live: LiveConfig | None = None
|
|
240
276
|
simulation: SimulationConfig | None = None
|
|
277
|
+
release: ReleaseConfig | None = None
|
|
241
278
|
|
|
242
279
|
|
|
243
280
|
def normalize_aux_config(aux_config: list[StorageConfig] | StorageConfig | None) -> list[StorageConfig]:
|
|
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.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py
RENAMED
|
File without changes
|
{qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qubx-1.1.3.dev17 → qubx-1.1.3.dev18}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py
RENAMED
|
File without changes
|