Qubx 0.6.91__tar.gz → 0.6.94__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.

Potentially problematic release.


This version of Qubx might be problematic. Click here for more details.

Files changed (267) hide show
  1. {qubx-0.6.91 → qubx-0.6.94}/PKG-INFO +5 -2
  2. {qubx-0.6.91 → qubx-0.6.94}/pyproject.toml +9 -2
  3. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/management.py +2 -1
  4. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/simulator.py +15 -1
  5. qubx-0.6.94/src/qubx/backtester/transfers.py +146 -0
  6. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/cli/commands.py +117 -2
  7. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/factory.py +15 -20
  8. qubx-0.6.94/src/qubx/connectors/xlighter/__init__.py +83 -0
  9. qubx-0.6.94/src/qubx/connectors/xlighter/account.py +421 -0
  10. qubx-0.6.94/src/qubx/connectors/xlighter/broker.py +857 -0
  11. qubx-0.6.94/src/qubx/connectors/xlighter/client.py +378 -0
  12. qubx-0.6.94/src/qubx/connectors/xlighter/constants.py +125 -0
  13. qubx-0.6.94/src/qubx/connectors/xlighter/data.py +722 -0
  14. qubx-0.6.94/src/qubx/connectors/xlighter/extensions.py +248 -0
  15. qubx-0.6.94/src/qubx/connectors/xlighter/factory.py +285 -0
  16. qubx-0.6.94/src/qubx/connectors/xlighter/handlers/__init__.py +15 -0
  17. qubx-0.6.94/src/qubx/connectors/xlighter/handlers/base.py +104 -0
  18. qubx-0.6.94/src/qubx/connectors/xlighter/handlers/orderbook.py +120 -0
  19. qubx-0.6.94/src/qubx/connectors/xlighter/handlers/quote.py +158 -0
  20. qubx-0.6.94/src/qubx/connectors/xlighter/handlers/stats.py +351 -0
  21. qubx-0.6.94/src/qubx/connectors/xlighter/handlers/trades.py +146 -0
  22. qubx-0.6.94/src/qubx/connectors/xlighter/instruments.py +253 -0
  23. qubx-0.6.94/src/qubx/connectors/xlighter/parsers.py +675 -0
  24. qubx-0.6.94/src/qubx/connectors/xlighter/reader.py +490 -0
  25. qubx-0.6.94/src/qubx/connectors/xlighter/utils.py +281 -0
  26. qubx-0.6.94/src/qubx/connectors/xlighter/websocket.py +443 -0
  27. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/account.py +40 -34
  28. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/basics.py +32 -5
  29. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/context.py +71 -6
  30. qubx-0.6.94/src/qubx/core/detectors/__init__.py +4 -0
  31. qubx-0.6.94/src/qubx/core/detectors/delisting.py +81 -0
  32. qubx-0.6.91/src/qubx/core/stale_data_detector.py → qubx-0.6.94/src/qubx/core/detectors/stale.py +101 -100
  33. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/initializer.py +46 -1
  34. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/interfaces.py +283 -0
  35. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/metrics.py +96 -5
  36. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/mixins/processing.py +13 -17
  37. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/mixins/trading.py +162 -5
  38. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/mixins/universe.py +7 -0
  39. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/series.pxd +8 -0
  40. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/series.pyi +46 -2
  41. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/series.pyx +134 -4
  42. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/storages/questdb.py +34 -8
  43. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/transformers.py +36 -1
  44. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/pandaz/utils.py +1 -1
  45. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/orderbook.py +102 -2
  46. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/runner/accounts.py +30 -1
  47. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/runner/configs.py +2 -1
  48. qubx-0.6.94/src/qubx/utils/runner/kernel_service.py +195 -0
  49. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/runner/runner.py +111 -11
  50. qubx-0.6.94/src/qubx/utils/runner/textual/__init__.py +177 -0
  51. qubx-0.6.94/src/qubx/utils/runner/textual/app.py +392 -0
  52. qubx-0.6.94/src/qubx/utils/runner/textual/handlers.py +90 -0
  53. qubx-0.6.94/src/qubx/utils/runner/textual/init_code.py +304 -0
  54. qubx-0.6.94/src/qubx/utils/runner/textual/kernel.py +269 -0
  55. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/runner/textual/styles.tcss +43 -9
  56. qubx-0.6.94/src/qubx/utils/runner/textual/widgets/__init__.py +10 -0
  57. qubx-0.6.94/src/qubx/utils/runner/textual/widgets/command_input.py +105 -0
  58. qubx-0.6.94/src/qubx/utils/runner/textual/widgets/debug_log.py +97 -0
  59. qubx-0.6.94/src/qubx/utils/runner/textual/widgets/orders_table.py +61 -0
  60. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/runner/textual/widgets/positions_table.py +3 -1
  61. qubx-0.6.94/src/qubx/utils/runner/textual/widgets/quotes_table.py +90 -0
  62. qubx-0.6.94/src/qubx/utils/runner/textual/widgets/repl_output.py +97 -0
  63. qubx-0.6.94/src/qubx/utils/websocket_manager.py +592 -0
  64. qubx-0.6.91/src/qubx/utils/runner/textual/__init__.py +0 -60
  65. qubx-0.6.91/src/qubx/utils/runner/textual/app.py +0 -149
  66. qubx-0.6.91/src/qubx/utils/runner/textual/handlers.py +0 -72
  67. qubx-0.6.91/src/qubx/utils/runner/textual/init_code.py +0 -143
  68. qubx-0.6.91/src/qubx/utils/runner/textual/kernel.py +0 -110
  69. qubx-0.6.91/src/qubx/utils/runner/textual/widgets/__init__.py +0 -6
  70. qubx-0.6.91/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -14
  71. {qubx-0.6.91 → qubx-0.6.94}/LICENSE +0 -0
  72. {qubx-0.6.91 → qubx-0.6.94}/README.md +0 -0
  73. {qubx-0.6.91 → qubx-0.6.94}/build.py +0 -0
  74. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/__init__.py +0 -0
  75. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/_nb_magic.py +0 -0
  76. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/__init__.py +0 -0
  77. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/account.py +0 -0
  78. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/broker.py +0 -0
  79. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/data.py +0 -0
  80. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/ome.py +0 -0
  81. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/optimization.py +0 -0
  82. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/runner.py +0 -0
  83. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/sentinels.py +0 -0
  84. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/simulated_data.py +0 -0
  85. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/simulated_exchange.py +0 -0
  86. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/backtester/utils.py +0 -0
  87. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/cli/__init__.py +0 -0
  88. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/cli/deploy.py +0 -0
  89. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/cli/misc.py +0 -0
  90. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/cli/release.py +0 -0
  91. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/cli/tui.py +0 -0
  92. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/__init__.py +0 -0
  93. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/account.py +0 -0
  94. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
  95. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
  96. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/broker.py +0 -0
  97. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
  98. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/data.py +0 -0
  99. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  100. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
  101. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
  102. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
  103. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
  104. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
  105. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
  106. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
  107. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
  108. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
  109. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
  110. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
  111. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
  112. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
  113. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
  114. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
  115. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
  116. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
  117. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
  118. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
  119. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
  120. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
  121. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
  122. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/reader.py +0 -0
  123. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
  124. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
  125. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
  126. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/utils.py +0 -0
  127. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
  128. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/tardis/data.py +0 -0
  129. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/connectors/tardis/utils.py +0 -0
  130. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/__init__.py +0 -0
  131. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/deque.py +0 -0
  132. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/errors.py +0 -0
  133. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/exceptions.py +0 -0
  134. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/helpers.py +0 -0
  135. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/loggers.py +0 -0
  136. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/lookups.py +0 -0
  137. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/mixins/__init__.py +0 -0
  138. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/mixins/market.py +0 -0
  139. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/mixins/subscription.py +0 -0
  140. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/mixins/utils.py +0 -0
  141. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/utils.pyi +0 -0
  142. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/core/utils.pyx +0 -0
  143. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/__init__.py +0 -0
  144. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/composite.py +0 -0
  145. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/containers.py +0 -0
  146. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/helpers.py +0 -0
  147. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/hft.py +0 -0
  148. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/readers.py +0 -0
  149. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/registry.py +0 -0
  150. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/storage.py +0 -0
  151. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/storages/csv.py +0 -0
  152. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/storages/utils.py +0 -0
  153. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/data/tardis.py +0 -0
  154. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/emitters/__init__.py +0 -0
  155. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/emitters/base.py +0 -0
  156. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/emitters/composite.py +0 -0
  157. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/emitters/csv.py +0 -0
  158. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/emitters/indicator.py +0 -0
  159. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/emitters/inmemory.py +0 -0
  160. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/emitters/prometheus.py +0 -0
  161. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/emitters/questdb.py +0 -0
  162. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/exporters/__init__.py +0 -0
  163. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/exporters/composite.py +0 -0
  164. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/exporters/formatters/__init__.py +0 -0
  165. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/exporters/formatters/base.py +0 -0
  166. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/exporters/formatters/incremental.py +0 -0
  167. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/exporters/formatters/slack.py +0 -0
  168. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/exporters/formatters/target_position.py +0 -0
  169. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/exporters/redis_streams.py +0 -0
  170. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/exporters/slack.py +0 -0
  171. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/features/__init__.py +0 -0
  172. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/features/core.py +0 -0
  173. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/features/orderbook.py +0 -0
  174. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/features/price.py +0 -0
  175. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/features/trades.py +0 -0
  176. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/features/utils.py +0 -0
  177. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/gathering/simplest.py +0 -0
  178. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/health/__init__.py +0 -0
  179. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/health/base.py +0 -0
  180. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/loggers/__init__.py +0 -0
  181. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/loggers/csv.py +0 -0
  182. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/loggers/factory.py +0 -0
  183. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/loggers/inmemory.py +0 -0
  184. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/loggers/mongo.py +0 -0
  185. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/math/__init__.py +0 -0
  186. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/math/stats.py +0 -0
  187. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/notifications/__init__.py +0 -0
  188. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/notifications/composite.py +0 -0
  189. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/notifications/slack.py +0 -0
  190. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/notifications/throttler.py +0 -0
  191. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/pandaz/__init__.py +0 -0
  192. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/pandaz/ta.py +0 -0
  193. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/_build.py +0 -0
  194. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/crypto-fees.ini +0 -0
  195. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
  196. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
  197. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
  198. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
  199. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
  200. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
  201. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
  202. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
  203. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
  204. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
  205. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
  206. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restarts/__init__.py +0 -0
  207. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restarts/state_resolvers.py +0 -0
  208. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restarts/time_finders.py +0 -0
  209. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restorers/__init__.py +0 -0
  210. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restorers/balance.py +0 -0
  211. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restorers/factory.py +0 -0
  212. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restorers/interfaces.py +0 -0
  213. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restorers/position.py +0 -0
  214. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restorers/signal.py +0 -0
  215. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restorers/state.py +0 -0
  216. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/restorers/utils.py +0 -0
  217. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/ta/__init__.py +0 -0
  218. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/ta/indicators.pxd +0 -0
  219. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/ta/indicators.pyi +0 -0
  220. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/ta/indicators.pyx +0 -0
  221. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/__init__.py +0 -0
  222. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/base.py +0 -0
  223. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/project/accounts.toml.j2 +0 -0
  224. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/project/config.yml.j2 +0 -0
  225. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/project/jlive.sh.j2 +0 -0
  226. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
  227. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
  228. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  229. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  230. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/project/template.yml +0 -0
  231. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/simple/__init__.py.j2 +0 -0
  232. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
  233. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/simple/config.yml.j2 +0 -0
  234. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
  235. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
  236. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/simple/strategy.py.j2 +0 -0
  237. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/templates/simple/template.yml +0 -0
  238. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/trackers/__init__.py +0 -0
  239. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/trackers/advanced.py +0 -0
  240. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/trackers/composite.py +0 -0
  241. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/trackers/rebalancers.py +0 -0
  242. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/trackers/riskctrl.py +0 -0
  243. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/trackers/sizers.py +0 -0
  244. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/__init__.py +0 -0
  245. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/_pyxreloader.py +0 -0
  246. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/charting/lookinglass.py +0 -0
  247. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  248. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/charting/orderbook.py +0 -0
  249. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/collections.py +0 -0
  250. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/marketdata/binance.py +0 -0
  251. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/marketdata/ccxt.py +0 -0
  252. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/marketdata/dukas.py +0 -0
  253. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/misc.py +0 -0
  254. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/ntp.py +0 -0
  255. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/numbers_utils.py +0 -0
  256. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/plotting/__init__.py +0 -0
  257. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/plotting/dashboard.py +0 -0
  258. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/plotting/data.py +0 -0
  259. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/plotting/interfaces.py +0 -0
  260. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  261. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  262. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/questdb.py +0 -0
  263. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/runner/__init__.py +0 -0
  264. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
  265. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/runner/factory.py +0 -0
  266. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/time.py +0 -0
  267. {qubx-0.6.91 → qubx-0.6.94}/src/qubx/utils/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Qubx
3
- Version: 0.6.91
3
+ Version: 0.6.94
4
4
  Summary: Qubx - Quantitative Trading Framework
5
5
  License-File: LICENSE
6
6
  Author: Dmitry Marienko
@@ -24,6 +24,7 @@ Requires-Dist: ipywidgets (>=8.1.5,<9.0.0)
24
24
  Requires-Dist: jinja2 (>=3.1.0,<4.0.0)
25
25
  Requires-Dist: jupyter (>=1.1.1,<2.0.0)
26
26
  Requires-Dist: jupyter-console (>=6.6.3,<7.0.0)
27
+ Requires-Dist: lighter-sdk (>=0.1.4,<0.2.0)
27
28
  Requires-Dist: loguru (>=0.7.2,<0.8.0)
28
29
  Requires-Dist: matplotlib (>=3.8.4,<4.0.0)
29
30
  Requires-Dist: msgspec (>=0.18.6,<0.19.0)
@@ -52,7 +53,9 @@ Requires-Dist: sortedcontainers (>=2.4.0,<3.0.0)
52
53
  Requires-Dist: stackprinter (>=0.2.10,<0.3.0)
53
54
  Requires-Dist: statsmodels (>=0.14.2,<0.15.0)
54
55
  Requires-Dist: tabulate (>=0.9.0,<0.10.0)
55
- Requires-Dist: textual (>=0.88.0,<0.89.0)
56
+ Requires-Dist: textual-autocomplete (>=4.0.0,<5.0.0)
57
+ Requires-Dist: textual-serve (>=1.0.0,<2.0.0)
58
+ Requires-Dist: textual[syntax] (>=6.0.0,<7.0.0)
56
59
  Requires-Dist: toml (>=0.10.2,<0.11.0)
57
60
  Requires-Dist: tqdm
58
61
  Requires-Dist: websockets (==15.0.1)
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "Qubx"
7
- version = "0.6.91"
7
+ version = "0.6.94"
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"
@@ -74,9 +74,11 @@ orjson = "^3.10.15"
74
74
  aiohttp = "~3.10.11"
75
75
  websockets = "15.0.1"
76
76
  qubx-bitfinex-api = "^3.0.7"
77
- textual = "^0.88.0"
77
+ textual-autocomplete = "^4.0.0"
78
+ textual-serve = "^1.0.0"
78
79
  rich = "^13.9.4"
79
80
  jinja2 = "^3.1.0"
81
+ lighter-sdk = "^0.1.4"
80
82
 
81
83
  [tool.ruff.lint]
82
84
  extend-select = [ "I",]
@@ -90,6 +92,10 @@ markers = [ "integration: mark test as requiring external services like Redis",
90
92
  addopts = "--disable-warnings"
91
93
  filterwarnings = [ "ignore:.*Jupyter is migrating.*:DeprecationWarning", "ignore:coroutine.*AsyncMockMixin._execute_mock_call.*was never awaited:RuntimeWarning",]
92
94
 
95
+ [tool.poetry.dependencies.textual]
96
+ extras = [ "syntax",]
97
+ version = "^6.0.0"
98
+
93
99
  [tool.ruff.lint.extend-per-file-ignores]
94
100
  "*.ipynb" = [ "F405", "F401", "E701", "E402", "F403", "E401", "E702", "I001",]
95
101
 
@@ -120,6 +126,7 @@ pytest-mock = "^3.12.0"
120
126
  pytest-lazy-fixture = "^0.6.3"
121
127
  pytest-cov = "^4.1.0"
122
128
  mongomock = "^4.3.0"
129
+ pytest-textual-snapshot = "^1.1.0"
123
130
 
124
131
  [tool.poetry.group.k8.dependencies]
125
132
  prometheus-client = "^0.21.1"
@@ -1,6 +1,7 @@
1
1
  import re
2
2
  import zipfile
3
3
  from collections import defaultdict
4
+ from os.path import expanduser
4
5
  from pathlib import Path
5
6
 
6
7
  import numpy as np
@@ -39,7 +40,7 @@ class BacktestsResultsManager:
39
40
  """
40
41
 
41
42
  def __init__(self, path: str):
42
- self.path = path
43
+ self.path = expanduser(path)
43
44
  self.reload()
44
45
 
45
46
  def reload(self) -> "BacktestsResultsManager":
@@ -1,4 +1,4 @@
1
- from typing import Literal
1
+ from typing import Literal, cast
2
2
 
3
3
  import pandas as pd
4
4
  from joblib import delayed
@@ -15,6 +15,7 @@ from qubx.utils.runner.configs import PrefetchConfig
15
15
  from qubx.utils.time import handle_start_stop, to_utc_naive
16
16
 
17
17
  from .runner import SimulationRunner
18
+ from .transfers import SimulationTransferManager
18
19
  from .utils import (
19
20
  DataDecls_t,
20
21
  ExchangeName_t,
@@ -300,6 +301,18 @@ def _run_setup(
300
301
  if enable_inmemory_emitter and emitter is not None:
301
302
  emitter_data = emitter.get_dataframe()
302
303
 
304
+ # - get transfers log
305
+ transfers_log = None
306
+ if hasattr(runner.ctx, "_transfer_manager") and isinstance(
307
+ getattr(runner.ctx, "_transfer_manager"), SimulationTransferManager
308
+ ):
309
+ try:
310
+ transfer_manager = cast(SimulationTransferManager, getattr(runner.ctx, "_transfer_manager"))
311
+ transfers_log = transfer_manager.get_transfers_dataframe()
312
+ except Exception as e:
313
+ logger.error(f"Failed to get transfers log: {e}")
314
+ transfers_log = None
315
+
303
316
  return TradingSessionResult(
304
317
  setup_id,
305
318
  setup.name,
@@ -319,6 +332,7 @@ def _run_setup(
319
332
  is_simulation=True,
320
333
  author=get_current_user(),
321
334
  emitter_data=emitter_data,
335
+ transfers_log=transfers_log,
322
336
  )
323
337
  except Exception as e:
324
338
  logger.error(f"Simulation setup {setup_id} failed with error: {e}")
@@ -0,0 +1,146 @@
1
+ import uuid
2
+ from typing import Any
3
+
4
+ import pandas as pd
5
+
6
+ from qubx import logger
7
+ from qubx.core.account import CompositeAccountProcessor
8
+ from qubx.core.basics import ITimeProvider
9
+ from qubx.core.interfaces import ITransferManager
10
+
11
+
12
+ class SimulationTransferManager(ITransferManager):
13
+ """
14
+ Transfer manager for simulation mode.
15
+
16
+ Handles fund transfers between exchanges by directly manipulating account balances.
17
+ All transfers are instant and tracked in a DataFrame for export to results.
18
+ """
19
+
20
+ _account: CompositeAccountProcessor
21
+ _time: ITimeProvider
22
+ _transfers: list[dict[str, Any]]
23
+
24
+ def __init__(self, account_processor: CompositeAccountProcessor, time_provider: ITimeProvider):
25
+ """
26
+ Initialize simulation transfer manager.
27
+
28
+ Args:
29
+ account_processor: Account processor (typically CompositeAccountProcessor)
30
+ time_provider: Time provider for timestamping transfers
31
+ """
32
+ self._account = account_processor
33
+ self._time = time_provider
34
+ self._transfers = []
35
+
36
+ def transfer_funds(self, from_exchange: str, to_exchange: str, currency: str, amount: float) -> str:
37
+ """
38
+ Transfer funds between exchanges (instant in simulation).
39
+
40
+ Args:
41
+ from_exchange: Source exchange identifier
42
+ to_exchange: Destination exchange identifier
43
+ currency: Currency to transfer
44
+ amount: Amount to transfer
45
+
46
+ Returns:
47
+ str: Transaction ID (UUID)
48
+
49
+ Raises:
50
+ ValueError: If exchanges not found or insufficient funds
51
+ """
52
+ # Generate transaction ID
53
+ transaction_id = f"sim_{uuid.uuid4().hex[:12]}"
54
+
55
+ # Get timestamp
56
+ timestamp = self._time.time()
57
+
58
+ # Get individual processors
59
+ try:
60
+ from_processor = self._account.get_account_processor(from_exchange)
61
+ to_processor = self._account.get_account_processor(to_exchange)
62
+ except (KeyError, AttributeError) as e:
63
+ raise ValueError(f"Exchange not found: {e}")
64
+
65
+ # Validate sufficient funds
66
+ from_balances = from_processor.get_balances()
67
+ if currency not in from_balances:
68
+ raise ValueError(f"Currency '{currency}' not found in {from_exchange}")
69
+
70
+ available = from_balances[currency].free
71
+ if available < amount:
72
+ raise ValueError(
73
+ f"Insufficient funds in {from_exchange}: "
74
+ f"{available:.8f} {currency} available, {amount:.8f} {currency} requested"
75
+ )
76
+
77
+ # Execute transfer (instant balance manipulation)
78
+ from_balances[currency].total -= amount
79
+ from_balances[currency].free -= amount
80
+
81
+ to_balances = to_processor.get_balances()
82
+ to_balances[currency].total += amount
83
+ to_balances[currency].free += amount
84
+
85
+ # Record transfer
86
+ transfer_record = {
87
+ "transaction_id": transaction_id,
88
+ "timestamp": timestamp,
89
+ "from_exchange": from_exchange,
90
+ "to_exchange": to_exchange,
91
+ "currency": currency,
92
+ "amount": amount,
93
+ "status": "completed", # Always completed in simulation
94
+ }
95
+ self._transfers.append(transfer_record)
96
+
97
+ logger.debug(f"[SimTransfer] {amount:.8f} {currency} {from_exchange} → {to_exchange} (ID: {transaction_id})")
98
+
99
+ return transaction_id
100
+
101
+ def get_transfer_status(self, transaction_id: str) -> dict[str, Any]:
102
+ """
103
+ Get the status of a transfer.
104
+
105
+ Args:
106
+ transaction_id: Transaction ID
107
+
108
+ Returns:
109
+ dict[str, Any]: Transfer status information
110
+ """
111
+ # Find transfer
112
+ for transfer in self._transfers:
113
+ if transfer["transaction_id"] == transaction_id:
114
+ return transfer.copy()
115
+
116
+ # Not found
117
+ return {
118
+ "transaction_id": transaction_id,
119
+ "status": "not_found",
120
+ "error": f"Transaction {transaction_id} not found",
121
+ }
122
+
123
+ def get_transfers(self) -> dict[str, dict[str, Any]]:
124
+ """
125
+ Get all transfers as a dictionary.
126
+
127
+ Returns:
128
+ dict[str, dict[str, Any]]: Dictionary mapping transaction IDs to transfer info
129
+ """
130
+ return {t["transaction_id"]: t for t in self._transfers}
131
+
132
+ def get_transfers_dataframe(self) -> pd.DataFrame:
133
+ """
134
+ Get all transfers as a pandas DataFrame.
135
+
136
+ Returns:
137
+ pd.DataFrame: DataFrame with columns [transaction_id, timestamp, from_exchange,
138
+ to_exchange, currency, amount, status]
139
+ """
140
+ if not self._transfers:
141
+ # Return empty DataFrame with correct schema
142
+ return pd.DataFrame(
143
+ columns=["transaction_id", "from_exchange", "to_exchange", "currency", "amount", "status"] # type: ignore
144
+ )
145
+
146
+ return pd.DataFrame(self._transfers).set_index("timestamp")
@@ -72,11 +72,29 @@ def main(debug: bool, debug_port: int, log_level: str):
72
72
  @click.option(
73
73
  "--textual", "-t", is_flag=True, default=False, help="Run strategy in textual TUI.", show_default=True
74
74
  )
75
+ @click.option(
76
+ "--textual-dev", is_flag=True, default=False, help="Enable Textual dev mode (use with 'textual console').", show_default=True
77
+ )
78
+ @click.option(
79
+ "--textual-web", is_flag=True, default=False, help="Serve Textual app in web browser.", show_default=True
80
+ )
81
+ @click.option(
82
+ "--textual-port", type=int, default=None, help="Port for Textual (web server: 8000, devtools: 8081).", show_default=False
83
+ )
84
+ @click.option(
85
+ "--textual-host", type=str, default="0.0.0.0", help="Host for Textual web server.", show_default=True
86
+ )
87
+ @click.option(
88
+ "--kernel-only", is_flag=True, default=False, help="Start kernel without UI (returns connection file).", show_default=True
89
+ )
90
+ @click.option(
91
+ "--connect", type=Path, default=None, help="Connect to existing kernel via connection file.", show_default=False
92
+ )
75
93
  @click.option(
76
94
  "--restore", "-r", is_flag=True, default=False, help="Restore strategy state from previous run.", show_default=True
77
95
  )
78
96
  @click.option("--no-color", is_flag=True, default=False, help="Disable colored logging output.", show_default=True)
79
- def run(config_file: Path, account_file: Path | None, paper: bool, jupyter: bool, textual: bool, restore: bool, no_color: bool):
97
+ def run(config_file: Path, account_file: Path | None, paper: bool, jupyter: bool, textual: bool, textual_dev: bool, textual_web: bool, textual_port: int | None, textual_host: str, kernel_only: bool, connect: Path | None, restore: bool, no_color: bool):
80
98
  """
81
99
  Starts the strategy with the given configuration file. If paper mode is enabled, account is not required.
82
100
 
@@ -94,6 +112,39 @@ def run(config_file: Path, account_file: Path | None, paper: bool, jupyter: bool
94
112
  click.echo("Error: --jupyter and --textual cannot be used together.", err=True)
95
113
  raise click.Abort()
96
114
 
115
+ # Handle --kernel-only mode
116
+ if kernel_only:
117
+ import asyncio
118
+
119
+ from qubx.utils.runner.kernel_service import KernelService
120
+
121
+ add_project_to_system_path()
122
+ add_project_to_system_path(str(config_file.parent.parent))
123
+ add_project_to_system_path(str(config_file.parent))
124
+
125
+ click.echo("Starting persistent kernel...")
126
+ connection_file = asyncio.run(KernelService.start(config_file, account_file, paper, restore))
127
+ click.echo(click.style("✓ Kernel started successfully!", fg="green", bold=True))
128
+ click.echo(click.style(f"Connection file: {connection_file}", fg="cyan"))
129
+ click.echo()
130
+ click.echo("To connect a UI to this kernel:")
131
+ click.echo(f" qubx run --textual --connect {connection_file}")
132
+ click.echo()
133
+ click.echo("To stop this kernel:")
134
+ click.echo(f" qubx kernel stop {connection_file}")
135
+ click.echo()
136
+ click.echo("Press Ctrl+C to stop the kernel and exit...")
137
+
138
+ # Keep the process alive until interrupted
139
+ try:
140
+ import signal
141
+ signal.pause()
142
+ except KeyboardInterrupt:
143
+ click.echo("\nShutting down kernel...")
144
+ asyncio.run(KernelService.stop(connection_file))
145
+ click.echo("Kernel stopped.")
146
+ return
147
+
97
148
  add_project_to_system_path()
98
149
  add_project_to_system_path(str(config_file.parent.parent))
99
150
  add_project_to_system_path(str(config_file.parent))
@@ -101,7 +152,7 @@ def run(config_file: Path, account_file: Path | None, paper: bool, jupyter: bool
101
152
  if jupyter:
102
153
  run_strategy_yaml_in_jupyter(config_file, account_file, paper, restore)
103
154
  elif textual:
104
- run_strategy_yaml_in_textual(config_file, account_file, paper, restore)
155
+ run_strategy_yaml_in_textual(config_file, account_file, paper, restore, textual_dev, textual_web, textual_port, textual_host, connect)
105
156
  else:
106
157
  logo()
107
158
  run_strategy_yaml(config_file, account_file, paper=paper, restore=restore, blocking=True, no_color=no_color)
@@ -461,5 +512,69 @@ def init(
461
512
  raise click.Abort()
462
513
 
463
514
 
515
+ @main.group()
516
+ def kernel():
517
+ """
518
+ Manage persistent Jupyter kernels for strategy execution.
519
+
520
+ Kernels can be started independently of the UI, allowing multiple
521
+ UI instances to connect to the same running strategy.
522
+ """
523
+ pass
524
+
525
+
526
+ @kernel.command("list")
527
+ def kernel_list():
528
+ """
529
+ List all active kernel sessions.
530
+
531
+ Shows connection files and associated strategy configurations
532
+ for all currently running kernels.
533
+ """
534
+ from qubx.utils.runner.kernel_service import KernelService
535
+
536
+ active = KernelService.list_active()
537
+
538
+ if not active:
539
+ click.echo("No active kernels found.")
540
+ return
541
+
542
+ import datetime
543
+
544
+ click.echo(click.style("Active Kernels:", fg="cyan", bold=True))
545
+ click.echo()
546
+ for i, kernel_info in enumerate(active, 1):
547
+ # Format timestamp
548
+ ts = datetime.datetime.fromtimestamp(kernel_info["timestamp"])
549
+ time_str = ts.strftime("%Y-%m-%d %H:%M:%S")
550
+
551
+ click.echo(f"{i}. {click.style('Strategy:', fg='yellow')} {kernel_info['strategy_name']}")
552
+ click.echo(f" {click.style('Started:', fg='yellow')} {time_str}")
553
+ click.echo(f" {click.style('Connection:', fg='yellow')} {kernel_info['connection_file']}")
554
+ click.echo()
555
+
556
+
557
+ @kernel.command("stop")
558
+ @click.argument("connection-file", type=Path, required=True)
559
+ def kernel_stop(connection_file: Path):
560
+ """
561
+ Stop a running kernel by its connection file.
562
+
563
+ This will gracefully shutdown the kernel and clean up
564
+ the connection file.
565
+ """
566
+ import asyncio
567
+
568
+ from qubx.utils.runner.kernel_service import KernelService
569
+
570
+ if not connection_file.exists():
571
+ click.echo(click.style(f"✗ Connection file not found: {connection_file}", fg="red"))
572
+ raise click.Abort()
573
+
574
+ click.echo(f"Stopping kernel: {connection_file}")
575
+ asyncio.run(KernelService.stop(str(connection_file)))
576
+ click.echo(click.style("✓ Kernel stopped successfully", fg="green"))
577
+
578
+
464
579
  if __name__ == "__main__":
465
580
  main()
@@ -8,8 +8,8 @@ from qubx.core.basics import CtrlChannel
8
8
  from qubx.core.interfaces import IAccountProcessor, IBroker, IDataProvider, ITimeProvider
9
9
 
10
10
  from .account import CcxtAccountProcessor
11
- from .exchanges import CUSTOM_ACCOUNTS, CUSTOM_BROKERS, EXCHANGE_ALIASES
12
11
  from .exchange_manager import ExchangeManager
12
+ from .exchanges import CUSTOM_ACCOUNTS, CUSTOM_BROKERS, EXCHANGE_ALIASES
13
13
 
14
14
 
15
15
  def get_ccxt_exchange(
@@ -22,10 +22,10 @@ def get_ccxt_exchange(
22
22
  ) -> cxp.Exchange:
23
23
  """
24
24
  Get a raw CCXT exchange object.
25
-
25
+
26
26
  Creates and configures a CCXT exchange instance without any stability wrapper.
27
27
  Use get_ccxt_exchange_manager() if you need automatic stability management.
28
-
28
+
29
29
  Parameters:
30
30
  exchange (str): The exchange name.
31
31
  api_key (str, optional): The API key. Default is None.
@@ -33,7 +33,7 @@ def get_ccxt_exchange(
33
33
  loop (asyncio.AbstractEventLoop, optional): Event loop. Default is None.
34
34
  use_testnet (bool): Use testnet/sandbox mode. Default is False.
35
35
  **kwargs: Additional parameters for exchange configuration.
36
-
36
+
37
37
  Returns:
38
38
  Raw CCXT Exchange instance
39
39
  """
@@ -90,10 +90,10 @@ def get_ccxt_exchange_manager(
90
90
  ) -> ExchangeManager:
91
91
  """
92
92
  Get a CCXT exchange with automatic stability management.
93
-
93
+
94
94
  Returns ExchangeManager wrapper that handles exchange recreation
95
95
  during data stall scenarios via self-monitoring.
96
-
96
+
97
97
  Parameters:
98
98
  exchange (str): The exchange name.
99
99
  api_key (str, optional): The API key. Default is None.
@@ -102,28 +102,23 @@ def get_ccxt_exchange_manager(
102
102
  use_testnet (bool): Use testnet/sandbox mode. Default is False.
103
103
  check_interval_seconds (float): How often to check for stalls. Default is 30.0.
104
104
  **kwargs: Additional parameters for exchange configuration.
105
-
105
+
106
106
  Returns:
107
107
  ExchangeManager wrapping the CCXT Exchange
108
108
  """
109
109
  # Prepare factory parameters for ExchangeManager recreation
110
110
  factory_params = {
111
- 'exchange': exchange,
112
- 'api_key': api_key,
113
- 'secret': secret,
114
- 'loop': loop,
115
- 'use_testnet': use_testnet,
116
- **{k: v for k, v in kwargs.items() if k != 'check_interval_seconds'}
111
+ "exchange": exchange,
112
+ "api_key": api_key,
113
+ "secret": secret,
114
+ "loop": loop,
115
+ "use_testnet": use_testnet,
116
+ **{k: v for k, v in kwargs.items() if k != "check_interval_seconds"},
117
117
  }
118
-
118
+
119
119
  # Create raw CCXT exchange using public factory method
120
120
  ccxt_exchange = get_ccxt_exchange(
121
- exchange=exchange,
122
- api_key=api_key,
123
- secret=secret,
124
- loop=loop,
125
- use_testnet=use_testnet,
126
- **kwargs
121
+ exchange=exchange, api_key=api_key, secret=secret, loop=loop, use_testnet=use_testnet, **kwargs
127
122
  )
128
123
 
129
124
  # Wrap in ExchangeManager for stability management
@@ -0,0 +1,83 @@
1
+ """XLighter exchange connector for Qubx"""
2
+
3
+ from .account import LighterAccountProcessor
4
+ from .broker import LighterBroker
5
+ from .client import LighterClient
6
+ from .constants import (
7
+ DEDICATED_CHANNELS,
8
+ MULTIPLEX_CHANNELS,
9
+ WS_CHANNEL_ACCOUNT_ALL,
10
+ WS_CHANNEL_EXECUTED_TX,
11
+ WS_CHANNEL_MARKET_STATS,
12
+ WS_CHANNEL_ORDER_BOOK,
13
+ WS_CHANNEL_TRADE,
14
+ WS_CHANNEL_USER_STATS,
15
+ LighterMarginMode,
16
+ LighterOrderSide,
17
+ LighterOrderType,
18
+ LighterTimeInForce,
19
+ )
20
+ from .data import LighterDataProvider
21
+ from .factory import (
22
+ get_xlighter_account,
23
+ get_xlighter_broker,
24
+ get_xlighter_client,
25
+ get_xlighter_data_provider,
26
+ )
27
+ from .instruments import LighterInstrumentLoader, load_lighter_instruments
28
+ from .parsers import PositionState
29
+ from .reader import XLighterDataReader
30
+ from .utils import (
31
+ convert_lighter_order,
32
+ convert_lighter_orderbook,
33
+ convert_lighter_quote,
34
+ convert_lighter_trade,
35
+ lighter_order_side_to_qubx,
36
+ lighter_symbol_to_qubx,
37
+ qubx_order_side_to_lighter,
38
+ qubx_symbol_to_lighter,
39
+ )
40
+ from .websocket import LighterWebSocketManager
41
+
42
+ __all__ = [
43
+ # Core Components
44
+ "LighterClient",
45
+ "LighterDataProvider",
46
+ "LighterBroker",
47
+ "LighterAccountProcessor",
48
+ "LighterWebSocketManager",
49
+ # Factory Functions
50
+ "get_xlighter_client",
51
+ "get_xlighter_data_provider",
52
+ "get_xlighter_account",
53
+ "get_xlighter_broker",
54
+ # Data Reader
55
+ "XLighterDataReader",
56
+ # Instruments
57
+ "LighterInstrumentLoader",
58
+ "load_lighter_instruments",
59
+ # Parsers
60
+ "PositionState",
61
+ # Constants
62
+ "LighterOrderType",
63
+ "LighterTimeInForce",
64
+ "LighterOrderSide",
65
+ "LighterMarginMode",
66
+ "WS_CHANNEL_ORDER_BOOK",
67
+ "WS_CHANNEL_TRADE",
68
+ "WS_CHANNEL_MARKET_STATS",
69
+ "WS_CHANNEL_ACCOUNT_ALL",
70
+ "WS_CHANNEL_USER_STATS",
71
+ "WS_CHANNEL_EXECUTED_TX",
72
+ "MULTIPLEX_CHANNELS",
73
+ "DEDICATED_CHANNELS",
74
+ # Utils
75
+ "lighter_symbol_to_qubx",
76
+ "qubx_symbol_to_lighter",
77
+ "lighter_order_side_to_qubx",
78
+ "qubx_order_side_to_lighter",
79
+ "convert_lighter_orderbook",
80
+ "convert_lighter_trade",
81
+ "convert_lighter_quote",
82
+ "convert_lighter_order",
83
+ ]