Qubx 1.1.2.dev6__tar.gz → 1.1.2.dev7__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.2.dev6 → qubx-1.1.2.dev7}/PKG-INFO +1 -1
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/__init__.py +38 -8
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/_version.py +2 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/data.py +3 -4
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/runner.py +4 -6
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/simulated_data.py +17 -17
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/utils.py +17 -8
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/cli/commands.py +1 -11
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/account.py +6 -6
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/broker.py +2 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/data.py +3 -4
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +2 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/handlers/ohlc.py +4 -4
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/utils.py +2 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/tardis/data.py +6 -5
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/basics.py +7 -6
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/detectors/delisting.py +4 -5
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/detectors/stale.py +2 -3
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/helpers.py +5 -5
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/loggers.py +18 -1
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/lookups.py +2 -1
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/metrics.py +30 -27
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/mixins/market.py +5 -3
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/mixins/universe.py +1 -1
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/cache.py +8 -8
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/guards.py +7 -6
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/storages/ccxt.py +5 -5
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/storages/multi.py +2 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/storages/questdb.py +4 -4
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/storages/utils.py +3 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/transformers.py +4 -4
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/emitters/base.py +2 -3
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/emitters/indicator.py +2 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/emitters/inmemory.py +3 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/emitters/questdb.py +2 -1
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/exporters/formatters/base.py +3 -4
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/pandaz/ta.py +7 -7
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restarts/time_finders.py +24 -1
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/trackers/advanced.py +3 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/trackers/sizers.py +2 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/charting/lookinglass.py +4 -3
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/charting/mpl_helpers.py +2 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/misc.py +4 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/results.py +2 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/configs.py +1 -2
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/runner.py +15 -21
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/init_code.py +16 -16
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/time.py +19 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/.gitignore +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/LICENSE +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/README.md +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/pyproject.toml +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/scripts/build.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/_nb_magic.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/account.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/broker.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/iteratedstream.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/management.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/ome.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/optimization.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/sentinels.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/simulated_exchange.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/simulator.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/backtester/transfers.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/cli/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/cli/deploy.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/cli/misc.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/cli/release.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/cli/resolver.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/cli/s3.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/cli/theme.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/cli/tui.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/cli/user_config.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/config.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exceptions.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/okx/account.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/okx/broker.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/factory.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/registry.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/connectors/tardis/utils.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/account.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/context.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/detectors/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/errors.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/exceptions.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/initializer.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/interfaces.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/mixins/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/mixins/processing.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/mixins/subscription.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/mixins/trading.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/mixins/utils.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/series.pxd +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/series.pyi +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/series.pyx +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/utils.pyi +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/core/utils.pyx +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/containers.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/registry.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/storage.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/storages/csv.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/storages/handy.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/data/storages/stub.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/emitters/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/emitters/composite.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/emitters/csv.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/emitters/prometheus.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/exporters/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/exporters/composite.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/exporters/formatters/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/exporters/formatters/incremental.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/exporters/formatters/slack.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/exporters/formatters/target_position.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/exporters/redis_streams.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/exporters/slack.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/gathering/simplest.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/health/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/health/base.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/health/dummy.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/health/server.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/loggers/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/loggers/csv.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/loggers/factory.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/loggers/inmemory.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/loggers/mongo.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/notifications/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/notifications/composite.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/notifications/slack.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/notifications/throttler.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/pandaz/stats.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/pandaz/utils.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/plugins/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/plugins/loader.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/_build.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/crypto-fees.ini +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restarts/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restarts/state_resolvers.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restorers/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restorers/balance.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restorers/factory.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restorers/interfaces.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restorers/position.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restorers/signal.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restorers/state.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/restorers/utils.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/state/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/state/dummy.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/state/redis.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/ta/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/ta/indicators.pxd +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/ta/indicators.pyi +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/ta/indicators.pyx +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/base.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/project/accounts.toml.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/project/config.yml.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/project/jlive.sh.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/project/template.yml +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/.claude/skills/simulation-explorer/SKILL.md +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/.gitignore.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/.python-version +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/.vscode/launch.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/.vscode/settings.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/.vscode/tasks.json +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/CLAUDE.md.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/README.md.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/configs/.gitkeep +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/justfile.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/pyproject.toml.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/research/.gitkeep +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/template.yml +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/repo/tests/test_strategy.py.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/simple/__init__.py.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/simple/config.yml.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/simple/strategy.py.j2 +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/templates/simple/template.yml +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/trackers/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/trackers/composite.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/trackers/riskctrl.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/charting/orderbook.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/collections.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/hft/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/hft/numba_utils.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/hft/orderbook.pyi +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/hft/orderbook.pyx +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/marketdata/ccxt.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/marketdata/dukas.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/nonce.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/ntp.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/numbers_utils.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/orderbook.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/plotting/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/plotting/dashboard.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/plotting/data.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/plotting/interfaces.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/questdb.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/rate_limiter.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/ringbuffer.pxd +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/ringbuffer.pyi +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/ringbuffer.pyx +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/accounts.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/factory.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/kernel_service.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/app.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/handlers.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/kernel.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/styles.tcss +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/widgets/account_summary.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/s3.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/slack.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/throttler.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/src/qubx/utils/websocket_manager.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
- {qubx-1.1.2.dev6 → qubx-1.1.2.dev7}/tests/strategies/obi_trader/src/obi_trader/models/obi_trader.py +0 -0
|
@@ -47,6 +47,16 @@ def format_platform_identity(record) -> str:
|
|
|
47
47
|
return ""
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
def format_phase(record) -> str:
|
|
51
|
+
"""Return a colored phase tag from record extras."""
|
|
52
|
+
phase = record["extra"].get("phase")
|
|
53
|
+
if phase == "warmup":
|
|
54
|
+
return "<yellow>[WARMUP]</yellow> "
|
|
55
|
+
elif phase == "live":
|
|
56
|
+
return "<green>[LIVE]</green> "
|
|
57
|
+
return ""
|
|
58
|
+
|
|
59
|
+
|
|
50
60
|
def formatter(record):
|
|
51
61
|
end = record["extra"].get("end", "\n")
|
|
52
62
|
fmt = "<lvl>{message}</lvl>%s" % end
|
|
@@ -54,9 +64,10 @@ def formatter(record):
|
|
|
54
64
|
fmt = "<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - %s" % fmt
|
|
55
65
|
|
|
56
66
|
identity = format_platform_identity(record)
|
|
67
|
+
phase = format_phase(record)
|
|
57
68
|
prefix = (
|
|
58
|
-
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> [ <level>%s</level> ] %s<cyan>({module})</cyan> "
|
|
59
|
-
% (record["level"].icon, identity)
|
|
69
|
+
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> [ <level>%s</level> ] %s%s<cyan>({module})</cyan> "
|
|
70
|
+
% (record["level"].icon, identity, phase)
|
|
60
71
|
)
|
|
61
72
|
|
|
62
73
|
if record["exception"] is not None:
|
|
@@ -158,17 +169,36 @@ class QubxLogConfig:
|
|
|
158
169
|
)
|
|
159
170
|
logger = logger.opt(colors=colorize)
|
|
160
171
|
|
|
172
|
+
_bot_id: str | None = None
|
|
173
|
+
_instance_id: str | None = None
|
|
174
|
+
_phase: str | None = None
|
|
175
|
+
|
|
161
176
|
@staticmethod
|
|
162
|
-
def
|
|
163
|
-
"""
|
|
177
|
+
def _update_patcher():
|
|
178
|
+
"""Reconfigure loguru with a single patcher that applies all bound fields."""
|
|
164
179
|
def patcher(record):
|
|
165
|
-
if
|
|
166
|
-
record["extra"]["bot_id"] =
|
|
167
|
-
if
|
|
168
|
-
record["extra"]["instance_id"] =
|
|
180
|
+
if QubxLogConfig._bot_id:
|
|
181
|
+
record["extra"]["bot_id"] = QubxLogConfig._bot_id
|
|
182
|
+
if QubxLogConfig._instance_id:
|
|
183
|
+
record["extra"]["instance_id"] = QubxLogConfig._instance_id
|
|
184
|
+
if QubxLogConfig._phase:
|
|
185
|
+
record["extra"]["phase"] = QubxLogConfig._phase
|
|
169
186
|
|
|
170
187
|
logger.configure(patcher=patcher)
|
|
171
188
|
|
|
189
|
+
@staticmethod
|
|
190
|
+
def bind_platform_identity(bot_id: str | None = None, instance_id: str | None = None):
|
|
191
|
+
"""Bind platform identity fields (bot_id, instance_id) to all log messages globally."""
|
|
192
|
+
QubxLogConfig._bot_id = bot_id
|
|
193
|
+
QubxLogConfig._instance_id = instance_id
|
|
194
|
+
QubxLogConfig._update_patcher()
|
|
195
|
+
|
|
196
|
+
@staticmethod
|
|
197
|
+
def bind_phase(phase: str | None):
|
|
198
|
+
"""Bind phase (warmup/live) to all log messages globally."""
|
|
199
|
+
QubxLogConfig._phase = phase
|
|
200
|
+
QubxLogConfig._update_patcher()
|
|
201
|
+
|
|
172
202
|
|
|
173
203
|
QubxLogConfig.setup_logger()
|
|
174
204
|
|
|
@@ -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.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 1, 2, '
|
|
21
|
+
__version__ = version = '1.1.2.dev7'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 1, 2, 'dev7')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
from collections import defaultdict
|
|
2
2
|
from typing import TypeVar
|
|
3
3
|
|
|
4
|
-
import pandas as pd
|
|
5
|
-
|
|
6
4
|
from qubx import logger
|
|
7
5
|
from qubx.backtester.simulated_data import SimulatedDataIterator
|
|
8
6
|
from qubx.core.basics import (
|
|
@@ -12,6 +10,7 @@ from qubx.core.basics import (
|
|
|
12
10
|
)
|
|
13
11
|
from qubx.core.interfaces import IDataProvider
|
|
14
12
|
from qubx.core.series import Bar, Quote
|
|
13
|
+
from qubx.utils.time import to_timedelta, to_timestamp
|
|
15
14
|
|
|
16
15
|
from .account import SimulatedAccountProcessor
|
|
17
16
|
from .utils import SimulatedTimeProvider
|
|
@@ -144,8 +143,8 @@ class SimulatedDataProvider(IDataProvider):
|
|
|
144
143
|
self._data_source.set_warmup_period(si[0], warm_period)
|
|
145
144
|
|
|
146
145
|
def get_ohlc(self, instrument: Instrument, timeframe: str, nbarsback: int) -> list[Bar]:
|
|
147
|
-
start =
|
|
148
|
-
end = start - nbarsback *
|
|
146
|
+
start = to_timestamp(self.time_provider.time())
|
|
147
|
+
end = start - nbarsback * to_timedelta(timeframe)
|
|
149
148
|
return self._data_source.get_ohlc(instrument, timeframe, start, end)
|
|
150
149
|
|
|
151
150
|
def get_quote(self, instrument: Instrument) -> Quote | None:
|
|
@@ -34,7 +34,7 @@ from qubx.health import DummyHealthMonitor
|
|
|
34
34
|
from qubx.loggers.inmemory import InMemoryLogsWriter
|
|
35
35
|
from qubx.pandaz.utils import _frame_to_str
|
|
36
36
|
from qubx.utils.runner.configs import PrefetchConfig
|
|
37
|
-
from qubx.utils.time import now_ns
|
|
37
|
+
from qubx.utils.time import now_ns, to_timedelta
|
|
38
38
|
|
|
39
39
|
from .account import SimulatedAccountProcessor
|
|
40
40
|
from .broker import SimulatedBroker
|
|
@@ -437,7 +437,7 @@ class SimulationRunner:
|
|
|
437
437
|
# - TimeGuard is applied by callers that need it (aux/strategy-facing access).
|
|
438
438
|
# - Main simulation data (DataPump) must NOT be time-guarded: DataPump provides
|
|
439
439
|
# its own explicit [start, end] window and needs to read the full range upfront.
|
|
440
|
-
_prefetch_period = str(min(self.stop - self.start,
|
|
440
|
+
_prefetch_period = str(min(self.stop - self.start, to_timedelta(prefetch_cfg.prefetch_period)))
|
|
441
441
|
return CachedStorage(
|
|
442
442
|
storage, prefetch_period=_prefetch_period, cache_factory=lambda: MemoryCache(prefetch_cfg.cache_size_mb)
|
|
443
443
|
)
|
|
@@ -611,10 +611,8 @@ class SimulationRunner:
|
|
|
611
611
|
|
|
612
612
|
_account_processors = {}
|
|
613
613
|
for exchange in self.setup.exchanges:
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
_initial_capital = _initial_capital[exchange]
|
|
617
|
-
assert isinstance(_initial_capital, (float, int))
|
|
614
|
+
assert isinstance(self.setup.capital, dict)
|
|
615
|
+
_initial_capital = self.setup.capital[exchange]
|
|
618
616
|
_account_processors[exchange] = SimulatedAccountProcessor(
|
|
619
617
|
account_id=self.account_id,
|
|
620
618
|
exchange=_exchange_to_simulated_exchange[exchange],
|
|
@@ -16,7 +16,7 @@ from qubx.data.containers import IRawContainer, RawData, RawMultiData
|
|
|
16
16
|
from qubx.data.storage import IDataTransformer, IReader, IStorage, Transformable
|
|
17
17
|
from qubx.data.storages.utils import find_column_index_in_list
|
|
18
18
|
from qubx.data.transformers import TypedRecords
|
|
19
|
-
from qubx.utils.time import convert_times_to_ns, infer_series_frequency
|
|
19
|
+
from qubx.utils.time import convert_times_to_ns, infer_series_frequency, to_timedelta, to_timestamp
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class EmulatedUpdatesFromOHLC(IDataTransformer):
|
|
@@ -26,7 +26,7 @@ class EmulatedUpdatesFromOHLC(IDataTransformer):
|
|
|
26
26
|
|
|
27
27
|
@staticmethod
|
|
28
28
|
def timedelta_to_numpy(x: str) -> int:
|
|
29
|
-
return
|
|
29
|
+
return to_timedelta(x).to_numpy().item()
|
|
30
30
|
|
|
31
31
|
D1, H1 = timedelta_to_numpy("1D"), timedelta_to_numpy("1h")
|
|
32
32
|
MS1 = 1_000_000
|
|
@@ -329,9 +329,9 @@ class RawSymbolBuffer:
|
|
|
329
329
|
if isinstance(value, pd.Timestamp):
|
|
330
330
|
return value.value
|
|
331
331
|
if isinstance(value, np.datetime64):
|
|
332
|
-
return
|
|
332
|
+
return to_timestamp(value).value
|
|
333
333
|
# - fallback: try to convert via pd.Timestamp
|
|
334
|
-
return
|
|
334
|
+
return to_timestamp(value).value
|
|
335
335
|
|
|
336
336
|
def activate(self):
|
|
337
337
|
self._active = True
|
|
@@ -367,10 +367,10 @@ class RawSymbolBuffer:
|
|
|
367
367
|
# - dedup: filter out rows already in buffer using native pyarrow comparison
|
|
368
368
|
if self._watermark > 0:
|
|
369
369
|
if pa.types.is_timestamp(time_col.type):
|
|
370
|
-
wm_scalar = pa.scalar(
|
|
370
|
+
wm_scalar = pa.scalar(to_timestamp(self._watermark, unit="ns"), type=time_col.type)
|
|
371
371
|
elif pa.types.is_date(time_col.type):
|
|
372
372
|
# - date32 stores days since epoch; watermark is ns, convert to datetime.date
|
|
373
|
-
wm_scalar = pa.scalar(
|
|
373
|
+
wm_scalar = pa.scalar(to_timestamp(self._watermark, unit="ns").date(), type=time_col.type)
|
|
374
374
|
else:
|
|
375
375
|
wm_scalar = pa.scalar(self._watermark, type=time_col.type)
|
|
376
376
|
mask = pa.compute.greater(time_col, wm_scalar)
|
|
@@ -398,7 +398,7 @@ class RawSymbolBuffer:
|
|
|
398
398
|
return len(self._batches)
|
|
399
399
|
|
|
400
400
|
def __repr__(self) -> str:
|
|
401
|
-
_wm =
|
|
401
|
+
_wm = to_timestamp(self._watermark, unit="ns") if self._watermark else "N/A"
|
|
402
402
|
return f"RawSymbolBuffer({self._symbol}, chunks={len(self)}, wm={_wm}, active={self._active})"
|
|
403
403
|
|
|
404
404
|
|
|
@@ -644,13 +644,13 @@ class DataPump:
|
|
|
644
644
|
Starts backend reader, creates MemReaders for all symbols.
|
|
645
645
|
Returns dict suitable for slicer.put().
|
|
646
646
|
"""
|
|
647
|
-
self._end =
|
|
647
|
+
self._end = to_timestamp(end)
|
|
648
648
|
symbols = self._active_symbols()
|
|
649
649
|
if not symbols:
|
|
650
650
|
return {}
|
|
651
651
|
|
|
652
652
|
# - all symbols need warmup on first read
|
|
653
|
-
read_start =
|
|
653
|
+
read_start = to_timestamp(start)
|
|
654
654
|
if self._warmup_period:
|
|
655
655
|
read_start = read_start - self._warmup_period
|
|
656
656
|
self._warmed.update(symbols)
|
|
@@ -686,7 +686,7 @@ class DataPump:
|
|
|
686
686
|
dict of NEW MemReaders only (for slicer.put())
|
|
687
687
|
"""
|
|
688
688
|
if end is not None:
|
|
689
|
-
self._end =
|
|
689
|
+
self._end = to_timestamp(end)
|
|
690
690
|
|
|
691
691
|
symbols = self._active_symbols()
|
|
692
692
|
if not symbols:
|
|
@@ -695,7 +695,7 @@ class DataPump:
|
|
|
695
695
|
|
|
696
696
|
# - determine start time: extend into past if new symbols need warmup
|
|
697
697
|
new_symbols = [s for s in symbols if s not in self._warmed]
|
|
698
|
-
read_start =
|
|
698
|
+
read_start = to_timestamp(current_time)
|
|
699
699
|
|
|
700
700
|
if new_symbols and self._warmup_period:
|
|
701
701
|
read_start = read_start - self._warmup_period
|
|
@@ -876,7 +876,7 @@ class SimulatedDataIterator(Iterator):
|
|
|
876
876
|
def set_warmup_period(self, subscription: str, warmup_period: str | None = None):
|
|
877
877
|
if warmup_period:
|
|
878
878
|
access_key, _, _ = self._parse_subscription_spec(subscription)
|
|
879
|
-
self._warmups[access_key] =
|
|
879
|
+
self._warmups[access_key] = to_timedelta(warmup_period)
|
|
880
880
|
|
|
881
881
|
def _parse_subscription_spec(self, subscription: str) -> tuple[str, str, dict[str, object]]:
|
|
882
882
|
_subtype, _params = DataType.from_str(subscription)
|
|
@@ -999,7 +999,7 @@ class SimulatedDataIterator(Iterator):
|
|
|
999
999
|
|
|
1000
1000
|
# - if simulation is running, restart read to include new symbols
|
|
1001
1001
|
if self.is_running and new_instruments:
|
|
1002
|
-
new_mem_readers = pump.restart_read(
|
|
1002
|
+
new_mem_readers = pump.restart_read(to_timestamp(self._current_time, unit="ns"), self._stop) # type: ignore
|
|
1003
1003
|
if new_mem_readers and self._slicer is not None:
|
|
1004
1004
|
self._slicer.put(new_mem_readers)
|
|
1005
1005
|
|
|
@@ -1101,8 +1101,8 @@ class SimulatedDataIterator(Iterator):
|
|
|
1101
1101
|
return self._current_time is not None
|
|
1102
1102
|
|
|
1103
1103
|
def create_iterable(self, start: str | pd.Timestamp, stop: str | pd.Timestamp) -> Iterator:
|
|
1104
|
-
self._start =
|
|
1105
|
-
self._stop =
|
|
1104
|
+
self._start = to_timestamp(start)
|
|
1105
|
+
self._stop = to_timestamp(stop)
|
|
1106
1106
|
self._current_time = None
|
|
1107
1107
|
self._slicer = IteratedDataStreamsSlicer()
|
|
1108
1108
|
return self
|
|
@@ -1110,7 +1110,7 @@ class SimulatedDataIterator(Iterator):
|
|
|
1110
1110
|
def __iter__(self) -> Iterator:
|
|
1111
1111
|
assert self._start is not None and self._stop is not None
|
|
1112
1112
|
assert self._slicer is not None
|
|
1113
|
-
self._current_time = int(
|
|
1113
|
+
self._current_time = int(to_timestamp(self._start).timestamp() * 1e9)
|
|
1114
1114
|
|
|
1115
1115
|
# - initial read for all pumps
|
|
1116
1116
|
for pump in self._pumps.values():
|
|
@@ -1160,7 +1160,7 @@ class SimulatedDataIterator(Iterator):
|
|
|
1160
1160
|
assert isinstance(t_records, Transformable)
|
|
1161
1161
|
|
|
1162
1162
|
return self._process_bar_records(
|
|
1163
|
-
t_records.transform(TypedRecords()), time_as_nsec(start),
|
|
1163
|
+
t_records.transform(TypedRecords()), time_as_nsec(start), to_timedelta(timeframe).asm8.item()
|
|
1164
1164
|
)
|
|
1165
1165
|
|
|
1166
1166
|
def _process_bar_records(self, records: list[Bar], cut_time_ns: int, timeframe_ns: int) -> list[Bar]:
|
|
@@ -25,6 +25,7 @@ from qubx.core.utils import time_delta_to_str
|
|
|
25
25
|
from qubx.data.storage import IStorage
|
|
26
26
|
from qubx.utils.misc import safe_dtype_timeframe
|
|
27
27
|
from qubx.utils.runner.configs import PrefetchConfig
|
|
28
|
+
from qubx.utils.time import to_timedelta
|
|
28
29
|
|
|
29
30
|
SymbolOrInstrument_t: TypeAlias = str | Instrument
|
|
30
31
|
ExchangeName_t: TypeAlias = str
|
|
@@ -45,7 +46,7 @@ StrategiesDecls_t: TypeAlias = (
|
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
def _timedelta_to_numpy(x: str | int) -> int:
|
|
48
|
-
return
|
|
49
|
+
return to_timedelta(x).to_numpy().item()
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
DEFAULT_DAILY_SESSION = (_timedelta_to_numpy("00:00:00.100"), _timedelta_to_numpy("23:59:59.900"))
|
|
@@ -98,6 +99,13 @@ class SimulationSetup:
|
|
|
98
99
|
accurate_stop_orders_execution: bool = False
|
|
99
100
|
enable_funding: bool = False
|
|
100
101
|
|
|
102
|
+
def __post_init__(self) -> None:
|
|
103
|
+
# Normalize capital to a per-exchange dict: split evenly when a single float is given
|
|
104
|
+
if isinstance(self.capital, (int, float)):
|
|
105
|
+
n = len(self.exchanges)
|
|
106
|
+
per_exchange = float(self.capital) / n if n > 0 else float(self.capital)
|
|
107
|
+
self.capital = {exchange: per_exchange for exchange in self.exchanges}
|
|
108
|
+
|
|
101
109
|
def __str__(self) -> str:
|
|
102
110
|
return f"{self.name} {self.setup_type} capital {self.capital} {self.base_currency} for [{','.join(map(lambda x: x.symbol, self.instruments))}] @ {self.exchanges}[{self.commissions}]"
|
|
103
111
|
|
|
@@ -133,10 +141,11 @@ class SimulatedLogFormatter:
|
|
|
133
141
|
else:
|
|
134
142
|
now = self.time_provider.time().astype("datetime64[us]").item().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
|
135
143
|
|
|
136
|
-
from qubx import format_platform_identity
|
|
144
|
+
from qubx import format_phase, format_platform_identity
|
|
137
145
|
|
|
138
146
|
identity = format_platform_identity(record)
|
|
139
|
-
|
|
147
|
+
phase = format_phase(record)
|
|
148
|
+
prefix = f"<lc>{now}</lc> [<level>{record['level'].icon}</level>] {identity}{phase}<cyan>({{module}})</cyan> "
|
|
140
149
|
|
|
141
150
|
if record["exception"] is not None:
|
|
142
151
|
record["extra"]["stack"] = stackprinter.format(record["exception"], style="darkbg3")
|
|
@@ -521,15 +530,15 @@ def _adjust_open_close_time_indent_secs(timeframe: pd.Timedelta | None, original
|
|
|
521
530
|
return original_indent_secs
|
|
522
531
|
|
|
523
532
|
# - if it triggers at daily+ bar let's assume this bar is 'closed' 5 min before exact closing time
|
|
524
|
-
if timeframe >=
|
|
533
|
+
if timeframe >= to_timedelta("1d"):
|
|
525
534
|
return max(original_indent_secs, 5 * 60)
|
|
526
535
|
|
|
527
536
|
# - if it triggers at 1Min+ bar let's assume this bar is 'closed' 5 sec before exact closing time
|
|
528
|
-
if timeframe >=
|
|
537
|
+
if timeframe >= to_timedelta("1min"):
|
|
529
538
|
return max(original_indent_secs, 5)
|
|
530
539
|
|
|
531
540
|
# - for all sub-minute timeframes just use 1 sec shift
|
|
532
|
-
if timeframe >
|
|
541
|
+
if timeframe > to_timedelta("1s"):
|
|
533
542
|
return max(original_indent_secs, 1)
|
|
534
543
|
|
|
535
544
|
# - for rest just keep original indent
|
|
@@ -538,9 +547,9 @@ def _adjust_open_close_time_indent_secs(timeframe: pd.Timedelta | None, original
|
|
|
538
547
|
|
|
539
548
|
def _get_default_warmup_period(base_subscription: str, in_timeframe: pd.Timedelta | None) -> pd.Timedelta:
|
|
540
549
|
if in_timeframe is None or base_subscription in [DataType.QUOTE, DataType.TRADE, DataType.ORDERBOOK]:
|
|
541
|
-
return
|
|
550
|
+
return to_timedelta("1Min")
|
|
542
551
|
|
|
543
|
-
if in_timeframe <
|
|
552
|
+
if in_timeframe < to_timedelta("1h"):
|
|
544
553
|
return 5 * in_timeframe
|
|
545
554
|
|
|
546
555
|
return 2 * in_timeframe
|
|
@@ -230,15 +230,6 @@ def run(
|
|
|
230
230
|
@click.option(
|
|
231
231
|
"--report", "-r", default=None, type=str, help="Output directory for simulation reports.", show_default=True
|
|
232
232
|
)
|
|
233
|
-
@click.option(
|
|
234
|
-
"--log",
|
|
235
|
-
"-L",
|
|
236
|
-
"log_to_file",
|
|
237
|
-
is_flag=True,
|
|
238
|
-
default=False,
|
|
239
|
-
help="Write simulation logs to a file in the output directory.",
|
|
240
|
-
show_default=True,
|
|
241
|
-
)
|
|
242
233
|
@click.option(
|
|
243
234
|
"--name",
|
|
244
235
|
"-n",
|
|
@@ -261,7 +252,6 @@ def simulate(
|
|
|
261
252
|
end: str | None,
|
|
262
253
|
output: str | None,
|
|
263
254
|
report: str | None,
|
|
264
|
-
log_to_file: bool,
|
|
265
255
|
name: str | None,
|
|
266
256
|
log_file: str | None,
|
|
267
257
|
):
|
|
@@ -276,7 +266,7 @@ def simulate(
|
|
|
276
266
|
logo()
|
|
277
267
|
logger.info(f"Process PID: <g>{os.getpid()}</g>")
|
|
278
268
|
simulate_strategy(
|
|
279
|
-
config_file, output, start, end, report,
|
|
269
|
+
config_file, output, start, end, report, name=name, log_file=log_file,
|
|
280
270
|
)
|
|
281
271
|
|
|
282
272
|
|
|
@@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Awaitable, Callable
|
|
|
7
7
|
|
|
8
8
|
import ccxt.pro as cxp
|
|
9
9
|
import numpy as np
|
|
10
|
-
import pandas as pd
|
|
11
10
|
from ccxt import ExchangeClosedByUser, ExchangeError, ExchangeNotAvailable, NetworkError
|
|
12
11
|
|
|
13
12
|
from qubx import logger
|
|
@@ -29,6 +28,7 @@ from qubx.core.helpers import extract_price
|
|
|
29
28
|
from qubx.core.interfaces import IHealthMonitor, ISubscriptionManager
|
|
30
29
|
from qubx.utils.marketdata.ccxt import ccxt_symbol_to_instrument
|
|
31
30
|
from qubx.utils.misc import AsyncThreadLoop
|
|
31
|
+
from qubx.utils.time import to_timedelta
|
|
32
32
|
|
|
33
33
|
from .exceptions import CcxtSymbolNotRecognized
|
|
34
34
|
from .exchange_manager import ExchangeManager
|
|
@@ -252,11 +252,11 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
252
252
|
interval: str,
|
|
253
253
|
backoff: str | None = None,
|
|
254
254
|
):
|
|
255
|
-
sleep_time =
|
|
255
|
+
sleep_time = to_timedelta(interval).total_seconds()
|
|
256
256
|
retries = 0
|
|
257
257
|
|
|
258
258
|
if backoff is not None:
|
|
259
|
-
sleep_time =
|
|
259
|
+
sleep_time = to_timedelta(backoff).total_seconds()
|
|
260
260
|
await asyncio.sleep(sleep_time)
|
|
261
261
|
|
|
262
262
|
while self.channel.control.is_set():
|
|
@@ -308,7 +308,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
308
308
|
async def _update_subscriptions(self) -> None:
|
|
309
309
|
"""Subscribe to required instruments"""
|
|
310
310
|
assert self._subscription_manager is not None
|
|
311
|
-
await asyncio.sleep(
|
|
311
|
+
await asyncio.sleep(to_timedelta(self.subscription_interval).total_seconds())
|
|
312
312
|
|
|
313
313
|
# if required instruments have changed, subscribe to them
|
|
314
314
|
if not self._latest_instruments.issuperset(self._required_instruments):
|
|
@@ -380,7 +380,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
380
380
|
current_pos.set_external_maint_margin(new_pos.maint_margin)
|
|
381
381
|
|
|
382
382
|
def _get_start_time_in_ms(self, days_before: int) -> int:
|
|
383
|
-
return (self.time_provider.time() - days_before *
|
|
383
|
+
return (self.time_provider.time() - days_before * to_timedelta("1d")).asm8.item() // 1000000 # type: ignore
|
|
384
384
|
|
|
385
385
|
def _is_our_order(self, order: Order) -> bool:
|
|
386
386
|
if order.client_id is None:
|
|
@@ -408,7 +408,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
|
|
|
408
408
|
_fetch_instruments: list[Instrument] = []
|
|
409
409
|
for instr in instruments:
|
|
410
410
|
_dt, _ = self._instrument_to_last_price.get(instr, (None, None))
|
|
411
|
-
if _dt is None or
|
|
411
|
+
if _dt is None or to_timedelta(_current_time - _dt) > to_timedelta(self.balance_interval): # type: ignore
|
|
412
412
|
_fetch_instruments.append(instr)
|
|
413
413
|
|
|
414
414
|
_symbol_to_instrument = {instr.symbol: instr for instr in instruments}
|
|
@@ -3,7 +3,6 @@ import traceback
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
5
|
import ccxt
|
|
6
|
-
import pandas as pd
|
|
7
6
|
from ccxt.base.errors import ExchangeError
|
|
8
7
|
|
|
9
8
|
from qubx import logger
|
|
@@ -24,6 +23,7 @@ from qubx.core.interfaces import (
|
|
|
24
23
|
ITimeProvider,
|
|
25
24
|
)
|
|
26
25
|
from qubx.utils.misc import AsyncThreadLoop
|
|
26
|
+
from qubx.utils.time import to_timedelta
|
|
27
27
|
|
|
28
28
|
from .exchange_manager import ExchangeManager
|
|
29
29
|
from .utils import ccxt_convert_order_info, instrument_to_ccxt_symbol
|
|
@@ -545,7 +545,7 @@ class CcxtBroker(IBroker):
|
|
|
545
545
|
|
|
546
546
|
# Common retry logic for all retryable errors
|
|
547
547
|
current_time = self.time_provider.time()
|
|
548
|
-
elapsed_seconds =
|
|
548
|
+
elapsed_seconds = to_timedelta(current_time - start_time).total_seconds()
|
|
549
549
|
retries += 1
|
|
550
550
|
|
|
551
551
|
if elapsed_seconds >= timeout_delta or retries >= self.max_cancel_retries:
|
|
@@ -2,8 +2,6 @@ import asyncio
|
|
|
2
2
|
from collections import defaultdict
|
|
3
3
|
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
|
|
4
4
|
|
|
5
|
-
import pandas as pd
|
|
6
|
-
|
|
7
5
|
# CCXT exceptions are now handled in ConnectionManager
|
|
8
6
|
from qubx import logger
|
|
9
7
|
from qubx.connectors.ccxt.utils import ccxt_convert_timeframe_to_exchange_format
|
|
@@ -12,6 +10,7 @@ from qubx.core.basics import CtrlChannel, DataType, Instrument, ITimeProvider
|
|
|
12
10
|
from qubx.core.interfaces import IDataProvider, IHealthMonitor
|
|
13
11
|
from qubx.core.series import Bar, Quote
|
|
14
12
|
from qubx.utils.misc import AsyncThreadLoop
|
|
13
|
+
from qubx.utils.time import to_timedelta, to_timestamp
|
|
15
14
|
|
|
16
15
|
from .connection_manager import ConnectionManager
|
|
17
16
|
from .exchange_manager import ExchangeManager
|
|
@@ -319,8 +318,8 @@ class CcxtDataProvider(IDataProvider):
|
|
|
319
318
|
return _key is None or _key == ""
|
|
320
319
|
|
|
321
320
|
def _time_msec_nbars_back(self, timeframe: str, nbarsback: int = 1) -> int:
|
|
322
|
-
now =
|
|
323
|
-
delta =
|
|
321
|
+
now = to_timestamp(self.time_provider.time())
|
|
322
|
+
delta = to_timedelta(timeframe) * nbarsback
|
|
324
323
|
return int((now - delta).value // 1_000_000)
|
|
325
324
|
|
|
326
325
|
def _get_exch_timeframe(self, timeframe: str) -> str:
|
|
@@ -16,7 +16,7 @@ from ccxt.base.types import (
|
|
|
16
16
|
Strings,
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
-
from qubx.utils.time import now_utc
|
|
19
|
+
from qubx.utils.time import now_utc, to_timedelta
|
|
20
20
|
|
|
21
21
|
from ..base import CcxtFuturePatchMixin
|
|
22
22
|
|
|
@@ -418,7 +418,7 @@ class BinanceQVUSDM(cxp.binanceusdm, BinanceQV):
|
|
|
418
418
|
super().__init__(*args, **kwargs)
|
|
419
419
|
self._funding_intervals = {}
|
|
420
420
|
self._funding_intervals_updated_at = None
|
|
421
|
-
self._funding_interval_cache_ttl =
|
|
421
|
+
self._funding_interval_cache_ttl = to_timedelta(minutes=10)
|
|
422
422
|
|
|
423
423
|
def describe(self):
|
|
424
424
|
"""
|
|
@@ -7,13 +7,13 @@ Handles subscription and warmup for OHLC (candlestick) data.
|
|
|
7
7
|
import asyncio
|
|
8
8
|
from typing import Set
|
|
9
9
|
|
|
10
|
-
import pandas as pd
|
|
11
10
|
from ccxt import BadSymbol
|
|
12
11
|
|
|
13
12
|
from qubx import logger
|
|
14
13
|
from qubx.core.basics import CtrlChannel, DataType, Instrument
|
|
15
14
|
from qubx.core.exceptions import NotSupported
|
|
16
15
|
from qubx.core.series import Bar, Quote
|
|
16
|
+
from qubx.utils.time import to_timedelta
|
|
17
17
|
|
|
18
18
|
from ..subscription_config import SubscriptionConfiguration
|
|
19
19
|
from ..utils import ccxt_find_instrument, create_market_type_batched_subscriber, instrument_to_ccxt_symbol
|
|
@@ -89,9 +89,9 @@ class OhlcDataHandler(BaseDataTypeHandler):
|
|
|
89
89
|
warmup_period: Period to warm up (e.g., "30d", "1000h")
|
|
90
90
|
timeframe: Timeframe for OHLC data (e.g., "1m", "5m", "1h")
|
|
91
91
|
"""
|
|
92
|
-
nbarsback =
|
|
92
|
+
nbarsback = to_timedelta(warmup_period) // to_timedelta(timeframe)
|
|
93
93
|
exch_timeframe = self._data_provider._get_exch_timeframe(timeframe)
|
|
94
|
-
_tf_msec =
|
|
94
|
+
_tf_msec = to_timedelta(timeframe).value // 1_000_000
|
|
95
95
|
|
|
96
96
|
for instrument in instruments:
|
|
97
97
|
start_since = self._data_provider._time_msec_nbars_back(timeframe, nbarsback)
|
|
@@ -149,7 +149,7 @@ class OhlcDataHandler(BaseDataTypeHandler):
|
|
|
149
149
|
exch_timeframe = self._data_provider._get_exch_timeframe(timeframe)
|
|
150
150
|
|
|
151
151
|
loaded_bars = {}
|
|
152
|
-
_tf_msec =
|
|
152
|
+
_tf_msec = to_timedelta(timeframe).value // 1_000_000 # convert to msec
|
|
153
153
|
|
|
154
154
|
# - retrieve OHLC data from exchange by chunks as some providers limit number of bars per request
|
|
155
155
|
# Loop until we have enough bars or exchange stops returning data.
|
|
@@ -26,7 +26,7 @@ from qubx.utils.marketdata.ccxt import (
|
|
|
26
26
|
ccxt_symbol_to_instrument,
|
|
27
27
|
)
|
|
28
28
|
from qubx.utils.orderbook import accumulate_orderbook_levels
|
|
29
|
-
from qubx.utils.time import now_utc
|
|
29
|
+
from qubx.utils.time import now_utc, to_timestamp
|
|
30
30
|
|
|
31
31
|
from .exceptions import (
|
|
32
32
|
CcxtLiquidationParsingError,
|
|
@@ -103,7 +103,7 @@ def ccxt_convert_deal_info(raw: Dict[str, Any]) -> Deal:
|
|
|
103
103
|
return Deal(
|
|
104
104
|
id=raw["id"],
|
|
105
105
|
order_id=raw["order"],
|
|
106
|
-
time=
|
|
106
|
+
time=to_timestamp(raw["timestamp"], unit="ms"),
|
|
107
107
|
amount=float(raw["amount"]) * (-1 if raw["side"] == "sell" else +1),
|
|
108
108
|
price=float(raw["price"]),
|
|
109
109
|
aggressive=raw["takerOrMaker"] == "taker",
|
|
@@ -15,6 +15,7 @@ from qubx.core.interfaces import IDataProvider, IHealthMonitor
|
|
|
15
15
|
from qubx.core.series import Bar, Quote, Trade
|
|
16
16
|
from qubx.health import DummyHealthMonitor
|
|
17
17
|
from qubx.utils.misc import AsyncThreadLoop, synchronized
|
|
18
|
+
from qubx.utils.time import to_timedelta
|
|
18
19
|
|
|
19
20
|
from .utils import (
|
|
20
21
|
tardis_convert_orderbook,
|
|
@@ -244,7 +245,7 @@ class TardisDataProvider(IDataProvider):
|
|
|
244
245
|
|
|
245
246
|
# Calculate start time based on timeframe and nbarsback
|
|
246
247
|
end_time = self.time_provider.time()
|
|
247
|
-
start_time = end_time -
|
|
248
|
+
start_time = end_time - to_timedelta(timeframe) * nbarsback
|
|
248
249
|
|
|
249
250
|
# Format dates for Tardis API
|
|
250
251
|
start_str = pd.Timestamp(start_time).strftime("%Y-%m-%d")
|
|
@@ -521,7 +522,7 @@ class TardisDataProvider(IDataProvider):
|
|
|
521
522
|
"""Fetch historical OHLC data for warmup."""
|
|
522
523
|
# Calculate start and end time
|
|
523
524
|
end_time = self.time_provider.time()
|
|
524
|
-
start_time = end_time -
|
|
525
|
+
start_time = end_time - to_timedelta(period)
|
|
525
526
|
|
|
526
527
|
# Format dates for Tardis API
|
|
527
528
|
start_str = pd.Timestamp(start_time).strftime("%Y-%m-%d")
|
|
@@ -578,7 +579,7 @@ class TardisDataProvider(IDataProvider):
|
|
|
578
579
|
"""Fetch historical trade data for warmup."""
|
|
579
580
|
# Calculate start and end time
|
|
580
581
|
end_time = self.time_provider.time()
|
|
581
|
-
start_time = end_time -
|
|
582
|
+
start_time = end_time - to_timedelta(period)
|
|
582
583
|
|
|
583
584
|
# Format dates for Tardis API
|
|
584
585
|
start_str = pd.Timestamp(start_time).strftime("%Y-%m-%d")
|
|
@@ -632,7 +633,7 @@ class TardisDataProvider(IDataProvider):
|
|
|
632
633
|
"""Fetch historical orderbook data for warmup."""
|
|
633
634
|
# Calculate start and end time
|
|
634
635
|
end_time = self.time_provider.time()
|
|
635
|
-
start_time = end_time -
|
|
636
|
+
start_time = end_time - to_timedelta(period)
|
|
636
637
|
|
|
637
638
|
# Format dates for Tardis API
|
|
638
639
|
start_str = pd.Timestamp(start_time).strftime("%Y-%m-%d")
|
|
@@ -724,7 +725,7 @@ class TardisDataProvider(IDataProvider):
|
|
|
724
725
|
|
|
725
726
|
# Otherwise, try to parse the timeframe
|
|
726
727
|
try:
|
|
727
|
-
pd_timedelta =
|
|
728
|
+
pd_timedelta = to_timedelta(timeframe)
|
|
728
729
|
return f"{int(pd_timedelta.total_seconds() * 1000)}ms"
|
|
729
730
|
except:
|
|
730
731
|
# Fallback to default 1m if parsing fails
|