Qubx 0.7.25__tar.gz → 0.7.35__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 (270) hide show
  1. {qubx-0.7.25 → qubx-0.7.35}/PKG-INFO +34 -7
  2. {qubx-0.7.25 → qubx-0.7.35}/README.md +29 -1
  3. {qubx-0.7.25 → qubx-0.7.35}/pyproject.toml +6 -5
  4. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/broker.py +52 -12
  5. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/simulator.py +3 -0
  6. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/commands.py +20 -9
  7. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/release.py +9 -10
  8. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/tui.py +0 -3
  9. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/broker.py +176 -37
  10. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/data.py +2 -5
  11. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/ohlc.py +25 -12
  12. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/reader.py +7 -2
  13. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/utils.py +15 -0
  14. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/account.py +159 -109
  15. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/broker.py +108 -174
  16. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/client.py +30 -25
  17. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/constants.py +9 -21
  18. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/data.py +14 -14
  19. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/extensions.py +7 -2
  20. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/factory.py +1 -0
  21. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/handlers/orderbook.py +48 -35
  22. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/nonce.py +9 -1
  23. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/parsers.py +2 -5
  24. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/rate_limits.py +1 -1
  25. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/reader.py +7 -7
  26. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/websocket.py +17 -4
  27. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/account.py +65 -7
  28. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/context.py +41 -9
  29. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/errors.py +3 -1
  30. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/interfaces.py +185 -37
  31. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/metrics.py +6 -1
  32. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/market.py +14 -11
  33. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/processing.py +36 -5
  34. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/subscription.py +1 -1
  35. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/trading.py +101 -51
  36. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/utils.pyi +2 -1
  37. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/readers.py +22 -4
  38. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/transformers.py +39 -6
  39. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/formatters/base.py +6 -5
  40. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/formatters/incremental.py +24 -20
  41. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/formatters/target_position.py +7 -2
  42. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/gathering/simplest.py +21 -7
  43. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/pandaz/__init__.py +18 -16
  44. {qubx-0.7.25/src/qubx/math → qubx-0.7.35/src/qubx/pandaz}/stats.py +41 -16
  45. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/pandaz/ta.py +2 -2
  46. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restarts/state_resolvers.py +1 -1
  47. qubx-0.7.35/src/qubx/state/__init__.py +14 -0
  48. qubx-0.7.35/src/qubx/state/dummy.py +35 -0
  49. qubx-0.7.35/src/qubx/state/redis.py +170 -0
  50. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/ta/indicators.pxd +14 -0
  51. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/ta/indicators.pyx +102 -1
  52. qubx-0.7.35/src/qubx/templates/__init__.py +5 -0
  53. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/base.py +41 -42
  54. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/riskctrl.py +2 -2
  55. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/misc.py +9 -4
  56. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/_jupyter_runner.pyt +2 -2
  57. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/configs.py +63 -1
  58. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/factory.py +60 -25
  59. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/runner.py +23 -14
  60. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/__init__.py +3 -1
  61. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/app.py +5 -1
  62. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/init_code.py +4 -3
  63. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/websocket_manager.py +5 -2
  64. qubx-0.7.25/src/qubx/math/__init__.py +0 -3
  65. qubx-0.7.25/src/qubx/templates/__init__.py +0 -5
  66. {qubx-0.7.25 → qubx-0.7.35}/LICENSE +0 -0
  67. {qubx-0.7.25 → qubx-0.7.35}/build.py +0 -0
  68. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/__init__.py +0 -0
  69. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/_nb_magic.py +0 -0
  70. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/__init__.py +0 -0
  71. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/account.py +0 -0
  72. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/data.py +0 -0
  73. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/management.py +0 -0
  74. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/ome.py +0 -0
  75. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/optimization.py +0 -0
  76. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/runner.py +0 -0
  77. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/sentinels.py +0 -0
  78. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/simulated_data.py +0 -0
  79. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/simulated_exchange.py +0 -0
  80. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/transfers.py +0 -0
  81. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/backtester/utils.py +0 -0
  82. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/__init__.py +0 -0
  83. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/deploy.py +0 -0
  84. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/cli/misc.py +0 -0
  85. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/__init__.py +0 -0
  86. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/account.py +0 -0
  87. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
  88. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
  89. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
  90. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  91. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
  92. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
  93. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
  94. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
  95. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
  96. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
  97. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
  98. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
  99. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
  100. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
  101. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
  102. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
  103. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/factory.py +0 -0
  104. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
  105. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
  106. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
  107. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
  108. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
  109. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
  110. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
  111. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
  112. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
  113. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
  114. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
  115. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
  116. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
  117. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/tardis/data.py +0 -0
  118. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/tardis/utils.py +0 -0
  119. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/__init__.py +0 -0
  120. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/handlers/__init__.py +0 -0
  121. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/handlers/base.py +0 -0
  122. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/handlers/stats.py +0 -0
  123. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/handlers/trades.py +0 -0
  124. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/instruments.py +0 -0
  125. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/connectors/xlighter/utils.py +0 -0
  126. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/__init__.py +0 -0
  127. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/basics.py +0 -0
  128. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/detectors/__init__.py +0 -0
  129. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/detectors/delisting.py +0 -0
  130. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/detectors/stale.py +0 -0
  131. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/exceptions.py +0 -0
  132. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/helpers.py +0 -0
  133. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/initializer.py +0 -0
  134. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/loggers.py +0 -0
  135. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/lookups.py +0 -0
  136. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/__init__.py +0 -0
  137. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/universe.py +0 -0
  138. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/mixins/utils.py +0 -0
  139. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/series.pxd +0 -0
  140. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/series.pyi +0 -0
  141. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/series.pyx +0 -0
  142. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/core/utils.pyx +0 -0
  143. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/__init__.py +0 -0
  144. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/composite.py +0 -0
  145. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/containers.py +0 -0
  146. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/helpers.py +0 -0
  147. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/hft.py +0 -0
  148. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/registry.py +0 -0
  149. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/storage.py +0 -0
  150. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/storages/csv.py +0 -0
  151. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/storages/questdb.py +0 -0
  152. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/storages/utils.py +0 -0
  153. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/data/tardis.py +0 -0
  154. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/__init__.py +0 -0
  155. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/base.py +0 -0
  156. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/composite.py +0 -0
  157. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/csv.py +0 -0
  158. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/indicator.py +0 -0
  159. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/inmemory.py +0 -0
  160. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/prometheus.py +0 -0
  161. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/emitters/questdb.py +0 -0
  162. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/__init__.py +0 -0
  163. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/composite.py +0 -0
  164. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/formatters/__init__.py +0 -0
  165. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/formatters/slack.py +0 -0
  166. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/redis_streams.py +0 -0
  167. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/exporters/slack.py +0 -0
  168. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/health/__init__.py +0 -0
  169. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/health/base.py +0 -0
  170. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/health/dummy.py +0 -0
  171. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/loggers/__init__.py +0 -0
  172. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/loggers/csv.py +0 -0
  173. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/loggers/factory.py +0 -0
  174. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/loggers/inmemory.py +0 -0
  175. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/loggers/mongo.py +0 -0
  176. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/notifications/__init__.py +0 -0
  177. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/notifications/composite.py +0 -0
  178. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/notifications/slack.py +0 -0
  179. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/notifications/throttler.py +0 -0
  180. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/pandaz/utils.py +0 -0
  181. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/_build.py +0 -0
  182. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/crypto-fees.ini +0 -0
  183. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
  184. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
  185. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
  186. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
  187. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
  188. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
  189. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
  190. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
  191. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
  192. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
  193. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
  194. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restarts/__init__.py +0 -0
  195. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restarts/time_finders.py +0 -0
  196. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/__init__.py +0 -0
  197. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/balance.py +0 -0
  198. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/factory.py +0 -0
  199. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/interfaces.py +0 -0
  200. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/position.py +0 -0
  201. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/signal.py +0 -0
  202. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/state.py +0 -0
  203. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/restorers/utils.py +0 -0
  204. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/ta/__init__.py +0 -0
  205. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/ta/indicators.pyi +0 -0
  206. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/accounts.toml.j2 +0 -0
  207. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/config.yml.j2 +0 -0
  208. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/jlive.sh.j2 +0 -0
  209. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
  210. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
  211. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  212. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  213. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/project/template.yml +0 -0
  214. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/__init__.py.j2 +0 -0
  215. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
  216. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/config.yml.j2 +0 -0
  217. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
  218. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
  219. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/strategy.py.j2 +0 -0
  220. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/templates/simple/template.yml +0 -0
  221. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/__init__.py +0 -0
  222. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/advanced.py +0 -0
  223. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/composite.py +0 -0
  224. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/rebalancers.py +0 -0
  225. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/trackers/sizers.py +0 -0
  226. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/__init__.py +0 -0
  227. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/_pyxreloader.py +0 -0
  228. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/charting/lookinglass.py +0 -0
  229. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  230. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/charting/orderbook.py +0 -0
  231. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/collections.py +0 -0
  232. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/hft/__init__.py +0 -0
  233. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/hft/numba_utils.py +0 -0
  234. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/hft/orderbook.pyi +0 -0
  235. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/hft/orderbook.pyx +0 -0
  236. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/marketdata/binance.py +0 -0
  237. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/marketdata/ccxt.py +0 -0
  238. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/marketdata/dukas.py +0 -0
  239. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/nonce.py +0 -0
  240. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/ntp.py +0 -0
  241. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/numbers_utils.py +0 -0
  242. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/orderbook.py +0 -0
  243. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/__init__.py +0 -0
  244. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/dashboard.py +0 -0
  245. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/data.py +0 -0
  246. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/interfaces.py +0 -0
  247. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  248. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  249. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/questdb.py +0 -0
  250. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/rate_limiter.py +0 -0
  251. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/ringbuffer.pxd +0 -0
  252. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/ringbuffer.pyi +0 -0
  253. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/ringbuffer.pyx +0 -0
  254. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/__init__.py +0 -0
  255. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/accounts.py +0 -0
  256. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/kernel_service.py +0 -0
  257. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/handlers.py +0 -0
  258. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/kernel.py +0 -0
  259. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/styles.tcss +0 -0
  260. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
  261. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
  262. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
  263. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
  264. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
  265. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
  266. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
  267. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/slack.py +0 -0
  268. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/throttler.py +0 -0
  269. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/time.py +0 -0
  270. {qubx-0.7.25 → qubx-0.7.35}/src/qubx/utils/version.py +0 -0
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Qubx
3
- Version: 0.7.25
3
+ Version: 0.7.35
4
4
  Summary: Qubx - Quantitative Trading Framework
5
5
  License-File: LICENSE
6
6
  Author: Dmitry Marienko
7
7
  Author-email: dmitry.marienko@xlydian.com
8
- Requires-Python: >=3.10,<4.0
8
+ Requires-Python: >=3.11,<4.0
9
9
  Classifier: Programming Language :: Python :: 3
10
- Classifier: Programming Language :: Python :: 3.10
11
10
  Classifier: Programming Language :: Python :: 3.11
12
11
  Classifier: Programming Language :: Python :: 3.12
13
12
  Classifier: Programming Language :: Python :: 3.13
@@ -43,15 +42,15 @@ Requires-Dist: python-binance (>=1.0.19,<2.0.0)
43
42
  Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
44
43
  Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
45
44
  Requires-Dist: qubx-bitfinex-api (>=3.0.7,<4.0.0)
46
- Requires-Dist: qubx-lighter-api (>=0.1.4,<0.2.0)
45
+ Requires-Dist: qubx-lighter-api (>=0.1.5,<0.2.0)
47
46
  Requires-Dist: questdb (>=2.0.3,<3.0.0)
48
47
  Requires-Dist: redis (>=5.2.1,<6.0.0)
49
48
  Requires-Dist: rich (>=13.9.4,<14.0.0)
50
- Requires-Dist: scikit-learn (>=1.4.2,<2.0.0)
49
+ Requires-Dist: scikit-learn (>=1.8.0)
51
50
  Requires-Dist: scipy (>=1.12.0,<2.0.0)
52
51
  Requires-Dist: sortedcontainers (>=2.4.0,<3.0.0)
53
52
  Requires-Dist: stackprinter (>=0.2.10,<0.3.0)
54
- Requires-Dist: statsmodels (>=0.14.2,<0.15.0)
53
+ Requires-Dist: statsmodels (>=0.14.6,<0.15.0)
55
54
  Requires-Dist: tabulate (>=0.9.0,<0.10.0)
56
55
  Requires-Dist: textual-autocomplete (>=4.0.0,<5.0.0)
57
56
  Requires-Dist: textual-serve (>=1.0.0,<2.0.0)
@@ -70,20 +69,33 @@ Description-Content-Type: text/markdown
70
69
  ```
71
70
  ⠀⠀⡰⡖⠒⠒⢒⢦⠀⠀
72
71
  ⠀⢠⠃⠈⢆⣀⣎⣀⣱⡀ QUBX | Quantitative Backtesting Environment
73
- ⠀⢳⠒⠒⡞⠚⡄⠀⡰⠁ (c) 2024, by Dmytro Mariienko
72
+ ⠀⢳⠒⠒⡞⠚⡄⠀⡰⠁ (c) 2026, by xLydian
74
73
  ⠀⠀⠱⣜⣀⣀⣈⣦⠃⠀⠀⠀
75
74
  ```
76
75
 
77
76
  Qubx is a next-generation quantitative trading framework designed for efficient backtesting and live trading. Built with Python, it offers a robust environment for developing, testing, and deploying trading strategies.
78
77
 
78
+ **Qubx is under active development.** We are continuously improving the framework and will update our documentation in the coming days/weeks. This will include comprehensive end-to-end examples for running simulations and live trading.
79
+
80
+ ### Supported Data Types
81
+
82
+ Qubx supports a wide range of market data:
83
+ - OHLC (candlestick data)
84
+ - L2 Orderbook
85
+ - Liquidations
86
+ - Funding rates
87
+ - And more...
88
+
79
89
  ## Quick Start
80
90
 
81
91
  ### 1. Install Dependencies
92
+
82
93
  ```bash
83
94
  poetry install
84
95
  ```
85
96
 
86
97
  ### 2. Create a Strategy
98
+
87
99
  ```bash
88
100
  # Create a simple strategy template (default)
89
101
  poetry run qubx init
@@ -93,6 +105,7 @@ poetry run qubx init --name my_strategy --symbols BTCUSDT,ETHUSDT
93
105
  ```
94
106
 
95
107
  ### 3. Run Your Strategy
108
+
96
109
  ```bash
97
110
  cd my_strategy
98
111
 
@@ -104,6 +117,7 @@ poetry run qubx run config.yml --paper
104
117
  ```
105
118
 
106
119
  ### Available Templates
120
+
107
121
  ```bash
108
122
  # List available strategy templates
109
123
  poetry run qubx init --list-templates
@@ -113,6 +127,7 @@ poetry run qubx init --template project --name my_project
113
127
  ```
114
128
 
115
129
  ### Strategy Development Workflow
130
+
116
131
  1. **Initialize**: `poetry run qubx init` - Create strategy from template
117
132
  2. **Develop**: Edit `strategy.py` to implement your trading logic
118
133
  3. **Test**: `poetry run qubx run config.yml --paper` - Run in paper mode
@@ -193,6 +208,18 @@ just test
193
208
  - Build package: `just build`
194
209
  - Run verbose tests: `just test-verbose`
195
210
 
211
+ ## In Production
212
+
213
+ Qubx powers the [AllegedAlpha](https://app.lighter.xyz/public-pools/281474976625478) public pool on Lighter. Public pools allow users to deposit funds from their blockchain wallet into a smart contract. The pool operator manages the trading strategy, and a performance fee is taken from profits (X: [@allegedalpha](https://x.com/allegedalpha)).
214
+
215
+ ## About xLydian
216
+
217
+ Qubx is developed by [xLydian](https://xlydian.com/).
218
+
219
+ - Website: [xlydian.com](https://xlydian.com/)
220
+ - X: [@xLydian_xyz](https://x.com/xLydian_xyz)
221
+ - Contact: [info@xlydian.com](mailto:info@xlydian.com)
222
+
196
223
  ## Contributing
197
224
 
198
225
  Contributions are welcome! Please feel free to submit a Pull Request.
@@ -5,20 +5,33 @@
5
5
  ```
6
6
  ⠀⠀⡰⡖⠒⠒⢒⢦⠀⠀
7
7
  ⠀⢠⠃⠈⢆⣀⣎⣀⣱⡀ QUBX | Quantitative Backtesting Environment
8
- ⠀⢳⠒⠒⡞⠚⡄⠀⡰⠁ (c) 2024, by Dmytro Mariienko
8
+ ⠀⢳⠒⠒⡞⠚⡄⠀⡰⠁ (c) 2026, by xLydian
9
9
  ⠀⠀⠱⣜⣀⣀⣈⣦⠃⠀⠀⠀
10
10
  ```
11
11
 
12
12
  Qubx is a next-generation quantitative trading framework designed for efficient backtesting and live trading. Built with Python, it offers a robust environment for developing, testing, and deploying trading strategies.
13
13
 
14
+ **Qubx is under active development.** We are continuously improving the framework and will update our documentation in the coming days/weeks. This will include comprehensive end-to-end examples for running simulations and live trading.
15
+
16
+ ### Supported Data Types
17
+
18
+ Qubx supports a wide range of market data:
19
+ - OHLC (candlestick data)
20
+ - L2 Orderbook
21
+ - Liquidations
22
+ - Funding rates
23
+ - And more...
24
+
14
25
  ## Quick Start
15
26
 
16
27
  ### 1. Install Dependencies
28
+
17
29
  ```bash
18
30
  poetry install
19
31
  ```
20
32
 
21
33
  ### 2. Create a Strategy
34
+
22
35
  ```bash
23
36
  # Create a simple strategy template (default)
24
37
  poetry run qubx init
@@ -28,6 +41,7 @@ poetry run qubx init --name my_strategy --symbols BTCUSDT,ETHUSDT
28
41
  ```
29
42
 
30
43
  ### 3. Run Your Strategy
44
+
31
45
  ```bash
32
46
  cd my_strategy
33
47
 
@@ -39,6 +53,7 @@ poetry run qubx run config.yml --paper
39
53
  ```
40
54
 
41
55
  ### Available Templates
56
+
42
57
  ```bash
43
58
  # List available strategy templates
44
59
  poetry run qubx init --list-templates
@@ -48,6 +63,7 @@ poetry run qubx init --template project --name my_project
48
63
  ```
49
64
 
50
65
  ### Strategy Development Workflow
66
+
51
67
  1. **Initialize**: `poetry run qubx init` - Create strategy from template
52
68
  2. **Develop**: Edit `strategy.py` to implement your trading logic
53
69
  3. **Test**: `poetry run qubx run config.yml --paper` - Run in paper mode
@@ -128,6 +144,18 @@ just test
128
144
  - Build package: `just build`
129
145
  - Run verbose tests: `just test-verbose`
130
146
 
147
+ ## In Production
148
+
149
+ Qubx powers the [AllegedAlpha](https://app.lighter.xyz/public-pools/281474976625478) public pool on Lighter. Public pools allow users to deposit funds from their blockchain wallet into a smart contract. The pool operator manages the trading strategy, and a performance fee is taken from profits (X: [@allegedalpha](https://x.com/allegedalpha)).
150
+
151
+ ## About xLydian
152
+
153
+ Qubx is developed by [xLydian](https://xlydian.com/).
154
+
155
+ - Website: [xlydian.com](https://xlydian.com/)
156
+ - X: [@xLydian_xyz](https://x.com/xLydian_xyz)
157
+ - Contact: [info@xlydian.com](mailto:info@xlydian.com)
158
+
131
159
  ## Contributing
132
160
 
133
161
  Contributions are welcome! Please feel free to submit a Pull Request.
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "Qubx"
7
- version = "0.7.25"
7
+ version = "0.7.35"
8
8
  description = "Qubx - Quantitative Trading Framework"
9
9
  authors = [ "Dmitry Marienko <dmitry.marienko@xlydian.com>", "Yuriy Arabskyy <yuriy.arabskyy@xlydian.com>",]
10
10
  readme = "README.md"
@@ -32,13 +32,13 @@ generate-setup-file = false
32
32
  qubx = "qubx.cli.commands:main"
33
33
 
34
34
  [tool.poetry.dependencies]
35
- python = ">=3.10,<4.0"
35
+ python = ">=3.11,<4.0"
36
36
  numpy = "^1.26.3"
37
37
  pandas = "^2.2.2"
38
38
  pyarrow = "^15.0.0"
39
39
  scipy = "^1.12.0"
40
- scikit-learn = "^1.4.2"
41
- statsmodels = "^0.14.2"
40
+ scikit-learn = ">=1.8.0"
41
+ statsmodels = "^0.14.6"
42
42
  numba = "^0.59.1"
43
43
  sortedcontainers = "^2.4.0"
44
44
  ntplib = "^0.4.0"
@@ -78,7 +78,7 @@ textual-autocomplete = "^4.0.0"
78
78
  textual-serve = "^1.0.0"
79
79
  rich = "^13.9.4"
80
80
  jinja2 = "^3.1.0"
81
- qubx-lighter-api = "^0.1.4"
81
+ qubx-lighter-api = "^0.1.5"
82
82
  uvloop = "^0.22.1"
83
83
 
84
84
  [tool.ruff.lint]
@@ -120,6 +120,7 @@ jinja2 = "3.1.5"
120
120
  mike = "2.1.3"
121
121
  mkdocs-jupyter = "0.25.1"
122
122
  debugpy = "^1.8.12"
123
+ py-spy = "^0.4.1"
123
124
 
124
125
  [tool.poetry.group.test.dependencies]
125
126
  pytest-asyncio = "^0.24.0"
@@ -51,29 +51,51 @@ class SimulatedBroker(IBroker):
51
51
  )
52
52
  return report.order
53
53
 
54
- def send_order_async(self, request: OrderRequest) -> None:
54
+ def send_order_async(self, request: OrderRequest) -> str | None:
55
55
  """Submit order asynchronously (same as sync in simulation)."""
56
56
  self.send_order(request)
57
+ return request.client_id
57
58
 
58
- def cancel_order(self, order_id: str) -> bool:
59
+ def _validate_order_ids(self, order_id: str | None, client_order_id: str | None) -> None:
60
+ if (order_id is None and client_order_id is None) or (order_id is not None and client_order_id is not None):
61
+ raise ValueError("Exactly one of order_id or client_order_id must be provided")
62
+
63
+ def _resolve_order_id(self, order_id: str | None, client_order_id: str | None) -> str | None:
64
+ if order_id is not None:
65
+ return order_id
66
+ if client_order_id is not None:
67
+ order = self._account.find_order_by_client_id(client_order_id)
68
+ return order.id if order is not None else None
69
+ return None
70
+
71
+ def cancel_order(self, order_id: str | None = None, client_order_id: str | None = None) -> bool:
59
72
  """Cancel an order synchronously and return success status."""
73
+ self._validate_order_ids(order_id, client_order_id)
74
+ resolved_id = self._resolve_order_id(order_id, client_order_id)
75
+ if resolved_id is None:
76
+ return False
60
77
  try:
61
- self._send_execution_report(order_update := self._exchange.cancel_order(order_id))
78
+ self._send_execution_report(order_update := self._exchange.cancel_order(resolved_id))
62
79
  return order_update is not None
63
80
  except OrderNotFound:
64
81
  # Order was already cancelled or doesn't exist
65
- logger.debug(f"Order {order_id} not found")
82
+ logger.debug(f"Order {resolved_id} not found")
66
83
  return False
67
84
 
68
- def cancel_order_async(self, order_id: str) -> None:
85
+ def cancel_order_async(self, order_id: str | None = None, client_order_id: str | None = None) -> None:
69
86
  """Cancel an order asynchronously (fire-and-forget)."""
70
- # For simulation, async is same as sync since it's fast
71
- self.cancel_order(order_id)
87
+ self._validate_order_ids(order_id, client_order_id)
88
+ resolved_id = self._resolve_order_id(order_id, client_order_id)
89
+ if resolved_id is None:
90
+ return
91
+ self.cancel_order(order_id=resolved_id)
72
92
 
73
93
  def cancel_orders(self, instrument: Instrument) -> None:
74
94
  raise NotImplementedError("Not implemented yet")
75
95
 
76
- def update_order(self, order_id: str, price: float, amount: float) -> Order:
96
+ def update_order(
97
+ self, price: float, amount: float, order_id: str | None = None, client_order_id: str | None = None
98
+ ) -> Order:
77
99
  """Update an existing limit order using cancel+recreate strategy.
78
100
 
79
101
  Args:
@@ -88,19 +110,25 @@ class SimulatedBroker(IBroker):
88
110
  OrderNotFound: If the order is not found
89
111
  BadRequest: If the order is not a limit order
90
112
  """
113
+ self._validate_order_ids(order_id, client_order_id)
114
+ resolved_id = self._resolve_order_id(order_id, client_order_id)
115
+ if resolved_id is None:
116
+ raise OrderNotFound(f"Order {order_id or client_order_id} not found")
117
+
91
118
  # Get the existing order from account
92
119
  active_orders = self._account.get_orders()
93
- existing_order = active_orders.get(order_id)
120
+ existing_order = active_orders.get(resolved_id)
94
121
  if not existing_order:
95
- raise OrderNotFound(f"Order {order_id} not found")
122
+ raise OrderNotFound(f"Order {resolved_id} not found")
96
123
 
97
124
  # Validate that it's a limit order
98
125
  if existing_order.type != "LIMIT":
99
126
  raise BadRequest(
100
- f"Order {order_id} is not a limit order (type: {existing_order.type}). Only limit orders can be updated."
127
+ f"Order {resolved_id} is not a limit order (type: {existing_order.type}). "
128
+ "Only limit orders can be updated."
101
129
  )
102
130
 
103
- self.cancel_order(order_id)
131
+ self.cancel_order(order_id=resolved_id)
104
132
 
105
133
  request = OrderRequest(
106
134
  instrument=existing_order.instrument,
@@ -109,6 +137,7 @@ class SimulatedBroker(IBroker):
109
137
  order_type="LIMIT",
110
138
  side=existing_order.side,
111
139
  time_in_force=existing_order.time_in_force or "gtc",
140
+ client_id=existing_order.client_id,
112
141
  options={},
113
142
  )
114
143
 
@@ -116,6 +145,17 @@ class SimulatedBroker(IBroker):
116
145
 
117
146
  return updated_order
118
147
 
148
+ def update_order_async(
149
+ self, price: float, amount: float, order_id: str | None = None, client_order_id: str | None = None
150
+ ) -> str | None:
151
+ """Update order asynchronously (same as sync in simulation)."""
152
+ self._validate_order_ids(order_id, client_order_id)
153
+ resolved_id = self._resolve_order_id(order_id, client_order_id)
154
+ if resolved_id is None:
155
+ return None
156
+ updated_order = self.update_order(order_id=resolved_id, price=price, amount=amount)
157
+ return updated_order.client_id
158
+
119
159
  def _send_execution_report(self, report: SimulatedExecutionReport | None):
120
160
  if report is None:
121
161
  return
@@ -335,5 +335,8 @@ def _run_setup(
335
335
  transfers_log=transfers_log,
336
336
  )
337
337
  except Exception as e:
338
+ import traceback
339
+
338
340
  logger.error(f"Simulation setup {setup_id} failed with error: {e}")
341
+ logger.opt(colors=False).error(traceback.format_exc())
339
342
  return None
@@ -1,4 +1,5 @@
1
1
  import os
2
+ from importlib.metadata import version
2
3
  from pathlib import Path
3
4
 
4
5
  import click
@@ -8,6 +9,7 @@ from qubx import QubxLogConfig, logger
8
9
 
9
10
 
10
11
  @click.group()
12
+ @click.version_option(version=version("qubx"), prog_name="qubx")
11
13
  @click.option(
12
14
  "--debug",
13
15
  "-d",
@@ -101,6 +103,9 @@ def main(debug: bool, debug_port: int, log_level: str):
101
103
  "--restore", "-r", is_flag=True, default=False, help="Restore strategy state from previous run.", show_default=True
102
104
  )
103
105
  @click.option("--no-color", is_flag=True, default=False, help="Disable colored logging output.", show_default=True)
106
+ @click.option(
107
+ "--dev", is_flag=True, default=False, help="Enable dev mode (adds ~/projects to path).", show_default=True
108
+ )
104
109
  def run(
105
110
  config_file: Path,
106
111
  account_file: Path | None,
@@ -115,6 +120,7 @@ def run(
115
120
  connect: Path | None,
116
121
  restore: bool,
117
122
  no_color: bool,
123
+ dev: bool,
118
124
  ):
119
125
  """
120
126
  Starts the strategy with the given configuration file. If paper mode is enabled, account is not required.
@@ -133,16 +139,16 @@ def run(
133
139
  click.echo("Error: --jupyter and --textual cannot be used together.", err=True)
134
140
  raise click.Abort()
135
141
 
142
+ if dev:
143
+ add_project_to_system_path() # Adds ~/projects in dev mode
144
+ add_project_to_system_path(config_file.parent)
145
+
136
146
  # Handle --kernel-only mode
137
147
  if kernel_only:
138
148
  import asyncio
139
149
 
140
150
  from qubx.utils.runner.kernel_service import KernelService
141
151
 
142
- add_project_to_system_path()
143
- add_project_to_system_path(str(config_file.parent.parent))
144
- add_project_to_system_path(str(config_file.parent))
145
-
146
152
  click.echo("Starting persistent kernel...")
147
153
  connection_file = asyncio.run(KernelService.start(config_file, account_file, paper, restore))
148
154
  click.echo(click.style("✓ Kernel started successfully!", fg="green", bold=True))
@@ -167,15 +173,20 @@ def run(
167
173
  click.echo("Kernel stopped.")
168
174
  return
169
175
 
170
- add_project_to_system_path()
171
- add_project_to_system_path(str(config_file.parent.parent))
172
- add_project_to_system_path(str(config_file.parent))
173
-
174
176
  if jupyter:
175
177
  run_strategy_yaml_in_jupyter(config_file, account_file, paper, restore)
176
178
  elif textual:
177
179
  run_strategy_yaml_in_textual(
178
- config_file, account_file, paper, restore, textual_dev, textual_web, textual_port, textual_host, connect
180
+ config_file,
181
+ account_file,
182
+ paper,
183
+ restore,
184
+ textual_dev,
185
+ textual_web,
186
+ textual_port,
187
+ textual_host,
188
+ connect,
189
+ dev,
179
190
  )
180
191
  else:
181
192
  logo()
@@ -463,6 +463,7 @@ def release_strategy(
463
463
  git_info=_git_info,
464
464
  pyproject_root=pyproject_root,
465
465
  output_dir=output_dir,
466
+ config_file=config_file,
466
467
  )
467
468
  except ValueError as e:
468
469
  logger.error(f"<r>{str(e)}</r>")
@@ -475,6 +476,7 @@ def create_released_pack(
475
476
  git_info: ReleaseInfo,
476
477
  pyproject_root: str,
477
478
  output_dir: str,
479
+ config_file: str,
478
480
  ):
479
481
  """
480
482
  Create a release package for a strategy.
@@ -484,7 +486,7 @@ def create_released_pack(
484
486
  git_info: Git release information
485
487
  pyproject_root: Path to the pyproject.toml root directory
486
488
  output_dir: Output directory for the release package
487
- strategy_config: Strategy configuration
489
+ config_file: Path to the original config file to copy
488
490
  """
489
491
  logger.info(f"Creating release pack for {git_info.tag} ...")
490
492
 
@@ -497,8 +499,8 @@ def create_released_pack(
497
499
  _copy_strategy_file(sc.path, pyproject_root, release_dir)
498
500
  _copy_dependencies(sc.path, pyproject_root, release_dir)
499
501
 
500
- # Save configuration
501
- _save_strategy_config(stg_name, stg_info.config, release_dir)
502
+ # Save configuration (copy original file to preserve comments and structure)
503
+ _save_strategy_config(config_file, release_dir)
502
504
 
503
505
  # Create metadata
504
506
  _create_metadata(stg_name, git_info, release_dir)
@@ -512,14 +514,11 @@ def create_released_pack(
512
514
  logger.info(f"Created release pack: {os.path.join(output_dir, git_info.tag)}.zip")
513
515
 
514
516
 
515
- def _save_strategy_config(stg_name: str, strategy_config: StrategyConfig, release_dir: str) -> None:
516
- """Save the strategy configuration to the release directory."""
517
+ def _save_strategy_config(config_file: str, release_dir: str) -> None:
518
+ """Copy the original strategy configuration file to preserve comments and structure."""
517
519
  config_path = os.path.join(release_dir, "config.yml")
518
- with open(config_path, "wt") as fs:
519
- # Convert to dict and save directly (not under 'config' key)
520
- config_dict = strategy_config.model_dump()
521
- yaml.safe_dump(config_dict, fs, sort_keys=False, indent=2)
522
- logger.debug(f"Saved strategy config to {config_path}")
520
+ shutil.copy2(config_file, config_path)
521
+ logger.debug(f"Copied strategy config to {config_path}")
523
522
 
524
523
 
525
524
  def _copy_strategy_file(strategy_path: str, pyproject_root: str, release_dir: str) -> None:
@@ -1,6 +1,3 @@
1
- import webbrowser
2
- from pathlib import Path
3
- from tempfile import NamedTemporaryFile
4
1
  from typing import Any
5
2
 
6
3
  import pandas as pd