Qubx 1.1.1.dev1__tar.gz → 1.1.1.dev2__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 (293) hide show
  1. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/PKG-INFO +1 -1
  2. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/_version.py +2 -2
  3. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/management.py +42 -12
  4. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/commands.py +1 -10
  5. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/tui.py +5 -9
  6. qubx-1.1.1.dev2/src/qubx/templates/repo/.claude/skills/simulation-explorer/SKILL.md +196 -0
  7. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/.gitignore +0 -0
  8. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/LICENSE +0 -0
  9. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/README.md +0 -0
  10. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/pyproject.toml +0 -0
  11. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/scripts/build.py +0 -0
  12. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/__init__.py +0 -0
  13. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/_nb_magic.py +0 -0
  14. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/__init__.py +0 -0
  15. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/account.py +0 -0
  16. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/broker.py +0 -0
  17. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/data.py +0 -0
  18. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/iteratedstream.py +0 -0
  19. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/ome.py +0 -0
  20. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/optimization.py +0 -0
  21. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/runner.py +0 -0
  22. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/sentinels.py +0 -0
  23. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/simulated_data.py +0 -0
  24. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/simulated_exchange.py +0 -0
  25. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/simulator.py +0 -0
  26. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/transfers.py +0 -0
  27. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/backtester/utils.py +0 -0
  28. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/__init__.py +0 -0
  29. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/deploy.py +0 -0
  30. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/misc.py +0 -0
  31. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/release.py +0 -0
  32. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/resolver.py +0 -0
  33. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/s3.py +0 -0
  34. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/theme.py +0 -0
  35. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/cli/user_config.py +0 -0
  36. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/config.py +0 -0
  37. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/__init__.py +0 -0
  38. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/__init__.py +0 -0
  39. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/account.py +0 -0
  40. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
  41. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
  42. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/broker.py +0 -0
  43. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
  44. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/data.py +0 -0
  45. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  46. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
  47. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
  48. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
  49. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
  50. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
  51. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
  52. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
  53. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
  54. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +0 -0
  55. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
  56. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
  57. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
  58. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
  59. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
  60. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
  61. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/okx/account.py +0 -0
  62. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/okx/broker.py +0 -0
  63. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
  64. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/factory.py +0 -0
  65. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
  66. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
  67. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
  68. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
  69. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
  70. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
  71. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
  72. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
  73. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
  74. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
  75. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
  76. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
  77. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
  78. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/utils.py +0 -0
  79. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
  80. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/registry.py +0 -0
  81. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/tardis/data.py +0 -0
  82. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/connectors/tardis/utils.py +0 -0
  83. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/__init__.py +0 -0
  84. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/account.py +0 -0
  85. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/basics.py +0 -0
  86. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/context.py +0 -0
  87. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/detectors/__init__.py +0 -0
  88. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/detectors/delisting.py +0 -0
  89. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/detectors/stale.py +0 -0
  90. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/errors.py +0 -0
  91. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/exceptions.py +0 -0
  92. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/helpers.py +0 -0
  93. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/initializer.py +0 -0
  94. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/interfaces.py +0 -0
  95. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/loggers.py +0 -0
  96. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/lookups.py +0 -0
  97. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/metrics.py +0 -0
  98. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/__init__.py +0 -0
  99. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/market.py +0 -0
  100. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/processing.py +0 -0
  101. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/subscription.py +0 -0
  102. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/trading.py +0 -0
  103. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/universe.py +0 -0
  104. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/mixins/utils.py +0 -0
  105. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/series.pxd +0 -0
  106. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/series.pyi +0 -0
  107. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/series.pyx +0 -0
  108. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/utils.pyi +0 -0
  109. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/core/utils.pyx +0 -0
  110. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/__init__.py +0 -0
  111. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/cache.py +0 -0
  112. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/containers.py +0 -0
  113. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/guards.py +0 -0
  114. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/registry.py +0 -0
  115. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storage.py +0 -0
  116. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/ccxt.py +0 -0
  117. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/csv.py +0 -0
  118. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/handy.py +0 -0
  119. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/multi.py +0 -0
  120. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/questdb.py +0 -0
  121. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/stub.py +0 -0
  122. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/storages/utils.py +0 -0
  123. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/data/transformers.py +0 -0
  124. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/__init__.py +0 -0
  125. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/base.py +0 -0
  126. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/composite.py +0 -0
  127. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/csv.py +0 -0
  128. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/indicator.py +0 -0
  129. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/inmemory.py +0 -0
  130. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/prometheus.py +0 -0
  131. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/emitters/questdb.py +0 -0
  132. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/__init__.py +0 -0
  133. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/composite.py +0 -0
  134. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/formatters/__init__.py +0 -0
  135. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/formatters/base.py +0 -0
  136. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/formatters/incremental.py +0 -0
  137. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/formatters/slack.py +0 -0
  138. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/formatters/target_position.py +0 -0
  139. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/redis_streams.py +0 -0
  140. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/exporters/slack.py +0 -0
  141. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/gathering/simplest.py +0 -0
  142. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/health/__init__.py +0 -0
  143. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/health/base.py +0 -0
  144. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/health/dummy.py +0 -0
  145. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/health/server.py +0 -0
  146. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/loggers/__init__.py +0 -0
  147. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/loggers/csv.py +0 -0
  148. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/loggers/factory.py +0 -0
  149. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/loggers/inmemory.py +0 -0
  150. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/loggers/mongo.py +0 -0
  151. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/notifications/__init__.py +0 -0
  152. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/notifications/composite.py +0 -0
  153. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/notifications/slack.py +0 -0
  154. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/notifications/throttler.py +0 -0
  155. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/pandaz/__init__.py +0 -0
  156. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/pandaz/stats.py +0 -0
  157. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/pandaz/ta.py +0 -0
  158. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/pandaz/utils.py +0 -0
  159. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/plugins/__init__.py +0 -0
  160. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/plugins/loader.py +0 -0
  161. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/_build.py +0 -0
  162. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/crypto-fees.ini +0 -0
  163. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
  164. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
  165. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
  166. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
  167. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
  168. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
  169. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
  170. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
  171. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
  172. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
  173. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
  174. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restarts/__init__.py +0 -0
  175. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restarts/state_resolvers.py +0 -0
  176. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restarts/time_finders.py +0 -0
  177. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/__init__.py +0 -0
  178. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/balance.py +0 -0
  179. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/factory.py +0 -0
  180. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/interfaces.py +0 -0
  181. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/position.py +0 -0
  182. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/signal.py +0 -0
  183. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/state.py +0 -0
  184. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/restorers/utils.py +0 -0
  185. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/state/__init__.py +0 -0
  186. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/state/dummy.py +0 -0
  187. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/state/redis.py +0 -0
  188. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/ta/__init__.py +0 -0
  189. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/ta/indicators.pxd +0 -0
  190. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/ta/indicators.pyi +0 -0
  191. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/ta/indicators.pyx +0 -0
  192. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/__init__.py +0 -0
  193. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/base.py +0 -0
  194. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/accounts.toml.j2 +0 -0
  195. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/config.yml.j2 +0 -0
  196. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/jlive.sh.j2 +0 -0
  197. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
  198. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
  199. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  200. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  201. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/project/template.yml +0 -0
  202. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +0 -0
  203. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +0 -0
  204. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +0 -0
  205. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +0 -0
  206. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.gitignore.j2 +0 -0
  207. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.python-version +0 -0
  208. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.vscode/launch.json +0 -0
  209. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.vscode/settings.json +0 -0
  210. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/.vscode/tasks.json +0 -0
  211. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/CLAUDE.md.j2 +0 -0
  212. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/README.md.j2 +0 -0
  213. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/configs/.gitkeep +0 -0
  214. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/justfile.j2 +0 -0
  215. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/pyproject.toml.j2 +0 -0
  216. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/research/.gitkeep +0 -0
  217. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  218. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +0 -0
  219. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  220. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/template.yml +0 -0
  221. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/repo/tests/test_strategy.py.j2 +0 -0
  222. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/__init__.py.j2 +0 -0
  223. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
  224. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/config.yml.j2 +0 -0
  225. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
  226. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
  227. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/strategy.py.j2 +0 -0
  228. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/templates/simple/template.yml +0 -0
  229. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/__init__.py +0 -0
  230. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/advanced.py +0 -0
  231. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/composite.py +0 -0
  232. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/rebalancers.py +0 -0
  233. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/riskctrl.py +0 -0
  234. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/trackers/sizers.py +0 -0
  235. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/__init__.py +0 -0
  236. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/_pyxreloader.py +0 -0
  237. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/charting/lookinglass.py +0 -0
  238. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  239. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/charting/orderbook.py +0 -0
  240. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/collections.py +0 -0
  241. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/hft/__init__.py +0 -0
  242. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/hft/numba_utils.py +0 -0
  243. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/hft/orderbook.pyi +0 -0
  244. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/hft/orderbook.pyx +0 -0
  245. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/marketdata/binance.py +0 -0
  246. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/marketdata/ccxt.py +0 -0
  247. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/marketdata/dukas.py +0 -0
  248. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/misc.py +0 -0
  249. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/nonce.py +0 -0
  250. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/ntp.py +0 -0
  251. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/numbers_utils.py +0 -0
  252. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/orderbook.py +0 -0
  253. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/__init__.py +0 -0
  254. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/dashboard.py +0 -0
  255. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/data.py +0 -0
  256. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/interfaces.py +0 -0
  257. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  258. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  259. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/questdb.py +0 -0
  260. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/rate_limiter.py +0 -0
  261. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/results.py +0 -0
  262. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/ringbuffer.pxd +0 -0
  263. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/ringbuffer.pyi +0 -0
  264. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/ringbuffer.pyx +0 -0
  265. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/__init__.py +0 -0
  266. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
  267. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/accounts.py +0 -0
  268. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/configs.py +0 -0
  269. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/factory.py +0 -0
  270. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/kernel_service.py +0 -0
  271. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/runner.py +0 -0
  272. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/__init__.py +0 -0
  273. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/app.py +0 -0
  274. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/handlers.py +0 -0
  275. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/init_code.py +0 -0
  276. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/kernel.py +0 -0
  277. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/styles.tcss +0 -0
  278. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
  279. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
  280. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
  281. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
  282. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
  283. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
  284. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
  285. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/s3.py +0 -0
  286. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/slack.py +0 -0
  287. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/throttler.py +0 -0
  288. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/time.py +0 -0
  289. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/src/qubx/utils/websocket_manager.py +0 -0
  290. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
  291. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
  292. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
  293. {qubx-1.1.1.dev1 → qubx-1.1.1.dev2}/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.1.dev1
3
+ Version: 1.1.1.dev2
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
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.1.1.dev1'
32
- __version_tuple__ = version_tuple = (1, 1, 1, 'dev1')
31
+ __version__ = version = '1.1.1.dev2'
32
+ __version_tuple__ = version_tuple = (1, 1, 1, 'dev2')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -50,7 +50,7 @@ import pandas as pd
50
50
  from qubx.core.metrics import TradingSessionResult
51
51
  from qubx.utils.misc import blue, cyan, green, magenta, red, yellow
52
52
  from qubx.utils.results import SimulationResultsSaver
53
- from qubx.utils.s3 import S3Client, is_cloud_path
53
+ from qubx.utils.s3 import S3Client, is_account_uri, is_cloud_path
54
54
 
55
55
 
56
56
  class BacktestStorage:
@@ -105,8 +105,11 @@ class BacktestStorage:
105
105
  Initialize BacktestStorage.
106
106
 
107
107
  Args:
108
- base_path: Root path for backtest storage (local dir or cloud URI)
109
- storage_options: Cloud storage credentials dict. None = uses default_s3_account from settings.
108
+ base_path: Root path for backtest storage. Supports:
109
+ - Local directory: ``"/backtests/"``
110
+ - S3 URI: ``"s3://bucket/path"``
111
+ - Account URI: ``"r2:backtests"`` (resolved via ~/.qubx/config.json)
112
+ storage_options: Cloud storage credentials dict. Overrides account lookup.
110
113
  """
111
114
  try:
112
115
  import duckdb
@@ -117,6 +120,13 @@ class BacktestStorage:
117
120
  "duckdb is required for BacktestStorage. "
118
121
  "Install with: pip install 'qubx[storage]' or pip install duckdb"
119
122
  )
123
+
124
+ # Resolve account URI (e.g., "r2:backtests") to S3 path + credentials
125
+ if is_account_uri(base_path) and storage_options is None:
126
+ client, s3_path = S3Client.from_uri(base_path)
127
+ storage_options = client.storage_options
128
+ base_path = f"s3://{s3_path}"
129
+
120
130
  self.base_path = base_path.rstrip("/") + "/"
121
131
  self._is_cloud = is_cloud_path(base_path)
122
132
 
@@ -134,17 +144,37 @@ class BacktestStorage:
134
144
  self._conn.execute("INSTALL httpfs; LOAD httpfs;")
135
145
 
136
146
  opts = self._storage_options or {}
137
- if "key" in opts:
138
- self._conn.execute(f"SET s3_access_key_id='{opts['key']}';")
139
- if "secret" in opts:
140
- self._conn.execute(f"SET s3_secret_access_key='{opts['secret']}';")
147
+ if not opts or "key" not in opts:
148
+ return
149
+
150
+ # Resolve region
151
+ region = "auto"
152
+ if "client_kwargs" in opts:
153
+ region = opts["client_kwargs"].get("region_name", "auto")
154
+ elif "region" in opts:
155
+ region = opts["region"]
156
+
157
+ # Build CREATE SECRET with scope matching our base_path so it takes
158
+ # priority over any pre-existing broader secrets (e.g. from duckdb config).
159
+ scope = self.base_path.rstrip("/")
160
+ endpoint_clause = ""
161
+ url_style_clause = ""
141
162
  if "endpoint_url" in opts:
142
163
  endpoint = opts["endpoint_url"].removeprefix("https://").removeprefix("http://")
143
- self._conn.execute(f"SET s3_endpoint='{endpoint}';")
144
- if "client_kwargs" in opts:
145
- region = opts["client_kwargs"].get("region_name")
146
- if region:
147
- self._conn.execute(f"SET s3_region='{region}';")
164
+ endpoint_clause = f"ENDPOINT '{endpoint}',"
165
+ url_style_clause = "URL_STYLE 'path',"
166
+
167
+ self._conn.execute(f"""
168
+ CREATE OR REPLACE SECRET qubx_s3 (
169
+ TYPE s3,
170
+ KEY_ID '{opts['key']}',
171
+ SECRET '{opts['secret']}',
172
+ {endpoint_clause}
173
+ {url_style_clause}
174
+ REGION '{region}',
175
+ SCOPE '{scope}'
176
+ )
177
+ """)
148
178
 
149
179
  def _glob(self, filename: str) -> str:
150
180
  """
@@ -501,22 +501,13 @@ def browse(results_path: str | None):
501
501
  qubx browse s3://my-bucket/backtests/
502
502
  """
503
503
  from qubx.config import get_settings
504
- from qubx.utils.s3 import S3Client, is_account_uri, is_cloud_path
505
504
 
506
505
  from .tui import run_backtest_browser
507
506
 
508
507
  if results_path is None:
509
508
  results_path = get_settings().default_browse_path or "results"
510
509
 
511
- storage_options: dict | None = None
512
- if is_account_uri(results_path):
513
- client, s3_path = S3Client.from_uri(results_path)
514
- storage_options = client.storage_options
515
- results_path = f"s3://{s3_path}"
516
- elif not is_cloud_path(results_path):
517
- results_path = os.path.abspath(os.path.expanduser(results_path))
518
-
519
- run_backtest_browser(results_path, storage_options=storage_options)
510
+ run_backtest_browser(results_path)
520
511
 
521
512
 
522
513
  @main.command()
@@ -450,12 +450,10 @@ class BacktestResultsTree(Tree[BacktestTreeNode]):
450
450
  def __init__(
451
451
  self,
452
452
  root_path: str,
453
- storage_options: dict | None = None,
454
453
  on_loaded: "callable | None" = None,
455
454
  ):
456
455
  self.root_path = root_path
457
- self.storage_options = storage_options
458
- self.storage = BacktestStorage(root_path, storage_options=storage_options)
456
+ self.storage = BacktestStorage(root_path)
459
457
  self._root_data = BacktestTreeNode("Backtest Results", root_path, self.storage)
460
458
  self._on_loaded = on_loaded
461
459
  super().__init__(self._root_data.name, data=self._root_data)
@@ -1073,10 +1071,9 @@ class BacktestBrowserApp(App):
1073
1071
  }
1074
1072
  """
1075
1073
 
1076
- def __init__(self, root_path: str, storage_options: dict | None = None):
1074
+ def __init__(self, root_path: str):
1077
1075
  super().__init__()
1078
1076
  self.root_path = root_path
1079
- self.storage_options = storage_options
1080
1077
  self.current_results: list[dict[str, Any]] = []
1081
1078
  self.current_storage: BacktestStorage | None = None
1082
1079
  self._selected_result: dict[str, Any] | None = None
@@ -1099,7 +1096,7 @@ class BacktestBrowserApp(App):
1099
1096
  with Horizontal():
1100
1097
  with Vertical(id="tree-container", classes="box"):
1101
1098
  yield Label("Backtest Results Tree")
1102
- yield BacktestResultsTree(self.root_path, storage_options=self.storage_options)
1099
+ yield BacktestResultsTree(self.root_path)
1103
1100
 
1104
1101
  with Vertical(id="content-container", classes="box"):
1105
1102
  with Horizontal(id="controls"):
@@ -1273,7 +1270,6 @@ class BacktestBrowserApp(App):
1273
1270
 
1274
1271
  new_tree = BacktestResultsTree(
1275
1272
  self.root_path,
1276
- storage_options=self.storage_options,
1277
1273
  on_loaded=self._restore_tree_state,
1278
1274
  )
1279
1275
  tree_container.mount(new_tree)
@@ -1564,10 +1560,10 @@ class BacktestBrowserApp(App):
1564
1560
  self.copy_to_clipboard(str(load_id))
1565
1561
  self.notify(f"Copied: {load_id}", timeout=3)
1566
1562
 
1567
- def run_backtest_browser(root_path: str, storage_options: dict | None = None):
1563
+ def run_backtest_browser(root_path: str):
1568
1564
  """Run the backtest browser TUI application"""
1569
1565
  try:
1570
- app = BacktestBrowserApp(root_path, storage_options=storage_options)
1566
+ app = BacktestBrowserApp(root_path)
1571
1567
  app.run()
1572
1568
  except Exception as e:
1573
1569
  logger.error(f"Failed to start backtest browser: {e}")
@@ -0,0 +1,196 @@
1
+ ---
2
+ name: simulation-explorer
3
+ description: Use when loading, comparing, or debugging backtest simulation results — tearsheets, execution logs, portfolio analysis, log inspection, and diagnosing data issues.
4
+ argument-hint: [backtest_path_or_paths] [question]
5
+ ---
6
+
7
+ # Simulation Explorer
8
+
9
+ You help load, compare, analyze, and debug backtest simulation results produced by `qubx simulate`.
10
+
11
+ ## Loading Results
12
+
13
+ ### From Python (notebook or script):
14
+ ```python
15
+ from qubx.backtester.management import BacktestStorage
16
+ from qubx.core.metrics import tearsheet
17
+
18
+ # Account URI — uses named S3 account from ~/.qubx/config.json
19
+ bs = BacktestStorage("r2:backtests")
20
+
21
+ # Also supports direct S3 or local paths
22
+ bs = BacktestStorage("s3://my-bucket/backtests")
23
+ bs = BacktestStorage("results") # local directory
24
+
25
+ # Load a specific run
26
+ r = bs.load("STRATEGY_CLASS/run_name/YYYYMMDD_HHMMSS")
27
+
28
+ # Compare multiple runs
29
+ r1 = bs.load("STRATEGY_CLASS/run1/20260320_120000")
30
+ r2 = bs.load("STRATEGY_CLASS/run2/20260320_130000")
31
+ tearsheet([r1, r2])
32
+ ```
33
+
34
+ ### From CLI:
35
+ ```bash
36
+ # Browse results interactively (account URI)
37
+ uv run qubx browse r2:backtests
38
+
39
+ # Also works with S3 or local paths
40
+ uv run qubx browse s3://my-bucket/backtests
41
+ uv run qubx browse results/
42
+
43
+ # Query results with filters
44
+ uv run qubx backtests r2:backtests -w "sharpe > 1.5" -n 10
45
+ ```
46
+
47
+ ## Result Object API
48
+
49
+ A loaded result `r` provides:
50
+
51
+ | Property | Description |
52
+ |----------|-------------|
53
+ | `r.tearsheet()` | Full performance report |
54
+ | `r.equity` | Equity curve (Series) |
55
+ | `r.portfolio_log` | Portfolio state over time (DataFrame) |
56
+ | `r.executions_log` | All executed trades (DataFrame) |
57
+ | `r.signals_log` | All signals generated (DataFrame) |
58
+ | `r.emitter_data` | Custom emitter data (if `enable_inmemory_emitter: true`) |
59
+
60
+ ### Common analysis patterns:
61
+
62
+ ```python
63
+ # Equity curve
64
+ r.equity.plot()
65
+
66
+ # Positions over time
67
+ r.portfolio_log.filter(like="_Pos")
68
+
69
+ # Funding PnL per asset
70
+ r.portfolio_log.filter(like="_Funding")
71
+
72
+ # Funding PnL breakdown
73
+ fp = r.get_funding_per_asset()
74
+ fp.iloc[-1].sort_values(ascending=False).head(15).plot(kind="bar")
75
+
76
+ # Trade list
77
+ r.executions_log
78
+
79
+ # Compare funding between runs
80
+ from qubx.pandaz.utils import scols
81
+ fp1 = r1.get_funding_per_asset()
82
+ fp2 = r2.get_funding_per_asset()
83
+ scols(
84
+ fp1["BTC"].rename("run1"),
85
+ fp2["BTC"].rename("run2"),
86
+ ).plot()
87
+ ```
88
+
89
+ ## Comparing Two Runs
90
+
91
+ ### Tearsheet comparison:
92
+ ```python
93
+ tearsheet([r1, r2])
94
+ ```
95
+
96
+ ### Execution diff — find trades in one run but not the other:
97
+ ```python
98
+ df1 = r1.executions_log.set_index(["symbol", "exchange"], append=True)
99
+ df2 = r2.executions_log.set_index(["symbol", "exchange"], append=True)
100
+ df_diff = df2[~df2.index.isin(df1.index)]
101
+ len(df_diff) # number of different executions
102
+ df_diff.head()
103
+ ```
104
+
105
+ ## Debugging via Log Files
106
+
107
+ Simulation logs are stored alongside results when run with `-L` flag:
108
+ ```
109
+ STRATEGY_CLASS/run_name/YYYYMMDD_HHMMSS/
110
+ ├── run_name.log # simulation log
111
+ ├── run_name.yml # config used
112
+ ├── portfolio.parquet
113
+ ├── executions.parquet
114
+ ├── signals.parquet
115
+ └── ...
116
+ ```
117
+
118
+ ### Log analysis patterns:
119
+
120
+ ```bash
121
+ # Find selector activity (pair entries/exits)
122
+ grep "selectors" /path/to/run.log
123
+
124
+ # Compare selector lines between two runs
125
+ diff <(grep "selectors" run1.log) <(grep "selectors" run2.log)
126
+
127
+ # Find first divergence point
128
+ paste <(grep "selectors" run1.log) <(grep "selectors" run2.log) | awk -F'\t' '$1 != $2 {print NR; exit}'
129
+
130
+ # Check funding payment data
131
+ grep "\[FP\]" /path/to/run.log | head -20
132
+
133
+ # Check errors
134
+ grep "❌" /path/to/run.log
135
+ ```
136
+
137
+ ## Running Simulations
138
+
139
+ ### From CLI:
140
+ ```bash
141
+ # Basic simulation
142
+ uv run qubx simulate configs/my_strategy.yaml -o s3://backtests -L
143
+
144
+ # Override dates
145
+ uv run qubx simulate configs/my_strategy.yaml -s 2026-01-01 -e 2026-03-01 -o s3://backtests -L
146
+
147
+ # Custom run name
148
+ uv run qubx simulate configs/my_strategy.yaml -o s3://backtests -n "baseline_v1" -L
149
+ ```
150
+
151
+ ### From notebook (quick iteration):
152
+ ```python
153
+ from qubx.utils.runner.runner import simulate_config
154
+
155
+ results = simulate_config(
156
+ "configs/my_strategy.yaml",
157
+ start="2026-01-15",
158
+ stop="2026-02-01",
159
+ # Override any strategy parameter:
160
+ max_pairs=3,
161
+ min_entry_ev=0,
162
+ )
163
+ r = results[0]
164
+ r.tearsheet()
165
+ ```
166
+
167
+ ## Output Paths
168
+
169
+ Results are organized as:
170
+ ```
171
+ <output>/<strategy_class>/<config_name>/<YYYYMMDD_HHMMSS>/
172
+ ```
173
+
174
+ - Local output: `-o results` or `-o /tmp/my_test`
175
+ - Account URI: `-o r2:backtests` (uses named S3 account from `~/.qubx/config.json`)
176
+ - S3 output: `-o s3://backtests`
177
+
178
+ ## Common Debugging Scenarios
179
+
180
+ ### Results differ between runs with same config
181
+ 1. Compare tearsheets: `tearsheet([r1, r2])`
182
+ 2. Find first selector divergence in logs
183
+ 3. Check if OHLC data or funding payment data differs at the divergence point
184
+ 4. Look for cache-related issues if prefetch is enabled
185
+
186
+ ### Strategy not trading
187
+ 1. Check if `on_event` fires: look for selector log lines
188
+ 2. Check `on_fit` timing: look for universe/pair discovery logs
189
+ 3. Verify funding payment data is available
190
+ 4. Check if `on_start` was called (requires market data for initial instruments)
191
+
192
+ ### Unexpected PnL
193
+ 1. Check `r.portfolio_log.filter(like="_Funding")` for funding PnL
194
+ 2. Check `r.portfolio_log.filter(like="_Pos")` for position changes
195
+ 3. Compare `r.executions_log` with expected trades
196
+ 4. Check spread PnL vs funding PnL breakdown
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes