Qubx 3.2.2.dev1__tar.gz → 3.3.0__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-3.2.2.dev1 → qubx-3.3.0}/PKG-INFO +1 -2
- {qubx-3.2.2.dev1 → qubx-3.3.0}/pyproject.toml +0 -1
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/_version.py +2 -2
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/connector.py +6 -2
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/runner.py +5 -14
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/simulator.py +1 -1
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/transfers.py +16 -5
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/utils.py +73 -8
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/connection_manager.py +2 -1
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/connector.py +309 -26
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchange_manager.py +18 -61
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/base.py +8 -2
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +15 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +13 -1
- qubx-3.3.0/src/qubx/connectors/ccxt/rate_limits.py +424 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/warmup_service.py +25 -3
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/control/builtin.py +27 -3
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/account_manager/manager.py +26 -4
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/account_manager/reconciler.py +6 -2
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/account_manager/reducer.py +12 -1
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/account_manager/state.py +41 -8
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/basics.py +15 -6
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/connector.py +12 -1
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/context.py +101 -6
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/errors.py +20 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/events.py +1 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/exceptions.py +20 -0
- qubx-3.3.0/src/qubx/core/fit_context.py +481 -0
- qubx-3.3.0/src/qubx/core/fit_executor.py +157 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/initializer.py +23 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/interfaces.py +151 -15
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/metrics.py +31 -28
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/mixins/instrument_service.py +18 -8
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/mixins/market.py +193 -26
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/mixins/processing.py +205 -13
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/mixins/subscription.py +177 -40
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/mixins/trading.py +102 -22
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/series.pyi +4 -0
- qubx-3.3.0/src/qubx/core/status.py +108 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/storages/ccxt.py +15 -56
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/storages/multi.py +23 -16
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/emitters/csv.py +2 -2
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/emitters/inmemory.py +2 -2
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/emitters/questdb.py +22 -15
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/exporters/redis_streams.py +22 -11
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/health/base.py +176 -11
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/health/dummy.py +7 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/rate_limiting/__init__.py +3 -1
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/rate_limiting/backend.py +13 -6
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/rate_limiting/config.py +9 -3
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/rate_limiting/engine.py +14 -1
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/rate_limiting/ip_resolver.py +6 -6
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/rate_limiting/manager.py +18 -3
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/rate_limiting/pools.py +63 -51
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/rate_limiting/redis_backend.py +28 -4
- qubx-3.3.0/src/qubx/rate_limiting/resilient.py +176 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/state/redis.py +19 -1
- qubx-3.3.0/src/qubx/state/safe.py +226 -0
- qubx-3.3.0/src/qubx/utils/clock.py +167 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/rate_limiter.py +53 -35
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/results.py +23 -1
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/configs.py +43 -1
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/factory.py +16 -3
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/runner.py +10 -1
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/handlers.py +1 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/init_code.py +14 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/styles.tcss +7 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/widgets/account_summary.py +26 -0
- qubx-3.3.0/src/qubx/utils/threading.py +84 -0
- qubx-3.2.2.dev1/src/qubx/connectors/ccxt/rate_limits.py +0 -295
- qubx-3.2.2.dev1/src/qubx/utils/ntp.py +0 -63
- {qubx-3.2.2.dev1 → qubx-3.3.0}/.gitignore +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/LICENSE +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/README.md +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/scripts/build.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/_nb_magic.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/data.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/iteratedstream.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/management.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/ome.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/optimization.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/sentinels.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/simulated_data.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/backtester/simulated_exchange.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/cli/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/cli/commands.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/cli/deploy.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/cli/misc.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/cli/release.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/cli/resolver.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/cli/s3.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/cli/theme.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/cli/tui.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/cli/user_config.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/config.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/data.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exceptions.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/_two_stream.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/binance/connector.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/bitfinex/connector.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/okx/connector.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/factory.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/plugin.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/ccxt/utils.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/plugin.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/registry.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/tardis/data.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/tardis/plugin.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/connectors/tardis/utils.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/control/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/control/decorator.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/control/executor.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/control/interfaces.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/control/server.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/control/types.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/account_manager/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/account_manager/config.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/account_manager/diffs.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/account_manager/state_machine.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/detectors/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/detectors/delisting.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/detectors/stale.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/helpers.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/instrument_service.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/loggers.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/lookups.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/mixins/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/mixins/universe.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/mixins/utils.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/series.pxd +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/series.pyx +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/utils.pyi +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/core/utils.pyx +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/cache.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/containers.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/guards.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/registry.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/storage.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/storages/csv.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/storages/handy.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/storages/questdb.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/storages/stub.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/storages/utils.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/data/transformers.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/emitters/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/emitters/base.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/emitters/composite.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/emitters/indicator.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/emitters/prometheus.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/exporters/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/exporters/composite.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/exporters/formatters/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/exporters/formatters/base.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/exporters/formatters/incremental.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/exporters/formatters/slack.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/exporters/formatters/target_position.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/exporters/slack.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/gathering/simplest.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/health/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/loggers/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/loggers/csv.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/loggers/factory.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/loggers/inmemory.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/loggers/mongo.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/loggers/postgres.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/notifications/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/notifications/composite.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/notifications/slack.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/notifications/throttler.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/pandaz/stats.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/pandaz/ta.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/pandaz/utils.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/plugins/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/plugins/loader.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/_build.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/crypto-fees.ini +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restarts/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restarts/state_resolvers.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restarts/time_finders.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restorers/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restorers/balance.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restorers/factory.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restorers/interfaces.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restorers/position.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restorers/signal.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restorers/state.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/restorers/utils.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/state/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/state/dummy.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/ta/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/ta/indicators.pxd +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/ta/indicators.pyi +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/ta/indicators.pyx +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/base.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/project/accounts.toml.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/project/config.yml.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/project/jlive.sh.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/project/template.yml +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/.claude/skills/simulation-explorer/SKILL.md +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/.gitignore.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/.python-version +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/.vscode/launch.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/.vscode/settings.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/.vscode/tasks.json +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/CLAUDE.md.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/README.md.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/configs/.gitkeep +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/justfile.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/pyproject.toml.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/research/.gitkeep +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/template.yml +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/repo/tests/test_strategy.py.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/simple/__init__.py.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/simple/config.yml.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/simple/strategy.py.j2 +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/templates/simple/template.yml +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/trackers/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/trackers/advanced.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/trackers/composite.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/trackers/riskctrl.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/trackers/sizers.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/charting/lookinglass.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/charting/mpl_helpers.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/charting/orderbook.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/collections.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/hft/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/hft/numba_utils.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/hft/orderbook.pyi +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/hft/orderbook.pyx +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/marketdata/ccxt.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/marketdata/dukas.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/misc.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/nonce.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/numbers_utils.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/orderbook.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/plotting/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/plotting/dashboard.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/plotting/data.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/plotting/interfaces.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/questdb.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/ringbuffer.pxd +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/ringbuffer.pyi +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/ringbuffer.pyx +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/accounts.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/kernel_service.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/app.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/kernel.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/s3.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/slack.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/throttler.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/time.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/src/qubx/utils/websocket_manager.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
- {qubx-3.2.2.dev1 → qubx-3.3.0}/tests/strategies/obi_trader/src/obi_trader/models/obi_trader.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: Qubx
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: Qubx - Quantitative Trading Framework
|
|
5
5
|
Project-URL: homepage, https://xlydian.com
|
|
6
6
|
Project-URL: repository, https://github.com/xLydianSoftware/Qubx
|
|
@@ -23,7 +23,6 @@ Requires-Dist: joblib<2,>=1.3.0
|
|
|
23
23
|
Requires-Dist: jupyter-client<9,>=8.0.0
|
|
24
24
|
Requires-Dist: loguru<1,>=0.7.2
|
|
25
25
|
Requires-Dist: msgspec<1,>=0.18.6
|
|
26
|
-
Requires-Dist: ntplib<1,>=0.4.0
|
|
27
26
|
Requires-Dist: numba<1,>=0.59.1
|
|
28
27
|
Requires-Dist: numpy>=2.0.0
|
|
29
28
|
Requires-Dist: orjson<4,>=3.10.15
|
|
@@ -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 = '3.
|
|
22
|
-
__version_tuple__ = version_tuple = (3,
|
|
21
|
+
__version__ = version = '3.3.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (3, 3, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -298,13 +298,17 @@ class SimulatedConnector(ChannelEmitter):
|
|
|
298
298
|
def is_simulated_trading(self) -> bool:
|
|
299
299
|
return True
|
|
300
300
|
|
|
301
|
-
def
|
|
302
|
-
|
|
301
|
+
def set_instrument_leverage(self, instrument: Instrument, leverage: float) -> None:
|
|
302
|
+
# - simulation does not model venue leverage settings; accept and forget
|
|
303
|
+
return None
|
|
303
304
|
|
|
304
305
|
def set_margin_mode(self, instrument: Instrument, mode: str) -> bool:
|
|
305
306
|
return True
|
|
306
307
|
|
|
307
308
|
# Simulation does not model venue margin: the config getters read neutral.
|
|
309
|
+
def get_instrument_leverage(self, instrument: Instrument) -> float | None:
|
|
310
|
+
return None
|
|
311
|
+
|
|
308
312
|
def get_max_instrument_leverage(self, instrument: Instrument) -> float | None:
|
|
309
313
|
return None
|
|
310
314
|
|
|
@@ -11,7 +11,7 @@ from qubx.backtester.sentinels import NoDataContinue
|
|
|
11
11
|
from qubx.backtester.simulated_data import SimulatedDataIterator
|
|
12
12
|
from qubx.backtester.transfers import SimulationTransferManager
|
|
13
13
|
from qubx.core.account_manager import AccountManagerConfig, SimulatedAccountManager
|
|
14
|
-
from qubx.core.basics import SW,
|
|
14
|
+
from qubx.core.basics import SW, DataType, Instrument, TransactionCostsCalculator
|
|
15
15
|
from qubx.core.context import StrategyContext
|
|
16
16
|
from qubx.core.exceptions import SimulationConfigError, SimulationError
|
|
17
17
|
from qubx.core.helpers import extract_parameters_from_object, full_qualified_class_name
|
|
@@ -49,6 +49,7 @@ from .utils import (
|
|
|
49
49
|
SimulationSetup,
|
|
50
50
|
_get_default_warmup_period,
|
|
51
51
|
find_open_close_time_indent_secs_from_subscription,
|
|
52
|
+
initial_balances,
|
|
52
53
|
)
|
|
53
54
|
|
|
54
55
|
|
|
@@ -608,7 +609,7 @@ class SimulationRunner:
|
|
|
608
609
|
|
|
609
610
|
am = SimulatedAccountManager(
|
|
610
611
|
connectors=self._connectors,
|
|
611
|
-
base_currencies=
|
|
612
|
+
base_currencies=self.setup.base_currencies,
|
|
612
613
|
time=time_provider,
|
|
613
614
|
cfg=AccountManagerConfig(),
|
|
614
615
|
account_id=self.account_id,
|
|
@@ -616,17 +617,7 @@ class SimulationRunner:
|
|
|
616
617
|
)
|
|
617
618
|
|
|
618
619
|
# - seed initial capital per exchange into the account state
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
am.seed_balance(
|
|
622
|
-
exchange,
|
|
623
|
-
Balance(
|
|
624
|
-
exchange=exchange,
|
|
625
|
-
currency=self.setup.base_currency,
|
|
626
|
-
total=capital,
|
|
627
|
-
free=capital,
|
|
628
|
-
locked=0.0,
|
|
629
|
-
),
|
|
630
|
-
)
|
|
620
|
+
for exchange, balance in initial_balances(self.setup).items():
|
|
621
|
+
am.seed_balance(exchange, balance)
|
|
631
622
|
|
|
632
623
|
return am
|
|
@@ -38,7 +38,7 @@ def simulate(
|
|
|
38
38
|
instruments: list[str] | list[Instrument] | dict[ExchangeName_t, list[SymbolOrInstrument_t]] | None = None,
|
|
39
39
|
commissions: str | dict[str, str | None] | None = None,
|
|
40
40
|
exchange: ExchangeName_t | list[ExchangeName_t] | None = None,
|
|
41
|
-
base_currency: str = "USDT",
|
|
41
|
+
base_currency: str | dict[str, str] = "USDT",
|
|
42
42
|
n_jobs: int = 1,
|
|
43
43
|
silent: bool = False,
|
|
44
44
|
aux_data: IStorage | None = None,
|
|
@@ -21,6 +21,15 @@ class SimulationTransferManager(ITransferManager):
|
|
|
21
21
|
def transfer_funds(self, from_exchange: str, to_exchange: str, currency: str, amount: float) -> str:
|
|
22
22
|
if amount <= 0:
|
|
23
23
|
raise ValueError(f"Transfer amount must be positive, got {amount}")
|
|
24
|
+
to_currency = self._account.get_base_currency(to_exchange)
|
|
25
|
+
# Venues settle in their own currency: a withdrawal leaves in `currency` and arrives as
|
|
26
|
+
# `to_currency`. Both legs must be priced — an unpriced asset has no transferable value.
|
|
27
|
+
from_rate = self._account.get_state(from_exchange).conversion_rate_to_base(currency)
|
|
28
|
+
to_rate = self._account.get_state(to_exchange).conversion_rate_to_base(to_currency)
|
|
29
|
+
if from_rate is None or to_rate is None:
|
|
30
|
+
raise ValueError(f"Cannot transfer {currency} to {to_exchange} {to_currency}: no conversion rate")
|
|
31
|
+
to_amount = amount * from_rate / to_rate
|
|
32
|
+
|
|
24
33
|
from_balance = self._account.get_balance(currency, exchange=from_exchange)
|
|
25
34
|
if from_balance.free < amount:
|
|
26
35
|
raise ValueError(
|
|
@@ -28,11 +37,8 @@ class SimulationTransferManager(ITransferManager):
|
|
|
28
37
|
f"{from_balance.free:.8f} {currency} available, {amount:.8f} requested"
|
|
29
38
|
)
|
|
30
39
|
|
|
31
|
-
# Instant transfer: debit the source and credit the destination. adjust_balance
|
|
32
|
-
# mutates the held Balance in place (holders keep a live reference) and creates
|
|
33
|
-
# the destination Balance if missing.
|
|
34
40
|
self._account.adjust_balance(from_exchange, currency, -amount)
|
|
35
|
-
self._account.adjust_balance(to_exchange,
|
|
41
|
+
self._account.adjust_balance(to_exchange, to_currency, to_amount)
|
|
36
42
|
|
|
37
43
|
transaction_id = f"sim_{uuid.uuid4().hex[:12]}"
|
|
38
44
|
self._transfers[transaction_id] = Transfer(
|
|
@@ -43,8 +49,13 @@ class SimulationTransferManager(ITransferManager):
|
|
|
43
49
|
amount=amount,
|
|
44
50
|
status=TransferStatus.COMPLETED, # transfers are instant in simulation
|
|
45
51
|
timestamp=self._time.time(),
|
|
52
|
+
to_currency=to_currency,
|
|
53
|
+
to_amount=to_amount,
|
|
54
|
+
)
|
|
55
|
+
logger.debug(
|
|
56
|
+
f"[SimTransfer] {amount:.8f} {currency} {from_exchange} -> "
|
|
57
|
+
f"{to_amount:.8f} {to_currency} {to_exchange} ({transaction_id})"
|
|
46
58
|
)
|
|
47
|
-
logger.debug(f"[SimTransfer] {amount:.8f} {currency} {from_exchange} -> {to_exchange} ({transaction_id})")
|
|
48
59
|
return transaction_id
|
|
49
60
|
|
|
50
61
|
def get_transfer_status(self, transaction_id: str) -> Transfer:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from collections.abc import Mapping, Sequence
|
|
2
|
-
from dataclasses import dataclass
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from typing import Any, Callable, TypeAlias
|
|
5
5
|
|
|
@@ -8,6 +8,8 @@ import pandas as pd
|
|
|
8
8
|
|
|
9
9
|
from qubx import logger
|
|
10
10
|
from qubx.core.basics import (
|
|
11
|
+
STABLE_CURRENCIES,
|
|
12
|
+
Balance,
|
|
11
13
|
CtrlChannel,
|
|
12
14
|
DataType,
|
|
13
15
|
Instrument,
|
|
@@ -80,6 +82,20 @@ def _type(obj: Any) -> SetupTypes:
|
|
|
80
82
|
return t
|
|
81
83
|
|
|
82
84
|
|
|
85
|
+
def _settle_currencies(instruments: list[Instrument], exchange: str) -> set[str]:
|
|
86
|
+
return {i.settle.upper() for i in instruments if i.exchange == exchange and i.settle}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _derive_settle_currency(instruments: list[Instrument], exchange: str) -> str | None:
|
|
90
|
+
"""The one settle currency a venue's instruments agree on, or None when absent, mixed, or
|
|
91
|
+
not a recognized stable — BINANCE.UM:ETHBTC settles in BTC, and BTC is not 1.0 of capital."""
|
|
92
|
+
settles = _settle_currencies(instruments, exchange)
|
|
93
|
+
if len(settles) != 1:
|
|
94
|
+
return None
|
|
95
|
+
settle = settles.pop()
|
|
96
|
+
return settle if settle in STABLE_CURRENCIES else None
|
|
97
|
+
|
|
98
|
+
|
|
83
99
|
@dataclass
|
|
84
100
|
class SimulationSetup:
|
|
85
101
|
"""
|
|
@@ -93,16 +109,37 @@ class SimulationSetup:
|
|
|
93
109
|
instruments: list[Instrument]
|
|
94
110
|
exchanges: list[str]
|
|
95
111
|
capital: float | dict[str, float]
|
|
96
|
-
base_currency: str
|
|
112
|
+
base_currency: str | dict[str, str]
|
|
97
113
|
commissions: str | dict[str, str | None] | None = None
|
|
98
114
|
signal_timeframe: str = "1Min"
|
|
99
115
|
accurate_stop_orders_execution: bool = False
|
|
100
116
|
enable_funding: bool = False
|
|
117
|
+
base_currencies: dict[str, str] = field(init=False, default_factory=dict)
|
|
101
118
|
|
|
102
119
|
def __post_init__(self) -> None:
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
|
|
120
|
+
# Per-exchange base currency: explicit mapping wins, else the venue's shared settle
|
|
121
|
+
# currency, else the scalar. AccountState upper-cases, so normalize here or the seeded
|
|
122
|
+
# Balance lands under a key total_capital never reads.
|
|
123
|
+
explicit = (
|
|
124
|
+
{ex: ccy.upper() for ex, ccy in self.base_currency.items()}
|
|
125
|
+
if isinstance(self.base_currency, dict)
|
|
126
|
+
else {}
|
|
127
|
+
)
|
|
128
|
+
default_ccy = (
|
|
129
|
+
self.base_currency.upper()
|
|
130
|
+
if isinstance(self.base_currency, str)
|
|
131
|
+
else (explicit.get(self.exchanges[0], "USDT") if self.exchanges else "USDT")
|
|
132
|
+
)
|
|
133
|
+
self.base_currencies = {}
|
|
134
|
+
for ex in self.exchanges:
|
|
135
|
+
resolved = explicit.get(ex) or _derive_settle_currency(self.instruments, ex)
|
|
136
|
+
if resolved is None and (settles := _settle_currencies(self.instruments, ex)):
|
|
137
|
+
logger.debug(
|
|
138
|
+
f"No single stable settle currency for {ex} (saw {sorted(settles)}) "
|
|
139
|
+
f"- using {default_ccy} as its base currency"
|
|
140
|
+
)
|
|
141
|
+
self.base_currencies[ex] = resolved or default_ccy
|
|
142
|
+
self.base_currency = self.base_currencies[self.exchanges[0]] if self.exchanges else default_ccy
|
|
106
143
|
# Normalize capital to a per-exchange dict: split evenly when a single float is given
|
|
107
144
|
if isinstance(self.capital, (int, float)):
|
|
108
145
|
n = len(self.exchanges)
|
|
@@ -113,6 +150,21 @@ class SimulationSetup:
|
|
|
113
150
|
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}]"
|
|
114
151
|
|
|
115
152
|
|
|
153
|
+
def initial_balances(setup: SimulationSetup) -> dict[str, Balance]:
|
|
154
|
+
"""Startup balance per exchange, each in that venue's own base currency."""
|
|
155
|
+
assert isinstance(setup.capital, dict)
|
|
156
|
+
return {
|
|
157
|
+
exchange: Balance(
|
|
158
|
+
exchange=exchange,
|
|
159
|
+
currency=setup.base_currencies[exchange],
|
|
160
|
+
total=capital,
|
|
161
|
+
free=capital,
|
|
162
|
+
locked=0.0,
|
|
163
|
+
)
|
|
164
|
+
for exchange, capital in setup.capital.items()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
116
168
|
# fmt: off
|
|
117
169
|
@dataclass
|
|
118
170
|
class SimulationDataConfig:
|
|
@@ -287,7 +339,9 @@ def find_instruments_and_exchanges(
|
|
|
287
339
|
_instrs.append(instrument)
|
|
288
340
|
_exchanges.append(resolved_exchange)
|
|
289
341
|
|
|
290
|
-
|
|
342
|
+
# sorted, not list(set(...)): string hashing is per-process randomized, and exchanges[0]
|
|
343
|
+
# decides the persisted scalar base_currency of a multi-venue run.
|
|
344
|
+
return _instrs, sorted(set(_exchanges))
|
|
291
345
|
|
|
292
346
|
|
|
293
347
|
class _StructureSniffer:
|
|
@@ -345,7 +399,7 @@ def recognize_simulation_configuration(
|
|
|
345
399
|
instruments: list[Instrument],
|
|
346
400
|
exchanges: list[str],
|
|
347
401
|
capital: float | dict[str, float],
|
|
348
|
-
basic_currency: str,
|
|
402
|
+
basic_currency: str | dict[str, str],
|
|
349
403
|
commissions: str | dict[str, str | None] | None,
|
|
350
404
|
signal_timeframe: str,
|
|
351
405
|
accurate_stop_orders_execution: bool,
|
|
@@ -640,7 +694,16 @@ def recognize_simulation_data_config(
|
|
|
640
694
|
)
|
|
641
695
|
|
|
642
696
|
|
|
643
|
-
_TRANSFERS_LOG_COLUMNS = [
|
|
697
|
+
_TRANSFERS_LOG_COLUMNS = [
|
|
698
|
+
"transaction_id",
|
|
699
|
+
"from_exchange",
|
|
700
|
+
"to_exchange",
|
|
701
|
+
"currency",
|
|
702
|
+
"amount",
|
|
703
|
+
"status",
|
|
704
|
+
"to_currency",
|
|
705
|
+
"to_amount",
|
|
706
|
+
]
|
|
644
707
|
|
|
645
708
|
|
|
646
709
|
def collect_transfers_log(transfer_manager: ITransferManager | None) -> pd.DataFrame | None:
|
|
@@ -667,6 +730,8 @@ def collect_transfers_log(transfer_manager: ITransferManager | None) -> pd.DataF
|
|
|
667
730
|
"currency": t.currency,
|
|
668
731
|
"amount": t.amount,
|
|
669
732
|
"status": str(t.status),
|
|
733
|
+
"to_currency": t.to_currency or t.currency,
|
|
734
|
+
"to_amount": t.to_amount if t.to_amount is not None else t.amount,
|
|
670
735
|
}
|
|
671
736
|
for t in transfers
|
|
672
737
|
]
|
|
@@ -155,7 +155,8 @@ class ConnectionManager:
|
|
|
155
155
|
await asyncio.sleep(1)
|
|
156
156
|
continue
|
|
157
157
|
except (RateLimitExceeded, DDoSProtection) as e:
|
|
158
|
-
#
|
|
158
|
+
# WS 429/418 are IP actions that ban REST too, and a gate close is not a token debit,
|
|
159
|
+
# so the next header sync does not erase it — hence the REST pool.
|
|
159
160
|
logger.warning(
|
|
160
161
|
f"<yellow>{self._exchange_id}</yellow> Rate limited in {stream_name}: {e}"
|
|
161
162
|
)
|