Qubx 1.1.3.dev23__tar.gz → 1.1.3.dev24__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 (309) hide show
  1. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/PKG-INFO +1 -1
  2. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/_version.py +2 -2
  3. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/cli/commands.py +118 -42
  4. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/cli/release.py +100 -61
  5. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/.gitignore +0 -0
  6. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/LICENSE +0 -0
  7. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/README.md +0 -0
  8. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/pyproject.toml +0 -0
  9. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/scripts/build.py +0 -0
  10. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/__init__.py +0 -0
  11. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/_nb_magic.py +0 -0
  12. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/__init__.py +0 -0
  13. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/account.py +0 -0
  14. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/broker.py +0 -0
  15. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/data.py +0 -0
  16. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/iteratedstream.py +0 -0
  17. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/management.py +0 -0
  18. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/ome.py +0 -0
  19. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/optimization.py +0 -0
  20. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/runner.py +0 -0
  21. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/sentinels.py +0 -0
  22. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/simulated_data.py +0 -0
  23. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/simulated_exchange.py +0 -0
  24. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/simulator.py +0 -0
  25. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/transfers.py +0 -0
  26. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/backtester/utils.py +0 -0
  27. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/cli/__init__.py +0 -0
  28. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/cli/deploy.py +0 -0
  29. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/cli/misc.py +0 -0
  30. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/cli/resolver.py +0 -0
  31. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/cli/s3.py +0 -0
  32. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/cli/theme.py +0 -0
  33. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/cli/tui.py +0 -0
  34. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/cli/user_config.py +0 -0
  35. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/config.py +0 -0
  36. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/__init__.py +0 -0
  37. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/__init__.py +0 -0
  38. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/account.py +0 -0
  39. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
  40. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
  41. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/broker.py +0 -0
  42. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
  43. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/data.py +0 -0
  44. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  45. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
  46. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
  47. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
  48. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
  49. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
  50. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
  51. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
  52. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
  53. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +0 -0
  54. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
  55. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
  56. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
  57. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
  58. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
  59. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
  60. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/okx/account.py +0 -0
  61. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/okx/broker.py +0 -0
  62. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
  63. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/factory.py +0 -0
  64. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
  65. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
  66. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
  67. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
  68. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
  69. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
  70. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
  71. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
  72. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
  73. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
  74. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/rate_limits.py +0 -0
  75. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
  76. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
  77. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
  78. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/utils.py +0 -0
  79. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
  80. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/registry.py +0 -0
  81. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/tardis/data.py +0 -0
  82. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/connectors/tardis/utils.py +0 -0
  83. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/control/__init__.py +0 -0
  84. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/control/builtin.py +0 -0
  85. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/control/decorator.py +0 -0
  86. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/control/executor.py +0 -0
  87. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/control/interfaces.py +0 -0
  88. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/control/server.py +0 -0
  89. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/control/types.py +0 -0
  90. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/__init__.py +0 -0
  91. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/account.py +0 -0
  92. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/basics.py +0 -0
  93. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/context.py +0 -0
  94. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/detectors/__init__.py +0 -0
  95. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/detectors/delisting.py +0 -0
  96. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/detectors/stale.py +0 -0
  97. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/errors.py +0 -0
  98. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/exceptions.py +0 -0
  99. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/helpers.py +0 -0
  100. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/initializer.py +0 -0
  101. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/interfaces.py +0 -0
  102. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/loggers.py +0 -0
  103. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/lookups.py +0 -0
  104. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/metrics.py +0 -0
  105. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/mixins/__init__.py +0 -0
  106. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/mixins/market.py +0 -0
  107. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/mixins/processing.py +0 -0
  108. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/mixins/subscription.py +0 -0
  109. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/mixins/trading.py +0 -0
  110. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/mixins/universe.py +0 -0
  111. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/mixins/utils.py +0 -0
  112. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/series.pxd +0 -0
  113. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/series.pyi +0 -0
  114. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/series.pyx +0 -0
  115. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/utils.pyi +0 -0
  116. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/core/utils.pyx +0 -0
  117. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/__init__.py +0 -0
  118. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/cache.py +0 -0
  119. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/containers.py +0 -0
  120. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/guards.py +0 -0
  121. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/registry.py +0 -0
  122. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/storage.py +0 -0
  123. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/storages/ccxt.py +0 -0
  124. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/storages/csv.py +0 -0
  125. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/storages/handy.py +0 -0
  126. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/storages/multi.py +0 -0
  127. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/storages/questdb.py +0 -0
  128. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/storages/stub.py +0 -0
  129. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/storages/utils.py +0 -0
  130. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/data/transformers.py +0 -0
  131. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/emitters/__init__.py +0 -0
  132. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/emitters/base.py +0 -0
  133. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/emitters/composite.py +0 -0
  134. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/emitters/csv.py +0 -0
  135. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/emitters/indicator.py +0 -0
  136. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/emitters/inmemory.py +0 -0
  137. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/emitters/prometheus.py +0 -0
  138. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/emitters/questdb.py +0 -0
  139. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/exporters/__init__.py +0 -0
  140. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/exporters/composite.py +0 -0
  141. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/exporters/formatters/__init__.py +0 -0
  142. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/exporters/formatters/base.py +0 -0
  143. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/exporters/formatters/incremental.py +0 -0
  144. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/exporters/formatters/slack.py +0 -0
  145. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/exporters/formatters/target_position.py +0 -0
  146. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/exporters/redis_streams.py +0 -0
  147. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/exporters/slack.py +0 -0
  148. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/gathering/simplest.py +0 -0
  149. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/health/__init__.py +0 -0
  150. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/health/base.py +0 -0
  151. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/health/dummy.py +0 -0
  152. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/loggers/__init__.py +0 -0
  153. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/loggers/csv.py +0 -0
  154. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/loggers/factory.py +0 -0
  155. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/loggers/inmemory.py +0 -0
  156. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/loggers/mongo.py +0 -0
  157. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/loggers/postgres.py +0 -0
  158. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/notifications/__init__.py +0 -0
  159. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/notifications/composite.py +0 -0
  160. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/notifications/slack.py +0 -0
  161. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/notifications/throttler.py +0 -0
  162. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/pandaz/__init__.py +0 -0
  163. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/pandaz/stats.py +0 -0
  164. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/pandaz/ta.py +0 -0
  165. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/pandaz/utils.py +0 -0
  166. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/plugins/__init__.py +0 -0
  167. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/plugins/loader.py +0 -0
  168. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/rate_limiting/__init__.py +0 -0
  169. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/rate_limiting/backend.py +0 -0
  170. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/rate_limiting/config.py +0 -0
  171. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/rate_limiting/engine.py +0 -0
  172. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/rate_limiting/ip_resolver.py +0 -0
  173. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/rate_limiting/manager.py +0 -0
  174. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/rate_limiting/redis_backend.py +0 -0
  175. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/_build.py +0 -0
  176. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/crypto-fees.ini +0 -0
  177. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
  178. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
  179. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
  180. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
  181. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
  182. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
  183. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
  184. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
  185. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
  186. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
  187. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
  188. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restarts/__init__.py +0 -0
  189. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restarts/state_resolvers.py +0 -0
  190. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restarts/time_finders.py +0 -0
  191. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restorers/__init__.py +0 -0
  192. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restorers/balance.py +0 -0
  193. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restorers/factory.py +0 -0
  194. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restorers/interfaces.py +0 -0
  195. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restorers/position.py +0 -0
  196. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restorers/signal.py +0 -0
  197. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restorers/state.py +0 -0
  198. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/restorers/utils.py +0 -0
  199. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/state/__init__.py +0 -0
  200. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/state/dummy.py +0 -0
  201. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/state/redis.py +0 -0
  202. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/ta/__init__.py +0 -0
  203. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/ta/indicators.pxd +0 -0
  204. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/ta/indicators.pyi +0 -0
  205. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/ta/indicators.pyx +0 -0
  206. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/__init__.py +0 -0
  207. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/base.py +0 -0
  208. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/project/accounts.toml.j2 +0 -0
  209. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/project/config.yml.j2 +0 -0
  210. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/project/jlive.sh.j2 +0 -0
  211. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
  212. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
  213. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  214. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  215. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/project/template.yml +0 -0
  216. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +0 -0
  217. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +0 -0
  218. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/.claude/skills/simulation-explorer/SKILL.md +0 -0
  219. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +0 -0
  220. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +0 -0
  221. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/.gitignore.j2 +0 -0
  222. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/.python-version +0 -0
  223. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/.vscode/launch.json +0 -0
  224. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/.vscode/settings.json +0 -0
  225. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/.vscode/tasks.json +0 -0
  226. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/CLAUDE.md.j2 +0 -0
  227. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/README.md.j2 +0 -0
  228. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/configs/.gitkeep +0 -0
  229. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/justfile.j2 +0 -0
  230. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/pyproject.toml.j2 +0 -0
  231. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/research/.gitkeep +0 -0
  232. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  233. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +0 -0
  234. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  235. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/template.yml +0 -0
  236. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/repo/tests/test_strategy.py.j2 +0 -0
  237. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/simple/__init__.py.j2 +0 -0
  238. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
  239. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/simple/config.yml.j2 +0 -0
  240. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
  241. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
  242. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/simple/strategy.py.j2 +0 -0
  243. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/templates/simple/template.yml +0 -0
  244. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/trackers/__init__.py +0 -0
  245. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/trackers/advanced.py +0 -0
  246. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/trackers/composite.py +0 -0
  247. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/trackers/rebalancers.py +0 -0
  248. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/trackers/riskctrl.py +0 -0
  249. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/trackers/sizers.py +0 -0
  250. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/__init__.py +0 -0
  251. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/_pyxreloader.py +0 -0
  252. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/charting/lookinglass.py +0 -0
  253. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  254. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/charting/orderbook.py +0 -0
  255. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/collections.py +0 -0
  256. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/hft/__init__.py +0 -0
  257. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/hft/numba_utils.py +0 -0
  258. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/hft/orderbook.pyi +0 -0
  259. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/hft/orderbook.pyx +0 -0
  260. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/marketdata/binance.py +0 -0
  261. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/marketdata/ccxt.py +0 -0
  262. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/marketdata/dukas.py +0 -0
  263. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/misc.py +0 -0
  264. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/nonce.py +0 -0
  265. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/ntp.py +0 -0
  266. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/numbers_utils.py +0 -0
  267. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/orderbook.py +0 -0
  268. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/plotting/__init__.py +0 -0
  269. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/plotting/dashboard.py +0 -0
  270. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/plotting/data.py +0 -0
  271. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/plotting/interfaces.py +0 -0
  272. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  273. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  274. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/questdb.py +0 -0
  275. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/rate_limiter.py +0 -0
  276. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/results.py +0 -0
  277. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/ringbuffer.pxd +0 -0
  278. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/ringbuffer.pyi +0 -0
  279. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/ringbuffer.pyx +0 -0
  280. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/__init__.py +0 -0
  281. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
  282. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/accounts.py +0 -0
  283. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/configs.py +0 -0
  284. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/factory.py +0 -0
  285. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/kernel_service.py +0 -0
  286. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/runner.py +0 -0
  287. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/__init__.py +0 -0
  288. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/app.py +0 -0
  289. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/handlers.py +0 -0
  290. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/init_code.py +0 -0
  291. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/kernel.py +0 -0
  292. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/styles.tcss +0 -0
  293. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
  294. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/widgets/account_summary.py +0 -0
  295. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
  296. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
  297. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
  298. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
  299. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
  300. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
  301. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/s3.py +0 -0
  302. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/slack.py +0 -0
  303. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/throttler.py +0 -0
  304. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/time.py +0 -0
  305. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/src/qubx/utils/websocket_manager.py +0 -0
  306. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
  307. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
  308. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
  309. {qubx-1.1.3.dev23 → qubx-1.1.3.dev24}/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.3.dev23
3
+ Version: 1.1.3.dev24
4
4
  Summary: Qubx - Quantitative Trading Framework
5
5
  Project-URL: homepage, https://xlydian.com
6
6
  Project-URL: repository, https://github.com/xLydianSoftware/Qubx
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.1.3.dev23'
22
- __version_tuple__ = version_tuple = (1, 1, 3, 'dev23')
21
+ __version__ = version = '1.1.3.dev24'
22
+ __version_tuple__ = version_tuple = (1, 1, 3, 'dev24')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -110,6 +110,20 @@ def main(debug: bool, debug_port: int, log_level: str):
110
110
  @click.option("--no-notifiers", is_flag=True, default=False, help="Disable lifecycle notifiers.", show_default=True)
111
111
  @click.option("--no-exporters", is_flag=True, default=False, help="Disable trade exporters.", show_default=True)
112
112
  @click.option("--override", type=Path, default=None, help="Sparse YAML file to deep-merge on top of config.", show_default=False)
113
+ @click.option(
114
+ "--from-sources",
115
+ is_flag=True,
116
+ default=False,
117
+ help="Clone the repo pinned in config.release.source and run the strategy from that checkout.",
118
+ show_default=True,
119
+ )
120
+ @click.option(
121
+ "--refresh-sources",
122
+ is_flag=True,
123
+ default=False,
124
+ help="Force re-clone of the source cache (only with --from-sources).",
125
+ show_default=True,
126
+ )
113
127
  def run(
114
128
  config_file: Path,
115
129
  account_file: Path | None,
@@ -129,6 +143,8 @@ def run(
129
143
  no_notifiers: bool,
130
144
  no_exporters: bool,
131
145
  override: Path | None,
146
+ from_sources: bool,
147
+ refresh_sources: bool,
132
148
  ):
133
149
  """
134
150
  Starts the strategy with the given configuration file. If paper mode is enabled, account is not required.
@@ -137,6 +153,11 @@ def run(
137
153
  - If provided, the account file is searched first.\n
138
154
  - If exists, accounts.toml located in the same folder with the config searched.\n
139
155
  - If neither of the above are provided, the accounts.toml in the ~/qubx/accounts.toml path is searched.
156
+
157
+ With --from-sources, the repo pinned in ``config.release.source`` is cloned into
158
+ ``.releases/sources/<repo>_<ref>/``, its dependencies installed via ``uv sync``,
159
+ and then ``qubx run`` is re-invoked inside that checkout. Useful for verifying
160
+ that a release config actually boots against its pinned strategy code.
140
161
  """
141
162
  from qubx.utils.misc import add_project_to_system_path, logo
142
163
  from qubx.utils.runner.runner import run_strategy_yaml, run_strategy_yaml_in_jupyter
@@ -147,6 +168,68 @@ def run(
147
168
  click.echo("Error: --jupyter and --textual cannot be used together.", err=True)
148
169
  raise click.Abort()
149
170
 
171
+ if from_sources:
172
+ import subprocess
173
+ import sys
174
+
175
+ from .release import prepare_source_checkout
176
+
177
+ abs_config = str(config_file.resolve())
178
+ try:
179
+ clone_dir, _ = prepare_source_checkout(
180
+ config_file=abs_config,
181
+ refresh=refresh_sources,
182
+ install_project=True,
183
+ )
184
+ except Exception as e:
185
+ click.echo(click.style(f"Failed to prepare source checkout: {e}", fg="red"), err=True)
186
+ raise click.Abort()
187
+
188
+ # Re-invoke `qubx run` inside the cloned venv. Forward every flag the user passed,
189
+ # minus --from-sources/--refresh-sources, and replace the config path with its
190
+ # absolute form so the inner invocation finds it regardless of CWD.
191
+ forwarded: list[str] = []
192
+ if account_file:
193
+ forwarded += ["--account-file", str(Path(account_file).resolve())]
194
+ if paper:
195
+ forwarded.append("--paper")
196
+ if jupyter:
197
+ forwarded.append("--jupyter")
198
+ if textual:
199
+ forwarded.append("--textual")
200
+ if textual_dev:
201
+ forwarded.append("--textual-dev")
202
+ if textual_web:
203
+ forwarded.append("--textual-web")
204
+ if textual_port is not None:
205
+ forwarded += ["--textual-port", str(textual_port)]
206
+ if textual_host and textual_host != "0.0.0.0":
207
+ forwarded += ["--textual-host", textual_host]
208
+ if kernel_only:
209
+ forwarded.append("--kernel-only")
210
+ if connect:
211
+ forwarded += ["--connect", str(Path(connect).resolve())]
212
+ if restore:
213
+ forwarded.append("--restore")
214
+ if no_color:
215
+ forwarded.append("--no-color")
216
+ if dev:
217
+ forwarded.append("--dev")
218
+ if no_emission:
219
+ forwarded.append("--no-emission")
220
+ if no_notifiers:
221
+ forwarded.append("--no-notifiers")
222
+ if no_exporters:
223
+ forwarded.append("--no-exporters")
224
+ if override:
225
+ forwarded += ["--override", str(Path(override).resolve())]
226
+
227
+ cmd = ["uv", "run", "qubx", "run", abs_config, *forwarded]
228
+ logger.info(f"Running inside source checkout: <cyan>{clone_dir}</cyan>")
229
+ logger.info(f"<dim>$ {' '.join(cmd)}</dim>")
230
+ result = subprocess.run(cmd, cwd=str(clone_dir))
231
+ sys.exit(result.returncode)
232
+
150
233
  if dev:
151
234
  add_project_to_system_path() # Adds ~/projects in dev mode
152
235
  add_project_to_system_path(config_file.parent)
@@ -378,6 +461,20 @@ def validate(config_file: Path, no_check_imports: bool):
378
461
  help="Commit changes and create tag in repo (default: False)",
379
462
  show_default=True,
380
463
  )
464
+ @click.option(
465
+ "--from-sources",
466
+ is_flag=True,
467
+ default=False,
468
+ help="Clone the repo pinned in config.release.source and build the release from that checkout.",
469
+ show_default=True,
470
+ )
471
+ @click.option(
472
+ "--refresh-sources",
473
+ is_flag=True,
474
+ default=False,
475
+ help="Force re-clone of the source cache (only with --from-sources).",
476
+ show_default=True,
477
+ )
381
478
  def release(
382
479
  directory: str,
383
480
  config: str,
@@ -385,6 +482,8 @@ def release(
385
482
  message: str | None,
386
483
  commit: bool,
387
484
  output_dir: str,
485
+ from_sources: bool,
486
+ refresh_sources: bool,
388
487
  ) -> None:
389
488
  """
390
489
  Releases the strategy to a zip file.
@@ -399,8 +498,26 @@ def release(
399
498
  - logging: Logging configuration
400
499
 
401
500
  All of the dependencies are included in the zip file.
501
+
502
+ With --from-sources, the config's ``release.source`` section is used to clone
503
+ the referenced repo at the specified ref and build the release from that
504
+ checkout instead of the local ``directory``. Used for CI-driven releases
505
+ where the config lives in a separate repo from the strategy code.
402
506
  """
403
- from .release import release_strategy
507
+ from .release import release_from_source, release_strategy
508
+
509
+ if from_sources:
510
+ zip_path = release_from_source(
511
+ config_file=config,
512
+ output_dir=output_dir,
513
+ tag=tag,
514
+ refresh=refresh_sources,
515
+ )
516
+ if zip_path:
517
+ click.echo(zip_path)
518
+ else:
519
+ raise click.Abort()
520
+ return
404
521
 
405
522
  release_strategy(
406
523
  directory=directory,
@@ -412,47 +529,6 @@ def release(
412
529
  )
413
530
 
414
531
 
415
- @main.command("release-from-source")
416
- @click.option(
417
- "--config",
418
- "-c",
419
- type=click.Path(exists=True, resolve_path=True),
420
- help="Path to a config YAML file with a release.source section",
421
- required=True,
422
- )
423
- @click.option(
424
- "--output-dir",
425
- "-o",
426
- type=click.Path(exists=False),
427
- help="Output directory to put zip file.",
428
- default=".releases",
429
- show_default=True,
430
- )
431
- @click.option(
432
- "--tag",
433
- "-t",
434
- type=click.STRING,
435
- help="Additional tag for this release",
436
- required=False,
437
- )
438
- def release_from_source_cmd(config: str, output_dir: str, tag: str | None) -> None:
439
- """
440
- Build a release by cloning the source repo specified in the config's release.source section.
441
-
442
- Used for CI-driven releases where the config lives in a separate repo from the strategy code.
443
- The config must have a release.source section specifying the GitHub repo and ref.
444
- """
445
- from .release import release_from_source
446
-
447
- zip_path = release_from_source(
448
- config_file=config,
449
- output_dir=output_dir,
450
- tag=tag,
451
- )
452
- if zip_path:
453
- click.echo(zip_path)
454
-
455
-
456
532
  @main.command()
457
533
  @click.argument(
458
534
  "zip-file",
@@ -562,102 +562,146 @@ def release_strategy(
562
562
  logger.opt(colors=False).error(f"Error releasing strategy: {e}")
563
563
 
564
564
 
565
- def release_from_source(
565
+ def prepare_source_checkout(
566
566
  config_file: str,
567
- output_dir: str,
568
- tag: str | None = None,
569
- ) -> str | None:
567
+ cache_root: str | Path = ".releases/sources",
568
+ refresh: bool = False,
569
+ install_project: bool = True,
570
+ ) -> tuple[Path, StrategyConfig]:
570
571
  """
571
- Build a release by cloning the source repo specified in the config's release.source section.
572
+ Prepare a source checkout for --from-sources workflows.
572
573
 
573
- This is the entry point for CI-driven releases (e.g., from xrelease GitHub Action).
574
- It reads the release.source.repo and release.source.ref from the config YAML,
575
- clones that repo at the specified ref into a temp directory, copies the config
576
- into the clone, and runs the standard release flow.
574
+ Clones the repo specified in ``config.release.source`` into a cached dir
575
+ (reusing it across invocations) and runs ``uv sync --all-extras`` so
576
+ dependencies are resolved and the strategy package is importable.
577
577
 
578
578
  Args:
579
- config_file: Path to the strategy config YAML (must have a release.source section)
580
- output_dir: Directory to write the release ZIP
581
- tag: Optional tag suffix for the release name
579
+ config_file: Path to the strategy config YAML (must have release.source).
580
+ cache_root: Root dir for cached checkouts. Relative paths are resolved
581
+ against the current working directory.
582
+ refresh: If True, wipe any existing cached checkout and re-clone.
583
+ install_project: If True (default), install the project package into the
584
+ cloned venv (needed for ``qubx run``). Set to False for release flows
585
+ that only need deps in the uv git cache.
582
586
 
583
587
  Returns:
584
- Path to the created ZIP file, or None on failure
588
+ Tuple of (clone_dir, parsed_strategy_config).
585
589
  """
586
590
  import subprocess
587
- import tempfile
588
591
 
589
- from qubx import QubxLogConfig
590
-
591
- QubxLogConfig.set_log_level("INFO")
592
+ config_path = os.path.abspath(os.path.expanduser(config_file))
593
+ if not is_config_file(config_path):
594
+ raise ValueError(f"Not a valid config file: {config_file}")
592
595
 
593
- try:
594
- config_path = os.path.abspath(os.path.expanduser(config_file))
595
- if not is_config_file(config_path):
596
- raise ValueError(f"Not a valid config file: {config_file}")
596
+ stg_config = load_strategy_config_from_yaml(config_path, resolve_env=False)
597
+ if not stg_config.release or not stg_config.release.source:
598
+ raise ValueError("Config must have a release.source section with repo and ref")
597
599
 
598
- # Parse the config to extract release.source
599
- stg_config = load_strategy_config_from_yaml(config_path, resolve_env=False)
600
- if not stg_config.release or not stg_config.release.source:
601
- raise ValueError("Config must have a release.source section with repo and ref")
600
+ source = stg_config.release.source
601
+ repo_url = f"https://github.com/{source.repo}.git"
602
+ ref = source.ref
602
603
 
603
- source = stg_config.release.source
604
- repo_url = f"https://github.com/{source.repo}.git"
605
- ref = source.ref
604
+ # Cache key: <owner>_<name>_<ref>, with ref sanitized for the filesystem.
605
+ owner, name = source.repo.split("/", 1)
606
+ safe_ref = re.sub(r"[^A-Za-z0-9._-]", "_", ref)
607
+ cache_dir = Path(cache_root).expanduser().resolve() / f"{owner}_{name}_{safe_ref}"
606
608
 
607
- logger.info(f"Cloning {source.repo} at ref '{ref}' ...")
608
- clone_dir = tempfile.mkdtemp(prefix="qubx-release-")
609
+ if refresh and cache_dir.exists():
610
+ logger.info(f"Refreshing cached checkout at <cyan>{cache_dir}</cyan>")
611
+ shutil.rmtree(cache_dir)
609
612
 
613
+ if not cache_dir.exists():
614
+ cache_dir.parent.mkdir(parents=True, exist_ok=True)
615
+ logger.info(f"Cloning <cyan>{source.repo}</cyan> at ref '<yellow>{ref}</yellow>' to {cache_dir}")
610
616
  try:
611
617
  subprocess.run(
612
- ["git", "clone", "--depth", "1", "--branch", ref, repo_url, clone_dir],
618
+ ["git", "clone", "--depth", "1", "--branch", ref, repo_url, str(cache_dir)],
613
619
  check=True,
614
620
  capture_output=True,
615
621
  text=True,
616
622
  )
617
623
  except subprocess.CalledProcessError:
618
- # --branch doesn't work with commit SHAs, fall back to full clone + checkout
624
+ # --branch doesn't work with commit SHAs; fall back to full clone + checkout.
619
625
  logger.info(f"Shallow clone failed for ref '{ref}', trying full clone...")
620
- shutil.rmtree(clone_dir, ignore_errors=True)
621
- os.makedirs(clone_dir)
626
+ if cache_dir.exists():
627
+ shutil.rmtree(cache_dir)
628
+ cache_dir.mkdir(parents=True, exist_ok=True)
622
629
  subprocess.run(
623
- ["git", "clone", repo_url, clone_dir],
630
+ ["git", "clone", repo_url, str(cache_dir)],
624
631
  check=True,
625
632
  capture_output=True,
626
633
  text=True,
627
634
  )
628
635
  subprocess.run(
629
636
  ["git", "checkout", ref],
630
- cwd=clone_dir,
637
+ cwd=str(cache_dir),
631
638
  check=True,
632
639
  capture_output=True,
633
640
  text=True,
634
641
  )
642
+ else:
643
+ logger.info(f"Reusing cached checkout at <cyan>{cache_dir}</cyan>")
635
644
 
636
- # Resolve and fetch dependencies in the cloned project.
637
- # uv sync populates the git checkout cache (needed for _find_uv_git_checkout),
638
- # while uv lock alone does not. Use --all-extras to include optional deps
639
- # (plugins like qubx-lighter, xmetals are typically in optional extras).
640
- logger.info("Resolving and fetching dependencies in cloned project...")
641
- result = subprocess.run(
642
- ["uv", "sync", "--no-install-project", "--all-extras"],
643
- cwd=clone_dir,
644
- capture_output=True,
645
- text=True,
645
+ # Resolve and install dependencies in the cloned project.
646
+ # --all-extras pulls in optional extras (qubx-lighter, xmetals plugins).
647
+ # Skipping --no-install-project installs the strategy package itself into
648
+ # the cloned venv — required for `qubx run` to import the strategy.
649
+ sync_args = ["uv", "sync", "--all-extras"]
650
+ if not install_project:
651
+ sync_args.insert(2, "--no-install-project")
652
+ logger.info(f"Running <cyan>{' '.join(sync_args)}</cyan> in {cache_dir}")
653
+ result = subprocess.run(sync_args, cwd=str(cache_dir), capture_output=True, text=True)
654
+ if result.returncode != 0:
655
+ logger.opt(colors=False).error(f"uv sync failed:\n{result.stderr}")
656
+ raise subprocess.CalledProcessError(result.returncode, result.args, output=result.stdout, stderr=result.stderr)
657
+
658
+ return cache_dir, stg_config
659
+
660
+
661
+ def release_from_source(
662
+ config_file: str,
663
+ output_dir: str,
664
+ tag: str | None = None,
665
+ refresh: bool = False,
666
+ ) -> str | None:
667
+ """
668
+ Build a release by cloning the source repo specified in ``config.release.source``.
669
+
670
+ Thin wrapper around ``prepare_source_checkout`` + the standard release flow.
671
+ Used by ``qubx release --from-sources``.
672
+
673
+ Args:
674
+ config_file: Path to the strategy config YAML (must have a release.source section)
675
+ output_dir: Directory to write the release ZIP
676
+ tag: Optional tag suffix for the release name
677
+ refresh: Force re-clone of the source cache
678
+
679
+ Returns:
680
+ Path to the created ZIP file, or None on failure
681
+ """
682
+ from qubx import QubxLogConfig
683
+
684
+ QubxLogConfig.set_log_level("INFO")
685
+
686
+ try:
687
+ config_path = os.path.abspath(os.path.expanduser(config_file))
688
+ # release flow reads source files directly from the clone; no need to install the project.
689
+ clone_dir, stg_config = prepare_source_checkout(
690
+ config_file=config_path,
691
+ refresh=refresh,
692
+ install_project=False,
646
693
  )
647
- if result.returncode != 0:
648
- logger.error(f"uv sync failed:\n{result.stderr}")
649
- raise subprocess.CalledProcessError(result.returncode, result.args)
694
+ assert stg_config.release and stg_config.release.source # verified in helper
695
+ ref = stg_config.release.source.ref
650
696
 
651
- # Copy the config into the clone root so release can find it
697
+ # Copy the config into the clone root so the release flow can find it.
652
698
  config_basename = os.path.basename(config_path)
653
- cloned_config = os.path.join(clone_dir, config_basename)
699
+ cloned_config = os.path.join(str(clone_dir), config_basename)
654
700
  shutil.copy2(config_path, cloned_config)
655
701
 
656
- # Run the standard release flow from the cloned project
657
- logger.info(f"Running release from {clone_dir} ...")
702
+ logger.info(f"Running release from <cyan>{clone_dir}</cyan>")
658
703
  output_dir = os.path.abspath(output_dir)
659
- stg_info = load_strategy_from_config(Path(cloned_config), clone_dir)
660
- pyproject_root = clone_dir
704
+ stg_info = load_strategy_from_config(Path(cloned_config), str(clone_dir))
661
705
 
662
706
  git_info = ReleaseInfo(
663
707
  tag=generate_tag(stg_info.name, tag),
@@ -670,12 +714,11 @@ def release_from_source(
670
714
  create_released_pack(
671
715
  stg_info=stg_info,
672
716
  git_info=git_info,
673
- pyproject_root=pyproject_root,
717
+ pyproject_root=str(clone_dir),
674
718
  output_dir=output_dir,
675
719
  config_file=cloned_config,
676
720
  )
677
721
 
678
- # Find the created ZIP
679
722
  zip_path = os.path.join(output_dir, f"{git_info.tag}.zip")
680
723
  if os.path.exists(zip_path):
681
724
  logger.info(f"Release created: {zip_path}")
@@ -687,10 +730,6 @@ def release_from_source(
687
730
  except Exception as e:
688
731
  logger.opt(colors=False).error(f"Error in release_from_source: {e}")
689
732
  return None
690
- finally:
691
- # Clean up temp clone
692
- if "clone_dir" in locals():
693
- shutil.rmtree(clone_dir, ignore_errors=True)
694
733
 
695
734
 
696
735
  def _find_source_root(pyproject_root: str, project_name: str) -> str | None:
File without changes
File without changes
File without changes
File without changes
File without changes