Qubx 1.1.2.dev13__tar.gz → 1.1.2.dev15__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.
Files changed (295) hide show
  1. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/PKG-INFO +1 -1
  2. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/_version.py +2 -2
  3. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/runner.py +5 -0
  4. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/simulated_data.py +11 -0
  5. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/exceptions.py +4 -0
  6. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/runner.py +140 -2
  7. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/.gitignore +0 -0
  8. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/LICENSE +0 -0
  9. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/README.md +0 -0
  10. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/pyproject.toml +0 -0
  11. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/scripts/build.py +0 -0
  12. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/__init__.py +0 -0
  13. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/_nb_magic.py +0 -0
  14. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/__init__.py +0 -0
  15. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/account.py +0 -0
  16. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/broker.py +0 -0
  17. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/data.py +0 -0
  18. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/iteratedstream.py +0 -0
  19. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/management.py +0 -0
  20. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/ome.py +0 -0
  21. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/optimization.py +0 -0
  22. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/sentinels.py +0 -0
  23. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/simulated_exchange.py +0 -0
  24. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/simulator.py +0 -0
  25. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/transfers.py +0 -0
  26. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/backtester/utils.py +0 -0
  27. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/cli/__init__.py +0 -0
  28. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/cli/commands.py +0 -0
  29. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/cli/deploy.py +0 -0
  30. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/cli/misc.py +0 -0
  31. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/cli/release.py +0 -0
  32. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/cli/resolver.py +0 -0
  33. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/cli/s3.py +0 -0
  34. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/cli/theme.py +0 -0
  35. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/cli/tui.py +0 -0
  36. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/cli/user_config.py +0 -0
  37. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/config.py +0 -0
  38. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/__init__.py +0 -0
  39. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/__init__.py +0 -0
  40. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/account.py +0 -0
  41. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
  42. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
  43. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/broker.py +0 -0
  44. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
  45. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/data.py +0 -0
  46. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  47. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
  48. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
  49. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
  50. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
  51. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
  52. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
  53. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
  54. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
  55. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +0 -0
  56. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
  57. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
  58. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
  59. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
  60. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
  61. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
  62. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/okx/account.py +0 -0
  63. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/okx/broker.py +0 -0
  64. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
  65. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/factory.py +0 -0
  66. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
  67. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
  68. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
  69. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
  70. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
  71. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
  72. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
  73. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
  74. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
  75. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
  76. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
  77. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
  78. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
  79. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/utils.py +0 -0
  80. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
  81. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/registry.py +0 -0
  82. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/tardis/data.py +0 -0
  83. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/connectors/tardis/utils.py +0 -0
  84. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/__init__.py +0 -0
  85. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/account.py +0 -0
  86. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/basics.py +0 -0
  87. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/context.py +0 -0
  88. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/detectors/__init__.py +0 -0
  89. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/detectors/delisting.py +0 -0
  90. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/detectors/stale.py +0 -0
  91. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/errors.py +0 -0
  92. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/helpers.py +0 -0
  93. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/initializer.py +0 -0
  94. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/interfaces.py +0 -0
  95. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/loggers.py +0 -0
  96. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/lookups.py +0 -0
  97. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/metrics.py +0 -0
  98. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/mixins/__init__.py +0 -0
  99. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/mixins/market.py +0 -0
  100. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/mixins/processing.py +0 -0
  101. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/mixins/subscription.py +0 -0
  102. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/mixins/trading.py +0 -0
  103. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/mixins/universe.py +0 -0
  104. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/mixins/utils.py +0 -0
  105. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/series.pxd +0 -0
  106. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/series.pyi +0 -0
  107. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/series.pyx +0 -0
  108. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/utils.pyi +0 -0
  109. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/core/utils.pyx +0 -0
  110. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/__init__.py +0 -0
  111. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/cache.py +0 -0
  112. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/containers.py +0 -0
  113. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/guards.py +0 -0
  114. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/registry.py +0 -0
  115. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/storage.py +0 -0
  116. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/storages/ccxt.py +0 -0
  117. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/storages/csv.py +0 -0
  118. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/storages/handy.py +0 -0
  119. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/storages/multi.py +0 -0
  120. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/storages/questdb.py +0 -0
  121. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/storages/stub.py +0 -0
  122. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/storages/utils.py +0 -0
  123. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/data/transformers.py +0 -0
  124. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/emitters/__init__.py +0 -0
  125. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/emitters/base.py +0 -0
  126. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/emitters/composite.py +0 -0
  127. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/emitters/csv.py +0 -0
  128. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/emitters/indicator.py +0 -0
  129. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/emitters/inmemory.py +0 -0
  130. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/emitters/prometheus.py +0 -0
  131. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/emitters/questdb.py +0 -0
  132. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/exporters/__init__.py +0 -0
  133. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/exporters/composite.py +0 -0
  134. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/exporters/formatters/__init__.py +0 -0
  135. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/exporters/formatters/base.py +0 -0
  136. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/exporters/formatters/incremental.py +0 -0
  137. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/exporters/formatters/slack.py +0 -0
  138. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/exporters/formatters/target_position.py +0 -0
  139. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/exporters/redis_streams.py +0 -0
  140. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/exporters/slack.py +0 -0
  141. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/gathering/simplest.py +0 -0
  142. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/health/__init__.py +0 -0
  143. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/health/base.py +0 -0
  144. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/health/dummy.py +0 -0
  145. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/health/server.py +0 -0
  146. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/loggers/__init__.py +0 -0
  147. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/loggers/csv.py +0 -0
  148. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/loggers/factory.py +0 -0
  149. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/loggers/inmemory.py +0 -0
  150. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/loggers/mongo.py +0 -0
  151. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/loggers/postgres.py +0 -0
  152. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/notifications/__init__.py +0 -0
  153. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/notifications/composite.py +0 -0
  154. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/notifications/slack.py +0 -0
  155. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/notifications/throttler.py +0 -0
  156. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/pandaz/__init__.py +0 -0
  157. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/pandaz/stats.py +0 -0
  158. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/pandaz/ta.py +0 -0
  159. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/pandaz/utils.py +0 -0
  160. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/plugins/__init__.py +0 -0
  161. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/plugins/loader.py +0 -0
  162. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/_build.py +0 -0
  163. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/crypto-fees.ini +0 -0
  164. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
  165. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
  166. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
  167. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
  168. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
  169. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
  170. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
  171. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
  172. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
  173. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
  174. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
  175. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restarts/__init__.py +0 -0
  176. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restarts/state_resolvers.py +0 -0
  177. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restarts/time_finders.py +0 -0
  178. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restorers/__init__.py +0 -0
  179. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restorers/balance.py +0 -0
  180. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restorers/factory.py +0 -0
  181. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restorers/interfaces.py +0 -0
  182. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restorers/position.py +0 -0
  183. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restorers/signal.py +0 -0
  184. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restorers/state.py +0 -0
  185. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/restorers/utils.py +0 -0
  186. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/state/__init__.py +0 -0
  187. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/state/dummy.py +0 -0
  188. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/state/redis.py +0 -0
  189. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/ta/__init__.py +0 -0
  190. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/ta/indicators.pxd +0 -0
  191. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/ta/indicators.pyi +0 -0
  192. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/ta/indicators.pyx +0 -0
  193. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/__init__.py +0 -0
  194. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/base.py +0 -0
  195. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/project/accounts.toml.j2 +0 -0
  196. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/project/config.yml.j2 +0 -0
  197. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/project/jlive.sh.j2 +0 -0
  198. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
  199. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
  200. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  201. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  202. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/project/template.yml +0 -0
  203. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +0 -0
  204. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +0 -0
  205. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/.claude/skills/simulation-explorer/SKILL.md +0 -0
  206. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +0 -0
  207. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +0 -0
  208. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/.gitignore.j2 +0 -0
  209. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/.python-version +0 -0
  210. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/.vscode/launch.json +0 -0
  211. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/.vscode/settings.json +0 -0
  212. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/.vscode/tasks.json +0 -0
  213. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/CLAUDE.md.j2 +0 -0
  214. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/README.md.j2 +0 -0
  215. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/configs/.gitkeep +0 -0
  216. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/justfile.j2 +0 -0
  217. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/pyproject.toml.j2 +0 -0
  218. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/research/.gitkeep +0 -0
  219. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  220. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +0 -0
  221. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  222. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/template.yml +0 -0
  223. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/repo/tests/test_strategy.py.j2 +0 -0
  224. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/simple/__init__.py.j2 +0 -0
  225. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
  226. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/simple/config.yml.j2 +0 -0
  227. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
  228. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
  229. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/simple/strategy.py.j2 +0 -0
  230. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/templates/simple/template.yml +0 -0
  231. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/trackers/__init__.py +0 -0
  232. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/trackers/advanced.py +0 -0
  233. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/trackers/composite.py +0 -0
  234. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/trackers/rebalancers.py +0 -0
  235. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/trackers/riskctrl.py +0 -0
  236. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/trackers/sizers.py +0 -0
  237. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/__init__.py +0 -0
  238. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/_pyxreloader.py +0 -0
  239. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/charting/lookinglass.py +0 -0
  240. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  241. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/charting/orderbook.py +0 -0
  242. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/collections.py +0 -0
  243. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/hft/__init__.py +0 -0
  244. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/hft/numba_utils.py +0 -0
  245. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/hft/orderbook.pyi +0 -0
  246. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/hft/orderbook.pyx +0 -0
  247. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/marketdata/binance.py +0 -0
  248. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/marketdata/ccxt.py +0 -0
  249. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/marketdata/dukas.py +0 -0
  250. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/misc.py +0 -0
  251. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/nonce.py +0 -0
  252. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/ntp.py +0 -0
  253. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/numbers_utils.py +0 -0
  254. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/orderbook.py +0 -0
  255. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/plotting/__init__.py +0 -0
  256. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/plotting/dashboard.py +0 -0
  257. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/plotting/data.py +0 -0
  258. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/plotting/interfaces.py +0 -0
  259. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  260. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  261. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/questdb.py +0 -0
  262. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/rate_limiter.py +0 -0
  263. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/results.py +0 -0
  264. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/ringbuffer.pxd +0 -0
  265. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/ringbuffer.pyi +0 -0
  266. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/ringbuffer.pyx +0 -0
  267. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/__init__.py +0 -0
  268. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
  269. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/accounts.py +0 -0
  270. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/configs.py +0 -0
  271. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/factory.py +0 -0
  272. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/kernel_service.py +0 -0
  273. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/__init__.py +0 -0
  274. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/app.py +0 -0
  275. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/handlers.py +0 -0
  276. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/init_code.py +0 -0
  277. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/kernel.py +0 -0
  278. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/styles.tcss +0 -0
  279. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
  280. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/widgets/account_summary.py +0 -0
  281. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
  282. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
  283. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
  284. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
  285. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
  286. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
  287. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/s3.py +0 -0
  288. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/slack.py +0 -0
  289. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/throttler.py +0 -0
  290. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/time.py +0 -0
  291. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/src/qubx/utils/websocket_manager.py +0 -0
  292. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
  293. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
  294. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
  295. {qubx-1.1.2.dev13 → qubx-1.1.2.dev15}/tests/strategies/obi_trader/src/obi_trader/models/obi_trader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Qubx
3
- Version: 1.1.2.dev13
3
+ Version: 1.1.2.dev15
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
@@ -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.dev13'
22
- __version_tuple__ = version_tuple = (1, 1, 2, 'dev13')
21
+ __version__ = version = '1.1.2.dev15'
22
+ __version_tuple__ = version_tuple = (1, 1, 2, 'dev15')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -131,6 +131,11 @@ class SimulationRunner:
131
131
  self._create_backtest_context()
132
132
  self._handle_ctx_subscriptions()
133
133
 
134
+ @property
135
+ def data_time_range(self) -> tuple[int, int] | None:
136
+ """Time range (ns) of historical data actually processed: (first, last), or None if no data arrived."""
137
+ return self._simulated_data_source.data_time_range
138
+
134
139
  def run(self, silent: bool = False, catch_keyboard_interrupt: bool = True, close_data_readers: bool = False):
135
140
  """
136
141
  Run the backtest from start to stop.
@@ -846,9 +846,17 @@ class SimulatedDataIterator(Iterator):
846
846
  self._slicer = None
847
847
  self._slicing_iterator = None
848
848
  self._current_time = None
849
+ self._first_data_time: int | None = None
849
850
  self._start = None
850
851
  self._stop = None
851
852
 
853
+ @property
854
+ def data_time_range(self) -> tuple[int, int] | None:
855
+ """Time range (ns) of historical data actually processed: (first, last), or None if no data arrived."""
856
+ if self._first_data_time is not None and self._current_time is not None:
857
+ return (self._first_data_time, self._current_time)
858
+ return None
859
+
852
860
  @property
853
861
  def emulation_time_indent_seconds(self) -> float:
854
862
  """
@@ -1104,6 +1112,7 @@ class SimulatedDataIterator(Iterator):
1104
1112
  self._start = to_timestamp(start)
1105
1113
  self._stop = to_timestamp(stop)
1106
1114
  self._current_time = None
1115
+ self._first_data_time = None
1107
1116
  self._slicer = IteratedDataStreamsSlicer()
1108
1117
  return self
1109
1118
 
@@ -1137,6 +1146,8 @@ class SimulatedDataIterator(Iterator):
1137
1146
  if t < self._current_time:
1138
1147
  is_historical = True
1139
1148
  else:
1149
+ if self._first_data_time is None:
1150
+ self._first_data_time = t
1140
1151
  self._current_time = t
1141
1152
 
1142
1153
  return instr, data_type, v, is_historical
@@ -48,3 +48,7 @@ class SimulationError(Exception):
48
48
 
49
49
  class SimulationConfigError(Exception):
50
50
  pass
51
+
52
+
53
+ class WarmupValidationError(Exception):
54
+ pass
@@ -2,6 +2,7 @@ import asyncio
2
2
  import socket
3
3
  import time
4
4
  from collections import defaultdict
5
+ from dataclasses import dataclass
5
6
  from functools import reduce
6
7
  from pathlib import Path
7
8
  from threading import Thread
@@ -28,6 +29,7 @@ from qubx.core.basics import (
28
29
  TransactionCostsCalculator,
29
30
  )
30
31
  from qubx.core.context import StrategyContext
32
+ from qubx.core.exceptions import WarmupValidationError
31
33
  from qubx.core.helpers import BasicScheduler
32
34
  from qubx.core.initializer import BasicStrategyInitializer
33
35
  from qubx.core.interfaces import (
@@ -274,7 +276,15 @@ def run_strategy(
274
276
  _health_server.start()
275
277
 
276
278
  # Restore state if configured
277
- restored_state = _restore_state(config.live.warmup.restorer if config.live.warmup else None) if restore else None
279
+ restored_state = (
280
+ _restore_state(
281
+ restorer_config=config.live.warmup.restorer if config.live.warmup else None,
282
+ logging_config=config.live.logging if config.live.logging else None,
283
+ strategy_name=_get_strategy_name(config),
284
+ )
285
+ if restore
286
+ else None
287
+ )
278
288
 
279
289
  # Resolve aux config with live section override (needed for both warmup and live context)
280
290
  aux_configs = resolve_aux_config(config.aux, getattr(config.live, "aux", None))
@@ -335,7 +345,50 @@ def run_strategy(
335
345
  return ctx
336
346
 
337
347
 
338
- def _restore_state(restorer_config: RestorerConfig | None) -> RestoredState | None:
348
+ def _infer_restorer_from_logger(logging_config: LoggingConfig, strategy_name: str) -> RestorerConfig | None:
349
+ """Infer a matching state restorer config from the logger config."""
350
+ logger_type = logging_config.logger
351
+ args = logging_config.args
352
+
353
+ if logger_type == "PostgresLogsWriter":
354
+ return RestorerConfig(
355
+ type="PostgresStateRestorer",
356
+ parameters={
357
+ "strategy_name": strategy_name,
358
+ "postgres_uri": args.get("postgres_uri", "postgresql://localhost:5432/qubx_logs"),
359
+ "table_prefix": args.get("table_prefix", "qubx_logs"),
360
+ },
361
+ )
362
+ elif logger_type == "MongoDBLogsWriter":
363
+ return RestorerConfig(
364
+ type="MongoDBStateRestorer",
365
+ parameters={
366
+ "strategy_name": strategy_name,
367
+ "mongo_uri": args.get("mongo_uri", "mongodb://localhost:27017/"),
368
+ "db_name": args.get("db_name", "default_logs_db"),
369
+ "collection_name_prefix": args.get("collection_name_prefix", "qubx_logs"),
370
+ },
371
+ )
372
+ elif logger_type == "CsvFileLogsWriter":
373
+ return RestorerConfig(
374
+ type="CsvStateRestorer",
375
+ parameters={
376
+ "strategy_name": strategy_name,
377
+ "base_dir": args.get("log_folder", "logs"),
378
+ },
379
+ )
380
+
381
+ return None
382
+
383
+
384
+ def _restore_state(
385
+ restorer_config: RestorerConfig | None,
386
+ logging_config: LoggingConfig | None = None,
387
+ strategy_name: str = "",
388
+ ) -> RestoredState | None:
389
+ if restorer_config is None and logging_config is not None:
390
+ restorer_config = _infer_restorer_from_logger(logging_config, strategy_name)
391
+
339
392
  if restorer_config is None:
340
393
  restorer_config = RestorerConfig(type="CsvStateRestorer", parameters={"base_dir": "logs"})
341
394
 
@@ -828,6 +881,79 @@ def _apply_inverse_exchange_mapping(exchanges: list[str]) -> list[str]:
828
881
  return mapped_exchanges
829
882
 
830
883
 
884
+ @dataclass(frozen=True)
885
+ class WarmupResult:
886
+ """Captures the outcome of a warmup simulation for validation."""
887
+
888
+ requested_start_ns: int
889
+ requested_stop_ns: int
890
+ data_start_ns: int | None
891
+ data_stop_ns: int | None
892
+ initial_capital: float
893
+ final_capital: float
894
+
895
+ @property
896
+ def requested_duration_ns(self) -> int:
897
+ return self.requested_stop_ns - self.requested_start_ns
898
+
899
+ @property
900
+ def data_duration_ns(self) -> int:
901
+ if self.data_start_ns is not None and self.data_stop_ns is not None:
902
+ return self.data_stop_ns - self.data_start_ns
903
+ return 0
904
+
905
+ @property
906
+ def coverage_ratio(self) -> float:
907
+ if self.requested_duration_ns <= 0:
908
+ return 1.0
909
+ if self.data_start_ns is None:
910
+ return 0.0
911
+ return max(0.0, self.data_duration_ns / self.requested_duration_ns)
912
+
913
+ @property
914
+ def has_data(self) -> bool:
915
+ return self.data_start_ns is not None
916
+
917
+
918
+ def _validate_warmup_result(result: WarmupResult, min_coverage_ratio: float = 0.5) -> None:
919
+ """Validate that warmup simulation produced usable state for live trading."""
920
+ if not result.has_data:
921
+ raise WarmupValidationError(
922
+ f"Warmup received no data at all. "
923
+ f"Requested window: {to_timedelta(result.requested_duration_ns, unit='ns')}. "
924
+ f"Check your warmup data source configuration."
925
+ )
926
+
927
+ if result.coverage_ratio < min_coverage_ratio:
928
+ data_start_gap = to_timedelta(result.data_start_ns - result.requested_start_ns, unit="ns")
929
+ data_end_gap = to_timedelta(result.requested_stop_ns - result.data_stop_ns, unit="ns")
930
+ raise WarmupValidationError(
931
+ f"Warmup data coverage insufficient: "
932
+ f"received {to_timedelta(result.data_duration_ns, unit='ns')} "
933
+ f"of requested {to_timedelta(result.requested_duration_ns, unit='ns')} "
934
+ f"({result.coverage_ratio:.1%} coverage, minimum required: {min_coverage_ratio:.0%}). "
935
+ f"Data started {data_start_gap} late, ended {data_end_gap} early. "
936
+ f"Check your warmup data source — it may have a max_history limit "
937
+ f"that is shorter than the warmup period."
938
+ )
939
+
940
+ if result.final_capital <= 0:
941
+ raise WarmupValidationError(
942
+ f"Warmup resulted in non-positive capital: {result.final_capital:.2f} "
943
+ f"(started with {result.initial_capital:.2f}). "
944
+ f"This typically means the warmup simulation traded with insufficient data, "
945
+ f"causing unrealistic losses. Check warmup data coverage and indicator warmup periods."
946
+ )
947
+
948
+ if result.initial_capital > 0 and result.final_capital < result.initial_capital * 0.5:
949
+ logger.warning(
950
+ f"<red>Warmup capital dropped significantly: "
951
+ f"{result.initial_capital:.2f} -> {result.final_capital:.2f} "
952
+ f"({(1 - result.final_capital / result.initial_capital):.1%} loss). "
953
+ f"Review warmup data coverage and strategy behavior during warmup.</red>"
954
+ )
955
+
956
+
831
957
  def _run_warmup(
832
958
  ctx: IStrategyContext,
833
959
  restored_state: RestoredState | None,
@@ -922,6 +1048,7 @@ def _run_warmup(
922
1048
  initializer=ctx.initializer,
923
1049
  warmup_mode=True,
924
1050
  )
1051
+ _initial_capital = ctx.account.get_total_capital()
925
1052
 
926
1053
  # - set the time provider to the simulated runner
927
1054
  _live_time_provider = simulated_formatter.time_provider
@@ -944,6 +1071,17 @@ def _run_warmup(
944
1071
 
945
1072
  logger.info("<yellow>Warmup completed</yellow>")
946
1073
 
1074
+ data_range = warmup_runner.data_time_range
1075
+ warmup_result = WarmupResult(
1076
+ requested_start_ns=to_timestamp(warmup_start_time).value,
1077
+ requested_stop_ns=to_timestamp(current_time).value,
1078
+ data_start_ns=data_range[0] if data_range else None,
1079
+ data_stop_ns=data_range[1] if data_range else None,
1080
+ initial_capital=_initial_capital,
1081
+ final_capital=warmup_runner.ctx.account.get_total_capital(),
1082
+ )
1083
+ _validate_warmup_result(warmup_result)
1084
+
947
1085
  # - reset the strategy ctx to point back to live context
948
1086
  if hasattr(ctx.strategy, "ctx"):
949
1087
  setattr(ctx.strategy, "ctx", ctx)
File without changes
File without changes
File without changes
File without changes
File without changes