Qubx 1.2.2.dev2__tar.gz → 1.2.3.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/PKG-INFO +1 -1
  2. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/_version.py +2 -2
  3. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/okx/account.py +55 -4
  4. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/account.py +4 -1
  5. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/loggers.py +39 -23
  6. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/utils.pyx +6 -0
  7. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/configs.py +3 -2
  8. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/runner.py +1 -0
  9. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/.gitignore +0 -0
  10. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/LICENSE +0 -0
  11. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/README.md +0 -0
  12. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/pyproject.toml +0 -0
  13. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/scripts/build.py +0 -0
  14. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/__init__.py +0 -0
  15. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/_nb_magic.py +0 -0
  16. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/__init__.py +0 -0
  17. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/account.py +0 -0
  18. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/broker.py +0 -0
  19. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/data.py +0 -0
  20. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/iteratedstream.py +0 -0
  21. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/management.py +0 -0
  22. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/ome.py +0 -0
  23. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/optimization.py +0 -0
  24. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/runner.py +0 -0
  25. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/sentinels.py +0 -0
  26. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/simulated_data.py +0 -0
  27. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/simulated_exchange.py +0 -0
  28. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/simulator.py +0 -0
  29. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/transfers.py +0 -0
  30. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/backtester/utils.py +0 -0
  31. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/cli/__init__.py +0 -0
  32. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/cli/commands.py +0 -0
  33. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/cli/deploy.py +0 -0
  34. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/cli/misc.py +0 -0
  35. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/cli/release.py +0 -0
  36. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/cli/resolver.py +0 -0
  37. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/cli/s3.py +0 -0
  38. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/cli/theme.py +0 -0
  39. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/cli/tui.py +0 -0
  40. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/cli/user_config.py +0 -0
  41. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/config.py +0 -0
  42. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/__init__.py +0 -0
  43. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/__init__.py +0 -0
  44. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/account.py +0 -0
  45. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
  46. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
  47. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/broker.py +0 -0
  48. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
  49. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/data.py +0 -0
  50. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  51. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
  52. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
  53. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
  54. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
  55. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
  56. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
  57. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
  58. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/gateio/__init__.py +0 -0
  59. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/gateio/gateio.py +0 -0
  60. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
  61. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
  62. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
  63. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
  64. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
  65. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/okx/__init__.py +0 -0
  66. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/okx/broker.py +0 -0
  67. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/exchanges/okx/okx.py +0 -0
  68. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/factory.py +0 -0
  69. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
  70. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
  71. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
  72. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
  73. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
  74. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
  75. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
  76. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
  77. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
  78. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
  79. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/rate_limits.py +0 -0
  80. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
  81. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
  82. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
  83. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/utils.py +0 -0
  84. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
  85. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/registry.py +0 -0
  86. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/tardis/data.py +0 -0
  87. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/connectors/tardis/utils.py +0 -0
  88. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/control/__init__.py +0 -0
  89. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/control/builtin.py +0 -0
  90. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/control/decorator.py +0 -0
  91. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/control/executor.py +0 -0
  92. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/control/interfaces.py +0 -0
  93. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/control/server.py +0 -0
  94. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/control/types.py +0 -0
  95. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/__init__.py +0 -0
  96. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/basics.py +0 -0
  97. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/context.py +0 -0
  98. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/detectors/__init__.py +0 -0
  99. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/detectors/delisting.py +0 -0
  100. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/detectors/stale.py +0 -0
  101. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/errors.py +0 -0
  102. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/exceptions.py +0 -0
  103. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/helpers.py +0 -0
  104. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/initializer.py +0 -0
  105. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/interfaces.py +0 -0
  106. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/lookups.py +0 -0
  107. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/metrics.py +0 -0
  108. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/mixins/__init__.py +0 -0
  109. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/mixins/market.py +0 -0
  110. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/mixins/processing.py +0 -0
  111. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/mixins/subscription.py +0 -0
  112. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/mixins/trading.py +0 -0
  113. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/mixins/universe.py +0 -0
  114. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/mixins/utils.py +0 -0
  115. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/series.pxd +0 -0
  116. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/series.pyi +0 -0
  117. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/series.pyx +0 -0
  118. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/core/utils.pyi +0 -0
  119. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/__init__.py +0 -0
  120. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/cache.py +0 -0
  121. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/containers.py +0 -0
  122. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/guards.py +0 -0
  123. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/registry.py +0 -0
  124. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/storage.py +0 -0
  125. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/storages/ccxt.py +0 -0
  126. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/storages/csv.py +0 -0
  127. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/storages/handy.py +0 -0
  128. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/storages/multi.py +0 -0
  129. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/storages/questdb.py +0 -0
  130. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/storages/stub.py +0 -0
  131. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/storages/utils.py +0 -0
  132. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/data/transformers.py +0 -0
  133. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/emitters/__init__.py +0 -0
  134. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/emitters/base.py +0 -0
  135. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/emitters/composite.py +0 -0
  136. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/emitters/csv.py +0 -0
  137. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/emitters/indicator.py +0 -0
  138. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/emitters/inmemory.py +0 -0
  139. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/emitters/prometheus.py +0 -0
  140. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/emitters/questdb.py +0 -0
  141. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/exporters/__init__.py +0 -0
  142. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/exporters/composite.py +0 -0
  143. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/exporters/formatters/__init__.py +0 -0
  144. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/exporters/formatters/base.py +0 -0
  145. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/exporters/formatters/incremental.py +0 -0
  146. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/exporters/formatters/slack.py +0 -0
  147. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/exporters/formatters/target_position.py +0 -0
  148. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/exporters/redis_streams.py +0 -0
  149. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/exporters/slack.py +0 -0
  150. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/gathering/simplest.py +0 -0
  151. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/health/__init__.py +0 -0
  152. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/health/base.py +0 -0
  153. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/health/dummy.py +0 -0
  154. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/loggers/__init__.py +0 -0
  155. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/loggers/csv.py +0 -0
  156. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/loggers/factory.py +0 -0
  157. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/loggers/inmemory.py +0 -0
  158. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/loggers/mongo.py +0 -0
  159. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/loggers/postgres.py +0 -0
  160. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/notifications/__init__.py +0 -0
  161. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/notifications/composite.py +0 -0
  162. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/notifications/slack.py +0 -0
  163. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/notifications/throttler.py +0 -0
  164. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/pandaz/__init__.py +0 -0
  165. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/pandaz/stats.py +0 -0
  166. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/pandaz/ta.py +0 -0
  167. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/pandaz/utils.py +0 -0
  168. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/plugins/__init__.py +0 -0
  169. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/plugins/loader.py +0 -0
  170. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/rate_limiting/__init__.py +0 -0
  171. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/rate_limiting/backend.py +0 -0
  172. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/rate_limiting/config.py +0 -0
  173. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/rate_limiting/engine.py +0 -0
  174. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/rate_limiting/ip_resolver.py +0 -0
  175. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/rate_limiting/manager.py +0 -0
  176. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/rate_limiting/pools.py +0 -0
  177. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/rate_limiting/redis_backend.py +0 -0
  178. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/_build.py +0 -0
  179. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/crypto-fees.ini +0 -0
  180. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
  181. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
  182. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
  183. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
  184. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
  185. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
  186. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
  187. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
  188. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
  189. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
  190. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
  191. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restarts/__init__.py +0 -0
  192. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restarts/state_resolvers.py +0 -0
  193. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restarts/time_finders.py +0 -0
  194. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restorers/__init__.py +0 -0
  195. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restorers/balance.py +0 -0
  196. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restorers/factory.py +0 -0
  197. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restorers/interfaces.py +0 -0
  198. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restorers/position.py +0 -0
  199. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restorers/signal.py +0 -0
  200. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restorers/state.py +0 -0
  201. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/restorers/utils.py +0 -0
  202. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/state/__init__.py +0 -0
  203. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/state/dummy.py +0 -0
  204. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/state/redis.py +0 -0
  205. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/ta/__init__.py +0 -0
  206. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/ta/indicators.pxd +0 -0
  207. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/ta/indicators.pyi +0 -0
  208. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/ta/indicators.pyx +0 -0
  209. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/__init__.py +0 -0
  210. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/base.py +0 -0
  211. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/project/accounts.toml.j2 +0 -0
  212. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/project/config.yml.j2 +0 -0
  213. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/project/jlive.sh.j2 +0 -0
  214. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
  215. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
  216. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  217. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  218. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/project/template.yml +0 -0
  219. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/.claude/skills/qubx-cli/SKILL.md +0 -0
  220. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/.claude/skills/qubx-indicators/SKILL.md +0 -0
  221. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/.claude/skills/simulation-explorer/SKILL.md +0 -0
  222. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/.claude/skills/strategy-release/SKILL.md +0 -0
  223. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/.github/workflows/ci.yml.j2 +0 -0
  224. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/.gitignore.j2 +0 -0
  225. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/.python-version +0 -0
  226. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/.vscode/launch.json +0 -0
  227. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/.vscode/settings.json +0 -0
  228. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/.vscode/tasks.json +0 -0
  229. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/CLAUDE.md.j2 +0 -0
  230. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/README.md.j2 +0 -0
  231. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/configs/.gitkeep +0 -0
  232. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/justfile.j2 +0 -0
  233. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/pyproject.toml.j2 +0 -0
  234. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/research/.gitkeep +0 -0
  235. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  236. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/src/{{ strategy_name }}/cli.py.j2 +0 -0
  237. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  238. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/template.yml +0 -0
  239. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/repo/tests/test_strategy.py.j2 +0 -0
  240. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/simple/__init__.py.j2 +0 -0
  241. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
  242. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/simple/config.yml.j2 +0 -0
  243. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
  244. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
  245. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/simple/strategy.py.j2 +0 -0
  246. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/templates/simple/template.yml +0 -0
  247. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/trackers/__init__.py +0 -0
  248. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/trackers/advanced.py +0 -0
  249. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/trackers/composite.py +0 -0
  250. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/trackers/rebalancers.py +0 -0
  251. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/trackers/riskctrl.py +0 -0
  252. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/trackers/sizers.py +0 -0
  253. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/__init__.py +0 -0
  254. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/_pyxreloader.py +0 -0
  255. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/charting/lookinglass.py +0 -0
  256. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  257. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/charting/orderbook.py +0 -0
  258. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/collections.py +0 -0
  259. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/hft/__init__.py +0 -0
  260. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/hft/numba_utils.py +0 -0
  261. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/hft/orderbook.pyi +0 -0
  262. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/hft/orderbook.pyx +0 -0
  263. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/marketdata/binance.py +0 -0
  264. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/marketdata/ccxt.py +0 -0
  265. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/marketdata/dukas.py +0 -0
  266. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/misc.py +0 -0
  267. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/nonce.py +0 -0
  268. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/ntp.py +0 -0
  269. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/numbers_utils.py +0 -0
  270. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/orderbook.py +0 -0
  271. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/plotting/__init__.py +0 -0
  272. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/plotting/dashboard.py +0 -0
  273. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/plotting/data.py +0 -0
  274. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/plotting/interfaces.py +0 -0
  275. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  276. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  277. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/questdb.py +0 -0
  278. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/rate_limiter.py +0 -0
  279. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/results.py +0 -0
  280. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/ringbuffer.pxd +0 -0
  281. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/ringbuffer.pyi +0 -0
  282. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/ringbuffer.pyx +0 -0
  283. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/__init__.py +0 -0
  284. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
  285. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/accounts.py +0 -0
  286. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/factory.py +0 -0
  287. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/kernel_service.py +0 -0
  288. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/__init__.py +0 -0
  289. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/app.py +0 -0
  290. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/handlers.py +0 -0
  291. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/init_code.py +0 -0
  292. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/kernel.py +0 -0
  293. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/styles.tcss +0 -0
  294. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
  295. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/widgets/account_summary.py +0 -0
  296. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
  297. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
  298. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
  299. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
  300. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
  301. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
  302. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/s3.py +0 -0
  303. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/slack.py +0 -0
  304. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/throttler.py +0 -0
  305. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/time.py +0 -0
  306. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/src/qubx/utils/websocket_manager.py +0 -0
  307. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/tests/strategies/macd_crossover/src/macd_crossover/indicators/macd.py +0 -0
  308. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/tests/strategies/macd_crossover/src/macd_crossover/models/macd_crossover.py +0 -0
  309. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/tests/strategies/macd_crossover/src/macd_crossover/models/utils.py +0 -0
  310. {qubx-1.2.2.dev2 → qubx-1.2.3.dev1}/tests/strategies/obi_trader/src/obi_trader/models/obi_trader.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Qubx
3
- Version: 1.2.2.dev2
3
+ Version: 1.2.3.dev1
4
4
  Summary: Qubx - Quantitative Trading Framework
5
5
  Project-URL: homepage, https://xlydian.com
6
6
  Project-URL: repository, https://github.com/xLydianSoftware/Qubx
@@ -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.2.2.dev2'
22
- __version_tuple__ = version_tuple = (1, 2, 2, 'dev2')
21
+ __version__ = version = '1.2.3.dev1'
22
+ __version_tuple__ = version_tuple = (1, 2, 3, 'dev1')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -18,8 +18,35 @@ class OkxAccountProcessor(CcxtAccountProcessor):
18
18
  so we must subscribe to both channels separately:
19
19
  - orders channel: for order status updates (watch_orders)
20
20
  - trades channel: for trade/fill updates (watch_my_trades)
21
+
22
+ Balance streaming: uses watch_balance WS to get real-time totalEq updates
23
+ instead of relying solely on 30s polling.
21
24
  """
22
25
 
26
+ def start(self):
27
+ super().start()
28
+ if self._is_running and not self.exchange_manager.exchange.isSandboxModeEnabled:
29
+ self._polling_tasks["balance_ws"] = self._loop.submit(self._watch_balance_stream())
30
+
31
+ async def _watch_balance_stream(self):
32
+ """Stream balance updates via WS to get real-time totalEq."""
33
+
34
+ async def _watch():
35
+ balances_raw = await self.exchange_manager.exchange.watch_balance()
36
+ equity = self._extract_portfolio_value(balances_raw)
37
+ if equity is not None:
38
+ self._exchange_total_capital = equity
39
+ margin_ratio = self._extract_margin_ratio(balances_raw)
40
+ if margin_ratio is not None:
41
+ self._exchange_margin_ratio = margin_ratio
42
+
43
+ await self._listen_to_stream(
44
+ subscriber=_watch,
45
+ exchange=self.exchange_manager.exchange,
46
+ channel=self.channel,
47
+ name="okx_balance_ws",
48
+ )
49
+
23
50
  async def _subscribe_executions(self, name: str, channel: CtrlChannel):
24
51
  logger.info("<yellow>[OKX]</yellow> Subscribing to executions (orders + trades)")
25
52
  _symbol_to_instrument = {}
@@ -57,11 +84,31 @@ class OkxAccountProcessor(CcxtAccountProcessor):
57
84
  ),
58
85
  )
59
86
 
87
+ def _get_account_data(self, balances_raw: dict) -> dict:
88
+ """Get the OKX account data dict from info.data[0]."""
89
+ return balances_raw.get("info", {}).get("data", [{}])[0]
90
+
91
+ def _extract_margin_ratio(self, balances_raw: dict) -> float | None:
92
+ """Extract OKX margin ratio from the base currency detail entry.
93
+
94
+ The account-level mgnRatio is empty; the real value is per-currency in details.
95
+ We use the base currency's mgnRatio as the account margin ratio.
96
+ """
97
+ details = self._get_account_data(balances_raw).get("details", [])
98
+ for detail in details:
99
+ if detail.get("ccy", "").upper() == self.base_currency.upper():
100
+ val = detail.get("mgnRatio")
101
+ if val is not None and val != "":
102
+ try:
103
+ return float(val)
104
+ except (ValueError, TypeError):
105
+ pass
106
+ return None
107
+
60
108
  def _extract_portfolio_value(self, balances_raw: dict) -> float | None:
61
- """Extract OKX total equity from raw balance response."""
62
- data = balances_raw.get("info", {}).get("data", [{}])[0]
63
- total_eq = data.get("totalEq")
64
- if total_eq is not None:
109
+ """Extract OKX total equity from info.data[0].totalEq."""
110
+ total_eq = self._get_account_data(balances_raw).get("totalEq")
111
+ if total_eq is not None and total_eq != "":
65
112
  try:
66
113
  return float(total_eq)
67
114
  except (ValueError, TypeError):
@@ -81,6 +128,10 @@ class OkxAccountProcessor(CcxtAccountProcessor):
81
128
  if equity is not None:
82
129
  self._exchange_total_capital = equity
83
130
 
131
+ margin_ratio = self._extract_margin_ratio(balances_raw)
132
+ if margin_ratio is not None:
133
+ self._exchange_margin_ratio = margin_ratio
134
+
84
135
  # Extract cashBal from raw OKX response instead of CCXT-parsed eq
85
136
  balances: list[AssetBalance] = []
86
137
  raw_details = balances_raw.get("info", {}).get("data", [{}])[0].get("details", [])
@@ -66,6 +66,7 @@ class BasicAccountProcessor(IAccountProcessor):
66
66
  self._pending_order_requests = {}
67
67
  self._balances = {}
68
68
  self._exchange_total_capital: float | None = None
69
+ self._exchange_margin_ratio: float | None = None
69
70
  # Initialize with base currency balance
70
71
  self._balances[self.base_currency] = AssetBalance(
71
72
  exchange=self.exchange, currency=self.base_currency, free=initial_capital, locked=0.0, total=initial_capital
@@ -183,7 +184,9 @@ class BasicAccountProcessor(IAccountProcessor):
183
184
  return self.get_total_capital(exchange) - self.get_total_required_margin(exchange)
184
185
 
185
186
  def get_margin_ratio(self, exchange: str | None = None) -> float:
186
- # total capital / total required margin
187
+ if self._exchange_margin_ratio is not None:
188
+ return self._exchange_margin_ratio
189
+ # fallback: total capital / total required margin
187
190
  required_margin = self.get_total_required_margin(exchange)
188
191
  if required_margin == 0:
189
192
  return 100.0
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from typing import Any
4
4
 
5
5
  import numpy as np
6
+ import pandas as pd
6
7
 
7
8
  from qubx import logger
8
9
  from qubx.core.basics import (
@@ -100,28 +101,35 @@ class PositionsDumper(_BaseIntervalDumper):
100
101
  self.positions[p.instrument] = p
101
102
  return self
102
103
 
104
+ @staticmethod
105
+ def _position_row(instrument: Instrument, position: Position, timestamp: np.datetime64) -> dict[str, Any]:
106
+ return {
107
+ "timestamp": str(timestamp),
108
+ "symbol": instrument.symbol,
109
+ "exchange": instrument.exchange,
110
+ "market_type": instrument.market_type,
111
+ "pnl_quoted": position.pnl,
112
+ "funding_pnl_quoted": position.cumulative_funding,
113
+ "realized_pnl_quoted": position.r_pnl,
114
+ "quantity": position.quantity,
115
+ "notional": position.notional_value,
116
+ "avg_position_price": position.position_avg_price if position.quantity != 0.0 else 0.0,
117
+ "current_price": position.last_update_price,
118
+ "market_value_quoted": position.market_value_funds,
119
+ "commissions_quoted": position.commissions,
120
+ }
121
+
103
122
  def dump(self, interval_start_time: np.datetime64, actual_timestamp: np.datetime64):
104
- data = []
105
- for i, p in self.positions.items():
106
- data.append(
107
- {
108
- "timestamp": str(actual_timestamp),
109
- "symbol": i.symbol,
110
- "exchange": i.exchange,
111
- "market_type": i.market_type,
112
- "pnl_quoted": p.pnl,
113
- "funding_pnl_quoted": p.cumulative_funding,
114
- "realized_pnl_quoted": p.r_pnl,
115
- "quantity": p.quantity,
116
- "notional": p.notional_value,
117
- "avg_position_price": p.position_avg_price if p.quantity != 0.0 else 0.0,
118
- "current_price": p.last_update_price,
119
- "market_value_quoted": p.market_value_funds,
120
- "commissions_quoted": p.commissions,
121
- }
122
- )
123
+ data = [self._position_row(i, p, actual_timestamp) for i, p in self.positions.items()]
123
124
  self._writer.write_data("positions", data)
124
125
 
126
+ def dump_instrument(self, instrument: Instrument, timestamp: np.datetime64) -> None:
127
+ """Write a single position snapshot row for ``instrument`` (used for on-fill event logging)."""
128
+ position = self.positions.get(instrument)
129
+ if position is None:
130
+ return
131
+ self._writer.write_data("positions", [self._position_row(instrument, position, timestamp)])
132
+
125
133
 
126
134
  class PortfolioLogger(PositionsDumper):
127
135
  """
@@ -343,11 +351,12 @@ class StrategyLogging:
343
351
  def __init__(
344
352
  self,
345
353
  logs_writer: LogsWriter | None = None,
346
- positions_log_freq: str = "1Min",
347
- portfolio_log_freq: str = "5Min",
354
+ positions_log_freq: str | None = "1Min",
355
+ portfolio_log_freq: str | None = "5Min",
348
356
  num_exec_records_to_write=1, # in live let's write every execution
349
357
  num_signals_records_to_write=1,
350
358
  heartbeat_freq: str | None = None,
359
+ positions_log_on_change: bool = False,
351
360
  ) -> None:
352
361
  # - instantiate loggers
353
362
  if logs_writer:
@@ -367,13 +376,16 @@ class StrategyLogging:
367
376
  if num_signals_records_to_write >= 1:
368
377
  self.signals_logger = SignalsAndTargetsLogger(logs_writer, num_signals_records_to_write)
369
378
 
370
- # - balance logger
371
- self.balance_logger = BalanceLogger(logs_writer, positions_log_freq)
379
+ # - balance logger (tracks positions_log_freq when set, otherwise portfolio_log_freq)
380
+ balance_freq = positions_log_freq or portfolio_log_freq
381
+ if balance_freq:
382
+ self.balance_logger = BalanceLogger(logs_writer, balance_freq)
372
383
  else:
373
384
  logger.warning("Log writer is not defined - strategy activity will not be saved !")
374
385
 
375
386
  self.logs_writer = logs_writer
376
387
  self.heartbeat_freq = convert_tf_str_td64(heartbeat_freq) if heartbeat_freq else None
388
+ self.positions_log_on_change = positions_log_on_change
377
389
 
378
390
  def initialize(
379
391
  self,
@@ -431,6 +443,10 @@ class StrategyLogging:
431
443
  def save_deals(self, instrument: Instrument, deals: list[Deal]):
432
444
  if self.executions_logger:
433
445
  self.executions_logger.record_deals(instrument, deals)
446
+ if self.positions_log_on_change and self.positions_dumper and deals:
447
+ latest_deal_time = max(d.time for d in deals)
448
+ ts = latest_deal_time.as_unit("ns").asm8 if isinstance(latest_deal_time, pd.Timestamp) else latest_deal_time
449
+ self.positions_dumper.dump_instrument(instrument, ts)
434
450
 
435
451
  def save_targets(self, targets: list[TargetPosition]):
436
452
  if self.signals_logger and targets:
@@ -8,6 +8,12 @@ NS = 1_000_000_000
8
8
 
9
9
  cpdef recognize_time(time):
10
10
  if isinstance(time, str):
11
+ # Strip timezone suffix (e.g. 'Z', '+00:00') — np.datetime64 expects tz-naive ISO strings.
12
+ # All incoming timestamps are assumed UTC.
13
+ if time.endswith('Z') or time.endswith('z'):
14
+ time = time[:-1]
15
+ elif '+' in time[10:]:
16
+ time = time[:time.rindex('+')]
11
17
  return np.datetime64(time, 'ns')
12
18
  elif isinstance(time, np.datetime64):
13
19
  return time
@@ -106,8 +106,9 @@ class WarmupConfig(StrictBaseModel):
106
106
 
107
107
  class LoggingConfig(StrictBaseModel):
108
108
  logger: str
109
- position_interval: str
110
- portfolio_interval: str
109
+ position_interval: str | None = None
110
+ portfolio_interval: str | None = None
111
+ position_log_on_change: bool = False
111
112
  args: dict = Field(default_factory=dict)
112
113
  heartbeat_interval: str | None = None
113
114
 
@@ -693,6 +693,7 @@ def _setup_strategy_logging(
693
693
  positions_log_freq=log_config.position_interval,
694
694
  portfolio_log_freq=log_config.portfolio_interval,
695
695
  heartbeat_freq=log_config.heartbeat_interval,
696
+ positions_log_on_change=log_config.position_log_on_change,
696
697
  )
697
698
  return stg_logging
698
699
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes