Qubx 1.1.2.dev6__tar.gz → 1.1.2.dev8__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 (294) hide show
  1. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/PKG-INFO +1 -1
  2. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/__init__.py +38 -8
  3. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/_version.py +2 -2
  4. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/data.py +3 -4
  5. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/runner.py +4 -6
  6. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/simulated_data.py +17 -17
  7. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/utils.py +17 -8
  8. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/cli/commands.py +1 -11
  9. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/account.py +6 -6
  10. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/broker.py +2 -2
  11. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/data.py +3 -4
  12. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +2 -2
  13. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/handlers/ohlc.py +4 -4
  14. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/utils.py +2 -2
  15. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/tardis/data.py +6 -5
  16. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/basics.py +7 -6
  17. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/detectors/delisting.py +4 -5
  18. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/detectors/stale.py +2 -3
  19. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/helpers.py +5 -5
  20. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/loggers.py +18 -1
  21. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/lookups.py +2 -1
  22. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/metrics.py +30 -27
  23. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/mixins/market.py +5 -3
  24. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/mixins/universe.py +1 -1
  25. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/cache.py +8 -8
  26. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/guards.py +7 -6
  27. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/storages/ccxt.py +5 -5
  28. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/storages/multi.py +2 -2
  29. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/storages/questdb.py +4 -4
  30. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/storages/utils.py +3 -2
  31. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/transformers.py +4 -4
  32. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/emitters/base.py +2 -3
  33. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/emitters/indicator.py +2 -2
  34. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/emitters/inmemory.py +3 -2
  35. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/emitters/questdb.py +2 -1
  36. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/exporters/formatters/base.py +3 -4
  37. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/pandaz/ta.py +7 -7
  38. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restarts/time_finders.py +24 -1
  39. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/trackers/advanced.py +3 -2
  40. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/trackers/sizers.py +2 -2
  41. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/charting/lookinglass.py +4 -3
  42. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/charting/mpl_helpers.py +2 -2
  43. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/misc.py +4 -2
  44. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/results.py +13 -11
  45. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/configs.py +1 -2
  46. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/runner.py +108 -23
  47. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/init_code.py +16 -16
  48. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/time.py +19 -1
  49. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/.gitignore +0 -0
  50. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/LICENSE +0 -0
  51. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/README.md +0 -0
  52. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/pyproject.toml +0 -0
  53. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/scripts/build.py +0 -0
  54. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/_nb_magic.py +0 -0
  55. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/__init__.py +0 -0
  56. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/account.py +0 -0
  57. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/broker.py +0 -0
  58. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/iteratedstream.py +0 -0
  59. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/management.py +0 -0
  60. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/ome.py +0 -0
  61. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/optimization.py +0 -0
  62. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/sentinels.py +0 -0
  63. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/simulated_exchange.py +0 -0
  64. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/simulator.py +0 -0
  65. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/backtester/transfers.py +0 -0
  66. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/cli/__init__.py +0 -0
  67. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/cli/deploy.py +0 -0
  68. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/cli/misc.py +0 -0
  69. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/cli/release.py +0 -0
  70. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/cli/resolver.py +0 -0
  71. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/cli/s3.py +0 -0
  72. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/cli/theme.py +0 -0
  73. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/cli/tui.py +0 -0
  74. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/cli/user_config.py +0 -0
  75. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/config.py +0 -0
  76. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/__init__.py +0 -0
  77. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/__init__.py +0 -0
  78. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
  79. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
  80. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
  81. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  82. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
  83. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
  84. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
  85. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
  86. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
  87. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
  88. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
  89. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +0 -0
  90. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
  91. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
  92. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
  93. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
  94. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
  95. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
  96. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/okx/account.py +0 -0
  97. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/okx/broker.py +0 -0
  98. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
  99. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/factory.py +0 -0
  100. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
  101. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
  102. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
  103. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
  104. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
  105. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
  106. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
  107. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
  108. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
  109. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
  110. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
  111. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
  112. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
  113. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/registry.py +0 -0
  114. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/connectors/tardis/utils.py +0 -0
  115. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/__init__.py +0 -0
  116. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/account.py +0 -0
  117. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/context.py +0 -0
  118. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/detectors/__init__.py +0 -0
  119. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/errors.py +0 -0
  120. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/exceptions.py +0 -0
  121. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/initializer.py +0 -0
  122. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/interfaces.py +0 -0
  123. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/mixins/__init__.py +0 -0
  124. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/mixins/processing.py +0 -0
  125. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/mixins/subscription.py +0 -0
  126. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/mixins/trading.py +0 -0
  127. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/mixins/utils.py +0 -0
  128. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/series.pxd +0 -0
  129. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/series.pyi +0 -0
  130. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/series.pyx +0 -0
  131. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/utils.pyi +0 -0
  132. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/core/utils.pyx +0 -0
  133. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/__init__.py +0 -0
  134. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/containers.py +0 -0
  135. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/registry.py +0 -0
  136. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/storage.py +0 -0
  137. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/storages/csv.py +0 -0
  138. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/storages/handy.py +0 -0
  139. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/data/storages/stub.py +0 -0
  140. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/emitters/__init__.py +0 -0
  141. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/emitters/composite.py +0 -0
  142. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/emitters/csv.py +0 -0
  143. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/emitters/prometheus.py +0 -0
  144. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/exporters/__init__.py +0 -0
  145. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/exporters/composite.py +0 -0
  146. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/exporters/formatters/__init__.py +0 -0
  147. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/exporters/formatters/incremental.py +0 -0
  148. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/exporters/formatters/slack.py +0 -0
  149. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/exporters/formatters/target_position.py +0 -0
  150. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/exporters/redis_streams.py +0 -0
  151. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/exporters/slack.py +0 -0
  152. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/gathering/simplest.py +0 -0
  153. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/health/__init__.py +0 -0
  154. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/health/base.py +0 -0
  155. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/health/dummy.py +0 -0
  156. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/health/server.py +0 -0
  157. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/loggers/__init__.py +0 -0
  158. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/loggers/csv.py +0 -0
  159. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/loggers/factory.py +0 -0
  160. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/loggers/inmemory.py +0 -0
  161. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/loggers/mongo.py +0 -0
  162. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/notifications/__init__.py +0 -0
  163. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/notifications/composite.py +0 -0
  164. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/notifications/slack.py +0 -0
  165. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/notifications/throttler.py +0 -0
  166. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/pandaz/__init__.py +0 -0
  167. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/pandaz/stats.py +0 -0
  168. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/pandaz/utils.py +0 -0
  169. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/plugins/__init__.py +0 -0
  170. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/plugins/loader.py +0 -0
  171. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/_build.py +0 -0
  172. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/crypto-fees.ini +0 -0
  173. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
  174. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
  175. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
  176. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
  177. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
  178. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
  179. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
  180. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
  181. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
  182. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
  183. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
  184. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restarts/__init__.py +0 -0
  185. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restarts/state_resolvers.py +0 -0
  186. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restorers/__init__.py +0 -0
  187. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restorers/balance.py +0 -0
  188. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restorers/factory.py +0 -0
  189. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restorers/interfaces.py +0 -0
  190. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restorers/position.py +0 -0
  191. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restorers/signal.py +0 -0
  192. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restorers/state.py +0 -0
  193. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/restorers/utils.py +0 -0
  194. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/state/__init__.py +0 -0
  195. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/state/dummy.py +0 -0
  196. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/state/redis.py +0 -0
  197. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/ta/__init__.py +0 -0
  198. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/ta/indicators.pxd +0 -0
  199. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/ta/indicators.pyi +0 -0
  200. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/ta/indicators.pyx +0 -0
  201. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/__init__.py +0 -0
  202. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/base.py +0 -0
  203. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/project/accounts.toml.j2 +0 -0
  204. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/project/config.yml.j2 +0 -0
  205. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/project/jlive.sh.j2 +0 -0
  206. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
  207. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
  208. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  209. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  210. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/project/template.yml +0 -0
  211. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +0 -0
  212. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +0 -0
  213. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/.claude/skills/simulation-explorer/SKILL.md +0 -0
  214. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +0 -0
  215. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +0 -0
  216. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/.gitignore.j2 +0 -0
  217. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/.python-version +0 -0
  218. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/.vscode/launch.json +0 -0
  219. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/.vscode/settings.json +0 -0
  220. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/.vscode/tasks.json +0 -0
  221. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/CLAUDE.md.j2 +0 -0
  222. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/README.md.j2 +0 -0
  223. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/configs/.gitkeep +0 -0
  224. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/justfile.j2 +0 -0
  225. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/pyproject.toml.j2 +0 -0
  226. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/research/.gitkeep +0 -0
  227. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  228. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +0 -0
  229. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  230. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/template.yml +0 -0
  231. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/repo/tests/test_strategy.py.j2 +0 -0
  232. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/simple/__init__.py.j2 +0 -0
  233. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
  234. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/simple/config.yml.j2 +0 -0
  235. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
  236. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
  237. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/simple/strategy.py.j2 +0 -0
  238. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/templates/simple/template.yml +0 -0
  239. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/trackers/__init__.py +0 -0
  240. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/trackers/composite.py +0 -0
  241. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/trackers/rebalancers.py +0 -0
  242. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/trackers/riskctrl.py +0 -0
  243. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/__init__.py +0 -0
  244. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/_pyxreloader.py +0 -0
  245. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/charting/orderbook.py +0 -0
  246. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/collections.py +0 -0
  247. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/hft/__init__.py +0 -0
  248. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/hft/numba_utils.py +0 -0
  249. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/hft/orderbook.pyi +0 -0
  250. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/hft/orderbook.pyx +0 -0
  251. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/marketdata/binance.py +0 -0
  252. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/marketdata/ccxt.py +0 -0
  253. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/marketdata/dukas.py +0 -0
  254. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/nonce.py +0 -0
  255. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/ntp.py +0 -0
  256. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/numbers_utils.py +0 -0
  257. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/orderbook.py +0 -0
  258. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/plotting/__init__.py +0 -0
  259. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/plotting/dashboard.py +0 -0
  260. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/plotting/data.py +0 -0
  261. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/plotting/interfaces.py +0 -0
  262. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  263. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  264. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/questdb.py +0 -0
  265. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/rate_limiter.py +0 -0
  266. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/ringbuffer.pxd +0 -0
  267. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/ringbuffer.pyi +0 -0
  268. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/ringbuffer.pyx +0 -0
  269. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/__init__.py +0 -0
  270. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
  271. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/accounts.py +0 -0
  272. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/factory.py +0 -0
  273. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/kernel_service.py +0 -0
  274. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/__init__.py +0 -0
  275. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/app.py +0 -0
  276. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/handlers.py +0 -0
  277. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/kernel.py +0 -0
  278. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/styles.tcss +0 -0
  279. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
  280. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/widgets/account_summary.py +0 -0
  281. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
  282. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
  283. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
  284. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
  285. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
  286. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
  287. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/s3.py +0 -0
  288. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/slack.py +0 -0
  289. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/throttler.py +0 -0
  290. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/src/qubx/utils/websocket_manager.py +0 -0
  291. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
  292. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
  293. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
  294. {qubx-1.1.2.dev6 → qubx-1.1.2.dev8}/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.dev6
3
+ Version: 1.1.2.dev8
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
@@ -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 bind_platform_identity(bot_id: str | None = None, instance_id: str | None = None):
163
- """Bind platform identity fields (bot_id, instance_id) to all log messages globally."""
177
+ def _update_patcher():
178
+ """Reconfigure loguru with a single patcher that applies all bound fields."""
164
179
  def patcher(record):
165
- if bot_id:
166
- record["extra"]["bot_id"] = bot_id
167
- if instance_id:
168
- record["extra"]["instance_id"] = 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.dev6'
22
- __version_tuple__ = version_tuple = (1, 1, 2, 'dev6')
21
+ __version__ = version = '1.1.2.dev8'
22
+ __version_tuple__ = version_tuple = (1, 1, 2, 'dev8')
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 = pd.Timestamp(self.time_provider.time())
148
- end = start - nbarsback * pd.Timedelta(timeframe)
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, pd.Timedelta(prefetch_cfg.prefetch_period)))
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
- _initial_capital = self.setup.capital
615
- if isinstance(_initial_capital, dict):
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 pd.Timedelta(x).to_numpy().item()
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 pd.Timestamp(value).value
332
+ return to_timestamp(value).value
333
333
  # - fallback: try to convert via pd.Timestamp
334
- return pd.Timestamp(value).value
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(pd.Timestamp(self._watermark, unit="ns"), type=time_col.type)
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(pd.Timestamp(self._watermark, unit="ns").date(), type=time_col.type)
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 = pd.Timestamp(self._watermark, unit="ns") if self._watermark else "N/A"
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 = pd.Timestamp(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 = pd.Timestamp(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 = pd.Timestamp(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 = pd.Timestamp(current_time)
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] = pd.Timedelta(warmup_period)
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(pd.Timestamp(self._current_time, unit="ns"), self._stop) # type: ignore
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 = pd.Timestamp(start)
1105
- self._stop = pd.Timestamp(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(pd.Timestamp(self._start).timestamp() * 1e9)
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), pd.Timedelta(timeframe).asm8.item()
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 pd.Timedelta(x).to_numpy().item()
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
- prefix = f"<lc>{now}</lc> [<level>{record['level'].icon}</level>] {identity}<cyan>({{module}})</cyan> "
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 >= pd.Timedelta("1d"):
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 >= pd.Timedelta("1min"):
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 > pd.Timedelta("1s"):
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 pd.Timedelta("1Min")
550
+ return to_timedelta("1Min")
542
551
 
543
- if in_timeframe < pd.Timedelta("1h"):
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, log_to_file, name=name, log_file=log_file,
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 = pd.Timedelta(interval).total_seconds()
255
+ sleep_time = to_timedelta(interval).total_seconds()
256
256
  retries = 0
257
257
 
258
258
  if backoff is not None:
259
- sleep_time = pd.Timedelta(backoff).total_seconds()
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(pd.Timedelta(self.subscription_interval).total_seconds())
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 * pd.Timedelta("1d")).asm8.item() // 1000000 # type: ignore
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 pd.Timedelta(_current_time - _dt) > pd.Timedelta(self.balance_interval): # type: ignore
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 = pd.Timedelta(current_time - start_time).total_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 = pd.Timestamp(self.time_provider.time())
323
- delta = pd.to_timedelta(timeframe) * nbarsback
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 = cast(pd.Timedelta, pd.Timedelta(minutes=10))
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 = pd.Timedelta(warmup_period) // pd.Timedelta(timeframe)
92
+ nbarsback = to_timedelta(warmup_period) // to_timedelta(timeframe)
93
93
  exch_timeframe = self._data_provider._get_exch_timeframe(timeframe)
94
- _tf_msec = pd.to_timedelta(timeframe).value // 1_000_000
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 = pd.to_timedelta(timeframe).value // 1_000_000 # convert to 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=pd.Timestamp(raw["timestamp"], unit="ms"), # type: ignore
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 - pd.Timedelta(timeframe) * nbarsback
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 - pd.Timedelta(period)
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 - pd.Timedelta(period)
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 - pd.Timedelta(period)
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 = pd.Timedelta(timeframe)
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