Qubx 1.0.6.dev7__tar.gz → 1.1.1.dev1__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 (292) hide show
  1. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/PKG-INFO +1 -1
  2. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/_version.py +2 -2
  3. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/management.py +81 -0
  4. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/commands.py +159 -29
  5. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/tui.py +382 -93
  6. qubx-1.1.1.dev1/src/qubx/cli/user_config.py +20 -0
  7. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/base.py +15 -15
  8. qubx-1.1.1.dev1/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +334 -0
  9. qubx-1.1.1.dev1/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +2229 -0
  10. qubx-1.1.1.dev1/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +235 -0
  11. qubx-1.1.1.dev1/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +49 -0
  12. qubx-1.1.1.dev1/src/qubx/templates/repo/.gitignore.j2 +22 -0
  13. qubx-1.1.1.dev1/src/qubx/templates/repo/.python-version +1 -0
  14. qubx-1.1.1.dev1/src/qubx/templates/repo/.vscode/launch.json +94 -0
  15. qubx-1.1.1.dev1/src/qubx/templates/repo/.vscode/settings.json +17 -0
  16. qubx-1.1.1.dev1/src/qubx/templates/repo/.vscode/tasks.json +144 -0
  17. qubx-1.1.1.dev1/src/qubx/templates/repo/CLAUDE.md.j2 +47 -0
  18. qubx-1.1.1.dev1/src/qubx/templates/repo/README.md.j2 +22 -0
  19. qubx-1.1.1.dev1/src/qubx/templates/repo/justfile.j2 +18 -0
  20. qubx-1.1.1.dev1/src/qubx/templates/repo/pyproject.toml.j2 +38 -0
  21. qubx-1.1.1.dev1/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +5 -0
  22. qubx-1.1.1.dev1/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +23 -0
  23. qubx-1.1.1.dev1/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +22 -0
  24. qubx-1.1.1.dev1/src/qubx/templates/repo/template.yml +29 -0
  25. qubx-1.1.1.dev1/src/qubx/templates/repo/tests/test_strategy.py.j2 +7 -0
  26. qubx-1.1.1.dev1/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  27. qubx-1.1.1.dev1/src/qubx/utils/runner/__init__.py +0 -0
  28. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/s3.py +9 -0
  29. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/.gitignore +0 -0
  30. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/LICENSE +0 -0
  31. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/README.md +0 -0
  32. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/pyproject.toml +0 -0
  33. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/scripts/build.py +0 -0
  34. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/__init__.py +0 -0
  35. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/_nb_magic.py +0 -0
  36. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/__init__.py +0 -0
  37. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/account.py +0 -0
  38. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/broker.py +0 -0
  39. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/data.py +0 -0
  40. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/iteratedstream.py +0 -0
  41. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/ome.py +0 -0
  42. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/optimization.py +0 -0
  43. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/runner.py +0 -0
  44. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/sentinels.py +0 -0
  45. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/simulated_data.py +0 -0
  46. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/simulated_exchange.py +0 -0
  47. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/simulator.py +0 -0
  48. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/transfers.py +0 -0
  49. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/backtester/utils.py +0 -0
  50. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/__init__.py +0 -0
  51. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/deploy.py +0 -0
  52. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/misc.py +0 -0
  53. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/release.py +0 -0
  54. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/resolver.py +0 -0
  55. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/s3.py +0 -0
  56. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/cli/theme.py +0 -0
  57. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/config.py +0 -0
  58. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/__init__.py +0 -0
  59. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/__init__.py +0 -0
  60. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/account.py +0 -0
  61. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
  62. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
  63. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/broker.py +0 -0
  64. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
  65. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/data.py +0 -0
  66. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  67. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
  68. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
  69. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
  70. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
  71. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
  72. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
  73. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
  74. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
  75. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +0 -0
  76. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
  77. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
  78. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
  79. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
  80. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
  81. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
  82. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/okx/account.py +0 -0
  83. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/okx/broker.py +0 -0
  84. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
  85. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/factory.py +0 -0
  86. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
  87. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
  88. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
  89. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
  90. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
  91. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
  92. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
  93. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
  94. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
  95. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
  96. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
  97. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
  98. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
  99. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/utils.py +0 -0
  100. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
  101. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/registry.py +0 -0
  102. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/tardis/data.py +0 -0
  103. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/connectors/tardis/utils.py +0 -0
  104. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/__init__.py +0 -0
  105. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/account.py +0 -0
  106. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/basics.py +0 -0
  107. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/context.py +0 -0
  108. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/detectors/__init__.py +0 -0
  109. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/detectors/delisting.py +0 -0
  110. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/detectors/stale.py +0 -0
  111. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/errors.py +0 -0
  112. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/exceptions.py +0 -0
  113. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/helpers.py +0 -0
  114. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/initializer.py +0 -0
  115. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/interfaces.py +0 -0
  116. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/loggers.py +0 -0
  117. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/lookups.py +0 -0
  118. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/metrics.py +0 -0
  119. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/__init__.py +0 -0
  120. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/market.py +0 -0
  121. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/processing.py +0 -0
  122. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/subscription.py +0 -0
  123. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/trading.py +0 -0
  124. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/universe.py +0 -0
  125. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/mixins/utils.py +0 -0
  126. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/series.pxd +0 -0
  127. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/series.pyi +0 -0
  128. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/series.pyx +0 -0
  129. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/utils.pyi +0 -0
  130. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/core/utils.pyx +0 -0
  131. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/__init__.py +0 -0
  132. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/cache.py +0 -0
  133. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/containers.py +0 -0
  134. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/guards.py +0 -0
  135. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/registry.py +0 -0
  136. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storage.py +0 -0
  137. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/ccxt.py +0 -0
  138. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/csv.py +0 -0
  139. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/handy.py +0 -0
  140. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/multi.py +0 -0
  141. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/questdb.py +0 -0
  142. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/stub.py +0 -0
  143. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/storages/utils.py +0 -0
  144. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/data/transformers.py +0 -0
  145. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/__init__.py +0 -0
  146. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/base.py +0 -0
  147. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/composite.py +0 -0
  148. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/csv.py +0 -0
  149. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/indicator.py +0 -0
  150. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/inmemory.py +0 -0
  151. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/prometheus.py +0 -0
  152. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/emitters/questdb.py +0 -0
  153. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/__init__.py +0 -0
  154. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/composite.py +0 -0
  155. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/formatters/__init__.py +0 -0
  156. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/formatters/base.py +0 -0
  157. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/formatters/incremental.py +0 -0
  158. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/formatters/slack.py +0 -0
  159. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/formatters/target_position.py +0 -0
  160. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/redis_streams.py +0 -0
  161. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/exporters/slack.py +0 -0
  162. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/gathering/simplest.py +0 -0
  163. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/health/__init__.py +0 -0
  164. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/health/base.py +0 -0
  165. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/health/dummy.py +0 -0
  166. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/health/server.py +0 -0
  167. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/loggers/__init__.py +0 -0
  168. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/loggers/csv.py +0 -0
  169. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/loggers/factory.py +0 -0
  170. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/loggers/inmemory.py +0 -0
  171. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/loggers/mongo.py +0 -0
  172. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/notifications/__init__.py +0 -0
  173. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/notifications/composite.py +0 -0
  174. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/notifications/slack.py +0 -0
  175. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/notifications/throttler.py +0 -0
  176. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/pandaz/__init__.py +0 -0
  177. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/pandaz/stats.py +0 -0
  178. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/pandaz/ta.py +0 -0
  179. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/pandaz/utils.py +0 -0
  180. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/plugins/__init__.py +0 -0
  181. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/plugins/loader.py +0 -0
  182. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/_build.py +0 -0
  183. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/crypto-fees.ini +0 -0
  184. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
  185. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
  186. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
  187. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
  188. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
  189. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
  190. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
  191. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
  192. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
  193. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
  194. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
  195. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restarts/__init__.py +0 -0
  196. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restarts/state_resolvers.py +0 -0
  197. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restarts/time_finders.py +0 -0
  198. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/__init__.py +0 -0
  199. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/balance.py +0 -0
  200. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/factory.py +0 -0
  201. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/interfaces.py +0 -0
  202. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/position.py +0 -0
  203. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/signal.py +0 -0
  204. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/state.py +0 -0
  205. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/restorers/utils.py +0 -0
  206. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/state/__init__.py +0 -0
  207. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/state/dummy.py +0 -0
  208. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/state/redis.py +0 -0
  209. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/ta/__init__.py +0 -0
  210. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/ta/indicators.pxd +0 -0
  211. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/ta/indicators.pyi +0 -0
  212. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/ta/indicators.pyx +0 -0
  213. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/__init__.py +0 -0
  214. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/accounts.toml.j2 +0 -0
  215. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/config.yml.j2 +0 -0
  216. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/jlive.sh.j2 +0 -0
  217. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
  218. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
  219. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  220. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  221. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/project/template.yml +0 -0
  222. /qubx-1.0.6.dev7/src/qubx/utils/plotting/__init__.py → /qubx-1.1.1.dev1/src/qubx/templates/repo/configs/.gitkeep +0 -0
  223. /qubx-1.0.6.dev7/src/qubx/utils/plotting/renderers/__init__.py → /qubx-1.1.1.dev1/src/qubx/templates/repo/research/.gitkeep +0 -0
  224. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/__init__.py.j2 +0 -0
  225. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
  226. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/config.yml.j2 +0 -0
  227. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
  228. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
  229. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/strategy.py.j2 +0 -0
  230. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/templates/simple/template.yml +0 -0
  231. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/__init__.py +0 -0
  232. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/advanced.py +0 -0
  233. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/composite.py +0 -0
  234. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/rebalancers.py +0 -0
  235. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/riskctrl.py +0 -0
  236. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/trackers/sizers.py +0 -0
  237. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/__init__.py +0 -0
  238. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/_pyxreloader.py +0 -0
  239. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/charting/lookinglass.py +0 -0
  240. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  241. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/charting/orderbook.py +0 -0
  242. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/collections.py +0 -0
  243. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/hft/__init__.py +0 -0
  244. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/hft/numba_utils.py +0 -0
  245. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/hft/orderbook.pyi +0 -0
  246. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/hft/orderbook.pyx +0 -0
  247. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/marketdata/binance.py +0 -0
  248. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/marketdata/ccxt.py +0 -0
  249. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/marketdata/dukas.py +0 -0
  250. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/misc.py +0 -0
  251. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/nonce.py +0 -0
  252. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/ntp.py +0 -0
  253. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/numbers_utils.py +0 -0
  254. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/orderbook.py +0 -0
  255. {qubx-1.0.6.dev7/src/qubx/utils/runner → qubx-1.1.1.dev1/src/qubx/utils/plotting}/__init__.py +0 -0
  256. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/plotting/dashboard.py +0 -0
  257. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/plotting/data.py +0 -0
  258. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/plotting/interfaces.py +0 -0
  259. /qubx-1.0.6.dev7/src/qubx/utils/plotting/renderers/plotly.py → /qubx-1.1.1.dev1/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  260. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/questdb.py +0 -0
  261. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/rate_limiter.py +0 -0
  262. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/results.py +0 -0
  263. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/ringbuffer.pxd +0 -0
  264. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/ringbuffer.pyi +0 -0
  265. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/ringbuffer.pyx +0 -0
  266. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
  267. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/accounts.py +0 -0
  268. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/configs.py +0 -0
  269. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/factory.py +0 -0
  270. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/kernel_service.py +0 -0
  271. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/runner.py +0 -0
  272. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/__init__.py +0 -0
  273. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/app.py +0 -0
  274. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/handlers.py +0 -0
  275. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/init_code.py +0 -0
  276. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/kernel.py +0 -0
  277. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/styles.tcss +0 -0
  278. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
  279. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
  280. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
  281. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
  282. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
  283. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
  284. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
  285. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/slack.py +0 -0
  286. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/throttler.py +0 -0
  287. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/time.py +0 -0
  288. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/src/qubx/utils/websocket_manager.py +0 -0
  289. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
  290. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
  291. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
  292. {qubx-1.0.6.dev7 → qubx-1.1.1.dev1}/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.0.6.dev7
3
+ Version: 1.1.1.dev1
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.0.6.dev7'
32
- __version_tuple__ = version_tuple = (1, 0, 6, 'dev7')
31
+ __version__ = version = '1.1.1.dev1'
32
+ __version_tuple__ = version_tuple = (1, 1, 1, 'dev1')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -502,6 +502,87 @@ class BacktestStorage:
502
502
  return []
503
503
  return df["backtest_id"].tolist()
504
504
 
505
+ def delete(self, backtest_id: str) -> None:
506
+ """
507
+ Delete a single backtest run directory.
508
+
509
+ Args:
510
+ backtest_id: Relative path within base_path,
511
+ e.g. "my_strategy/Nimble/20240301_120000"
512
+ """
513
+ path = f"{self.base_path}{backtest_id.strip('/')}/"
514
+ if self._is_cloud:
515
+ S3Client(storage_options=self._storage_options).rm(path, recursive=True)
516
+ else:
517
+ import shutil
518
+
519
+ shutil.rmtree(path, ignore_errors=True)
520
+ self._reset_conn()
521
+
522
+ def delete_group(self, group_path: str) -> None:
523
+ """
524
+ Delete an entire group directory (class-level or config-level).
525
+
526
+ Args:
527
+ group_path: Relative path within base_path,
528
+ e.g. "my_strategy/Nimble" (config) or "Nimble" (class)
529
+ """
530
+ path = f"{self.base_path}{group_path.strip('/')}/"
531
+ if self._is_cloud:
532
+ S3Client(storage_options=self._storage_options).rm(path, recursive=True)
533
+ else:
534
+ import shutil
535
+
536
+ shutil.rmtree(path, ignore_errors=True)
537
+ self._reset_conn()
538
+
539
+ def _reset_conn(self) -> None:
540
+ """Reset DuckDB connection to clear cached file metadata after mutations."""
541
+ self._conn.close()
542
+ self._conn = self._duckdb.connect()
543
+ if self._is_cloud:
544
+ self._setup_cloud_duckdb()
545
+
546
+ def get_log(self, backtest_id: str, config_name: str | None = None) -> str | None:
547
+ """
548
+ Read the log file for a backtest run.
549
+
550
+ Args:
551
+ backtest_id: Relative path within base_path.
552
+ config_name: Config name (used for log filename).
553
+ If None, searches for any .log file.
554
+
555
+ Returns:
556
+ Log file content as string, or None if not found.
557
+ """
558
+ run_dir = f"{self.base_path}{backtest_id.strip('/')}/"
559
+
560
+ if config_name:
561
+ log_path = f"{run_dir}{config_name}.log"
562
+ else:
563
+ try:
564
+ files_df = self._conn.execute(f"SELECT file FROM glob('{run_dir}*.log')").df()
565
+ if files_df.empty:
566
+ return None
567
+ log_path = files_df["file"].iloc[0]
568
+ except Exception:
569
+ return None
570
+
571
+ if self._is_cloud:
572
+ try:
573
+ from qubx.utils.s3 import strip_scheme
574
+
575
+ client = S3Client(storage_options=self._storage_options)
576
+ with client.fs.open_input_stream(strip_scheme(log_path)) as f:
577
+ return f.read().decode("utf-8")
578
+ except Exception:
579
+ return None
580
+ else:
581
+ from pathlib import Path
582
+
583
+ p = Path(log_path)
584
+ return p.read_text(encoding="utf-8", errors="replace") if p.is_file() else None
585
+
505
586
  def export_backtests_to_markdown(self, backtest_id: str, path: str, tags: tuple[str] | None = None):
506
587
  if tsr := self.load(backtest_id):
507
588
  tsr.to_markdown(path, list(tags) if tags else None)
@@ -496,21 +496,27 @@ def browse(results_path: str | None):
496
496
 
497
497
  qubx browse /backtests/momentum/
498
498
 
499
+ qubx browse r2:backtests
500
+
499
501
  qubx browse s3://my-bucket/backtests/
500
502
  """
501
503
  from qubx.config import get_settings
502
- from qubx.utils.s3 import is_cloud_path
504
+ from qubx.utils.s3 import S3Client, is_account_uri, is_cloud_path
503
505
 
504
506
  from .tui import run_backtest_browser
505
507
 
506
508
  if results_path is None:
507
509
  results_path = get_settings().default_browse_path or "results"
508
510
 
509
- # - only resolve local paths; cloud URIs (s3://, gs://, az://) are passed as-is
510
- if not is_cloud_path(results_path):
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):
511
517
  results_path = os.path.abspath(os.path.expanduser(results_path))
512
518
 
513
- run_backtest_browser(results_path)
519
+ run_backtest_browser(results_path, storage_options=storage_options)
514
520
 
515
521
 
516
522
  @main.command()
@@ -571,6 +577,12 @@ def browse(results_path: str | None):
571
577
  is_flag=True,
572
578
  help="List all available built-in templates",
573
579
  )
580
+ @click.option("--description", "-d", type=str, default=None, help="Project description (repo template)")
581
+ @click.option("--author", type=str, default=None, help="Author name (repo template)")
582
+ @click.option("--email", type=str, default=None, help="Author email (repo template)")
583
+ @click.option("--github-org", type=str, default=None, help="GitHub org or username (repo template)")
584
+ @click.option("--create-repo", is_flag=True, default=False, help="Create GitHub repository (repo template)")
585
+ @click.option("--private/--public", default=True, help="Repository visibility (repo template)")
574
586
  def init(
575
587
  template: str,
576
588
  template_path: str | None,
@@ -580,6 +592,12 @@ def init(
580
592
  timeframe: str,
581
593
  output_dir: str,
582
594
  list_templates: bool,
595
+ description: str | None,
596
+ author: str | None,
597
+ email: str | None,
598
+ github_org: str | None,
599
+ create_repo: bool,
600
+ private: bool,
583
601
  ):
584
602
  """
585
603
  Create a new strategy from a template.
@@ -589,9 +607,14 @@ def init(
589
607
  - Configuration file for qubx run command
590
608
  - Package structure for proper imports
591
609
 
610
+ Use --template repo for a full repository with CI, tests, and justfile.
611
+
592
612
  The generated strategy can be run immediately with:
593
613
  uv run qubx run --config config.yml --paper
594
614
  """
615
+ import subprocess
616
+ import sys
617
+
595
618
  from qubx.templates import TemplateError, TemplateManager
596
619
 
597
620
  try:
@@ -604,40 +627,147 @@ def init(
604
627
  return
605
628
 
606
629
  click.echo("Available templates:")
607
- for template_name, metadata in templates.items():
608
- description = metadata.get("description", "No description")
609
- click.echo(f" {template_name:<15} - {description}")
630
+ for tpl_name, metadata in templates.items():
631
+ tpl_desc = metadata.get("description", "No description")
632
+ click.echo(f" {tpl_name:<15} - {tpl_desc}")
610
633
  return
611
634
 
612
- # Generate strategy
613
- strategy_path = manager.generate_strategy(
614
- template_name=template if not template_path else None,
615
- template_path=template_path,
616
- output_dir=output_dir,
617
- name=name,
618
- exchange=exchange,
619
- symbols=symbols,
620
- timeframe=timeframe,
621
- )
622
-
623
- click.echo(f"✅ Strategy '{name}' created successfully!")
624
- click.echo(f"📁 Location: {strategy_path}")
625
- click.echo()
626
- click.echo("To run your strategy:")
627
- click.echo(f" cd {strategy_path}")
628
- click.echo(" uv run qubx run config.yml --paper")
629
- click.echo()
630
- click.echo("To run in Jupyter mode:")
631
- click.echo(" ./jpaper.sh")
635
+ if template == "repo":
636
+ _init_repo(
637
+ manager, name, description, author, email, github_org,
638
+ exchange, symbols, timeframe, output_dir, create_repo, private,
639
+ )
640
+ else:
641
+ strategy_path = manager.generate_strategy(
642
+ template_name=template if not template_path else None,
643
+ template_path=template_path,
644
+ output_dir=output_dir,
645
+ name=name,
646
+ exchange=exchange,
647
+ symbols=symbols,
648
+ timeframe=timeframe,
649
+ )
650
+
651
+ click.echo(f"Strategy '{name}' created successfully!")
652
+ click.echo(f"Location: {strategy_path}")
653
+ click.echo()
654
+ click.echo("To run your strategy:")
655
+ click.echo(f" cd {strategy_path}")
656
+ click.echo(" uv run qubx run config.yml --paper")
657
+ click.echo()
658
+ click.echo("To run in Jupyter mode:")
659
+ click.echo(" ./jpaper.sh")
632
660
 
633
661
  except TemplateError as e:
634
- click.echo(f"❌ Template error: {e}", err=True)
662
+ click.echo(f"Error: {e}", err=True)
635
663
  raise click.Abort()
636
664
  except Exception as e:
637
- click.echo(f"Unexpected error: {e}", err=True)
665
+ click.echo(f"Unexpected error: {e}", err=True)
638
666
  raise click.Abort()
639
667
 
640
668
 
669
+ def _init_repo(
670
+ manager,
671
+ name: str,
672
+ description: str | None,
673
+ author: str | None,
674
+ email: str | None,
675
+ github_org: str | None,
676
+ exchange: str,
677
+ symbols: str,
678
+ timeframe: str,
679
+ output_dir: str,
680
+ create_repo: bool,
681
+ private: bool,
682
+ ):
683
+ """Interactive wizard for creating a full strategy repository."""
684
+ import subprocess
685
+ import sys
686
+
687
+ from qubx.cli.user_config import load_init_config, save_init_config
688
+ from qubx.templates import TemplateError
689
+
690
+ config = load_init_config()
691
+ interactive = sys.stdin.isatty()
692
+
693
+ if interactive:
694
+ click.echo("\n Strategy Repository Setup\n")
695
+ name = click.prompt(" Strategy name", default=name)
696
+ description = click.prompt(" Description", default=description or "")
697
+ author = click.prompt(" Author name", default=author or config.get("author_name", ""))
698
+ email = click.prompt(" Author email", default=email or config.get("author_email", ""))
699
+ github_org = click.prompt(
700
+ " GitHub organization",
701
+ default=github_org or config.get("github_org", ""),
702
+ )
703
+ exchange = click.prompt(" Exchange", default=exchange)
704
+ symbols = click.prompt(" Symbols (comma-separated)", default=symbols)
705
+ timeframe = click.prompt(" Timeframe", default=timeframe)
706
+ if not create_repo:
707
+ create_repo = click.confirm("\n Create GitHub repository?", default=False)
708
+ if create_repo:
709
+ private = click.confirm(" Private repository?", default=True)
710
+ click.echo()
711
+ else:
712
+ description = description or ""
713
+ author = author or config.get("author_name", "")
714
+ email = email or config.get("author_email", "")
715
+ github_org = github_org or config.get("github_org", "")
716
+
717
+ # Save preferences for next time
718
+ save_init_config({
719
+ "author_name": author,
720
+ "author_email": email,
721
+ "github_org": github_org,
722
+ })
723
+
724
+ # Generate project from template
725
+ strategy_path = manager.generate_strategy(
726
+ template_name="repo",
727
+ output_dir=output_dir,
728
+ name=name,
729
+ description=description,
730
+ author_name=author,
731
+ author_email=email,
732
+ github_org=github_org,
733
+ exchange=exchange,
734
+ symbols=symbols,
735
+ timeframe=timeframe,
736
+ )
737
+
738
+ # Initialize git repo with main as default branch
739
+ subprocess.run(["git", "init", "-b", "main"], cwd=strategy_path, capture_output=True)
740
+ subprocess.run(["git", "add", "-A"], cwd=strategy_path, capture_output=True)
741
+ subprocess.run(
742
+ ["git", "commit", "-m", "Initial project setup"],
743
+ cwd=strategy_path,
744
+ capture_output=True,
745
+ )
746
+
747
+ click.echo(f"Strategy '{name}' created at {strategy_path}")
748
+
749
+ # Create GitHub repo if requested
750
+ if create_repo and github_org:
751
+ repo_name = f"{github_org}/{name}"
752
+ visibility = "--private" if private else "--public"
753
+ result = subprocess.run(
754
+ ["gh", "repo", "create", repo_name, visibility, "--source=.", "--push"],
755
+ cwd=strategy_path,
756
+ capture_output=True,
757
+ text=True,
758
+ )
759
+ if result.returncode == 0:
760
+ click.echo(f"GitHub repository created: {repo_name}")
761
+ else:
762
+ click.echo(f"Failed to create GitHub repo: {result.stderr.strip()}", err=True)
763
+
764
+ click.echo()
765
+ click.echo("Next steps:")
766
+ click.echo(f" cd {strategy_path}")
767
+ click.echo(" just install")
768
+ click.echo(" just test")
769
+
770
+
641
771
  @main.group()
642
772
  def kernel():
643
773
  """