Qubx 0.7.8__tar.gz → 0.7.10__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 (272) hide show
  1. {qubx-0.7.8 → qubx-0.7.10}/PKG-INFO +1 -1
  2. {qubx-0.7.8 → qubx-0.7.10}/pyproject.toml +1 -1
  3. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/client.py +7 -0
  4. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/reader.py +88 -1
  5. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/context.py +1 -2
  6. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/emitters/indicator.py +2 -10
  7. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/ta/indicators.pxd +27 -1
  8. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/ta/indicators.pyi +11 -0
  9. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/ta/indicators.pyx +216 -1
  10. {qubx-0.7.8 → qubx-0.7.10}/LICENSE +0 -0
  11. {qubx-0.7.8 → qubx-0.7.10}/README.md +0 -0
  12. {qubx-0.7.8 → qubx-0.7.10}/build.py +0 -0
  13. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/__init__.py +0 -0
  14. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/_nb_magic.py +0 -0
  15. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/__init__.py +0 -0
  16. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/account.py +0 -0
  17. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/broker.py +0 -0
  18. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/data.py +0 -0
  19. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/management.py +0 -0
  20. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/ome.py +0 -0
  21. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/optimization.py +0 -0
  22. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/runner.py +0 -0
  23. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/sentinels.py +0 -0
  24. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/simulated_data.py +0 -0
  25. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/simulated_exchange.py +0 -0
  26. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/simulator.py +0 -0
  27. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/transfers.py +0 -0
  28. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/backtester/utils.py +0 -0
  29. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/cli/__init__.py +0 -0
  30. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/cli/commands.py +0 -0
  31. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/cli/deploy.py +0 -0
  32. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/cli/misc.py +0 -0
  33. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/cli/release.py +0 -0
  34. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/cli/tui.py +0 -0
  35. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/__init__.py +0 -0
  36. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/account.py +0 -0
  37. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/adapters/__init__.py +0 -0
  38. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/adapters/polling_adapter.py +0 -0
  39. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/broker.py +0 -0
  40. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/connection_manager.py +0 -0
  41. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/data.py +0 -0
  42. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  43. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchange_manager.py +0 -0
  44. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
  45. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/base.py +0 -0
  46. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
  47. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
  48. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
  49. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
  50. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py +0 -0
  51. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/hyperliquid/account.py +0 -0
  52. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/hyperliquid/broker.py +0 -0
  53. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py +0 -0
  54. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
  55. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/factory.py +0 -0
  56. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/handlers/__init__.py +0 -0
  57. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/handlers/base.py +0 -0
  58. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/handlers/factory.py +0 -0
  59. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/handlers/funding_rate.py +0 -0
  60. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/handlers/liquidation.py +0 -0
  61. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/handlers/ohlc.py +0 -0
  62. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/handlers/open_interest.py +0 -0
  63. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/handlers/orderbook.py +0 -0
  64. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/handlers/quote.py +0 -0
  65. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/handlers/trade.py +0 -0
  66. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/reader.py +0 -0
  67. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/subscription_config.py +0 -0
  68. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/subscription_manager.py +0 -0
  69. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/subscription_orchestrator.py +0 -0
  70. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/utils.py +0 -0
  71. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/ccxt/warmup_service.py +0 -0
  72. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/tardis/data.py +0 -0
  73. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/tardis/utils.py +0 -0
  74. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/__init__.py +0 -0
  75. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/account.py +0 -0
  76. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/broker.py +0 -0
  77. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/constants.py +0 -0
  78. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/data.py +0 -0
  79. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/extensions.py +0 -0
  80. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/factory.py +0 -0
  81. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/handlers/__init__.py +0 -0
  82. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/handlers/base.py +0 -0
  83. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/handlers/orderbook.py +0 -0
  84. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/handlers/quote.py +0 -0
  85. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/handlers/stats.py +0 -0
  86. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/handlers/trades.py +0 -0
  87. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/instruments.py +0 -0
  88. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/nonce.py +0 -0
  89. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/parsers.py +0 -0
  90. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/rate_limits.py +0 -0
  91. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/utils.py +0 -0
  92. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/connectors/xlighter/websocket.py +0 -0
  93. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/__init__.py +0 -0
  94. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/account.py +0 -0
  95. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/basics.py +0 -0
  96. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/deque.py +0 -0
  97. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/detectors/__init__.py +0 -0
  98. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/detectors/delisting.py +0 -0
  99. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/detectors/stale.py +0 -0
  100. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/errors.py +0 -0
  101. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/exceptions.py +0 -0
  102. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/helpers.py +0 -0
  103. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/initializer.py +0 -0
  104. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/interfaces.py +0 -0
  105. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/loggers.py +0 -0
  106. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/lookups.py +0 -0
  107. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/metrics.py +0 -0
  108. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/mixins/__init__.py +0 -0
  109. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/mixins/market.py +0 -0
  110. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/mixins/processing.py +0 -0
  111. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/mixins/subscription.py +0 -0
  112. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/mixins/trading.py +0 -0
  113. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/mixins/universe.py +0 -0
  114. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/mixins/utils.py +0 -0
  115. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/series.pxd +0 -0
  116. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/series.pyi +0 -0
  117. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/series.pyx +0 -0
  118. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/utils.pyi +0 -0
  119. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/core/utils.pyx +0 -0
  120. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/__init__.py +0 -0
  121. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/composite.py +0 -0
  122. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/containers.py +0 -0
  123. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/helpers.py +0 -0
  124. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/hft.py +0 -0
  125. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/readers.py +0 -0
  126. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/registry.py +0 -0
  127. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/storage.py +0 -0
  128. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/storages/csv.py +0 -0
  129. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/storages/questdb.py +0 -0
  130. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/storages/utils.py +0 -0
  131. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/tardis.py +0 -0
  132. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/data/transformers.py +0 -0
  133. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/emitters/__init__.py +0 -0
  134. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/emitters/base.py +0 -0
  135. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/emitters/composite.py +0 -0
  136. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/emitters/csv.py +0 -0
  137. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/emitters/inmemory.py +0 -0
  138. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/emitters/prometheus.py +0 -0
  139. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/emitters/questdb.py +0 -0
  140. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/exporters/__init__.py +0 -0
  141. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/exporters/composite.py +0 -0
  142. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/exporters/formatters/__init__.py +0 -0
  143. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/exporters/formatters/base.py +0 -0
  144. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/exporters/formatters/incremental.py +0 -0
  145. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/exporters/formatters/slack.py +0 -0
  146. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/exporters/formatters/target_position.py +0 -0
  147. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/exporters/redis_streams.py +0 -0
  148. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/exporters/slack.py +0 -0
  149. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/features/__init__.py +0 -0
  150. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/features/core.py +0 -0
  151. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/features/orderbook.py +0 -0
  152. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/features/price.py +0 -0
  153. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/features/trades.py +0 -0
  154. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/features/utils.py +0 -0
  155. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/gathering/simplest.py +0 -0
  156. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/health/__init__.py +0 -0
  157. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/health/base.py +0 -0
  158. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/loggers/__init__.py +0 -0
  159. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/loggers/csv.py +0 -0
  160. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/loggers/factory.py +0 -0
  161. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/loggers/inmemory.py +0 -0
  162. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/loggers/mongo.py +0 -0
  163. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/math/__init__.py +0 -0
  164. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/math/stats.py +0 -0
  165. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/notifications/__init__.py +0 -0
  166. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/notifications/composite.py +0 -0
  167. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/notifications/slack.py +0 -0
  168. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/notifications/throttler.py +0 -0
  169. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/pandaz/__init__.py +0 -0
  170. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/pandaz/ta.py +0 -0
  171. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/pandaz/utils.py +0 -0
  172. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/_build.py +0 -0
  173. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/crypto-fees.ini +0 -0
  174. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/hyperliquid-spot.json +0 -0
  175. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/hyperliquid.f-perpetual.json +0 -0
  176. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/symbols-binance-spot.json +0 -0
  177. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/symbols-binance.cm-future.json +0 -0
  178. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/symbols-binance.cm-perpetual.json +0 -0
  179. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/symbols-binance.um-future.json +0 -0
  180. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/symbols-binance.um-perpetual.json +0 -0
  181. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/symbols-bitfinex.f-perpetual.json +0 -0
  182. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/symbols-kraken-spot.json +0 -0
  183. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/symbols-kraken.f-future.json +0 -0
  184. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/resources/instruments/symbols-kraken.f-perpetual.json +0 -0
  185. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restarts/__init__.py +0 -0
  186. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restarts/state_resolvers.py +0 -0
  187. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restarts/time_finders.py +0 -0
  188. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restorers/__init__.py +0 -0
  189. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restorers/balance.py +0 -0
  190. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restorers/factory.py +0 -0
  191. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restorers/interfaces.py +0 -0
  192. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restorers/position.py +0 -0
  193. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restorers/signal.py +0 -0
  194. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restorers/state.py +0 -0
  195. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/restorers/utils.py +0 -0
  196. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/ta/__init__.py +0 -0
  197. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/__init__.py +0 -0
  198. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/base.py +0 -0
  199. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/project/accounts.toml.j2 +0 -0
  200. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/project/config.yml.j2 +0 -0
  201. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/project/jlive.sh.j2 +0 -0
  202. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/project/jpaper.sh.j2 +0 -0
  203. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/project/pyproject.toml.j2 +0 -0
  204. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/project/src/{{ strategy_name }}/__init__.py.j2 +0 -0
  205. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/project/src/{{ strategy_name }}/strategy.py.j2 +0 -0
  206. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/project/template.yml +0 -0
  207. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/simple/__init__.py.j2 +0 -0
  208. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/simple/accounts.toml.j2 +0 -0
  209. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/simple/config.yml.j2 +0 -0
  210. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/simple/jlive.sh.j2 +0 -0
  211. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/simple/jpaper.sh.j2 +0 -0
  212. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/simple/strategy.py.j2 +0 -0
  213. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/templates/simple/template.yml +0 -0
  214. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/trackers/__init__.py +0 -0
  215. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/trackers/advanced.py +0 -0
  216. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/trackers/composite.py +0 -0
  217. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/trackers/rebalancers.py +0 -0
  218. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/trackers/riskctrl.py +0 -0
  219. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/trackers/sizers.py +0 -0
  220. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/__init__.py +0 -0
  221. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/_pyxreloader.py +0 -0
  222. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/charting/lookinglass.py +0 -0
  223. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  224. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/charting/orderbook.py +0 -0
  225. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/collections.py +0 -0
  226. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/hft/__init__.py +0 -0
  227. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/hft/numba_utils.py +0 -0
  228. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/hft/orderbook.pyi +0 -0
  229. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/hft/orderbook.pyx +0 -0
  230. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/marketdata/binance.py +0 -0
  231. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/marketdata/ccxt.py +0 -0
  232. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/marketdata/dukas.py +0 -0
  233. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/misc.py +0 -0
  234. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/nonce.py +0 -0
  235. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/ntp.py +0 -0
  236. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/numbers_utils.py +0 -0
  237. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/orderbook.py +0 -0
  238. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/plotting/__init__.py +0 -0
  239. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/plotting/dashboard.py +0 -0
  240. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/plotting/data.py +0 -0
  241. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/plotting/interfaces.py +0 -0
  242. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  243. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  244. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/questdb.py +0 -0
  245. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/rate_limiter.py +0 -0
  246. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/ringbuffer.pxd +0 -0
  247. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/ringbuffer.pyi +0 -0
  248. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/ringbuffer.pyx +0 -0
  249. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/__init__.py +0 -0
  250. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
  251. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/accounts.py +0 -0
  252. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/configs.py +0 -0
  253. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/factory.py +0 -0
  254. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/kernel_service.py +0 -0
  255. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/runner.py +0 -0
  256. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/__init__.py +0 -0
  257. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/app.py +0 -0
  258. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/handlers.py +0 -0
  259. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/init_code.py +0 -0
  260. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/kernel.py +0 -0
  261. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/styles.tcss +0 -0
  262. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/widgets/__init__.py +0 -0
  263. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/widgets/command_input.py +0 -0
  264. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/widgets/debug_log.py +0 -0
  265. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/widgets/orders_table.py +0 -0
  266. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/widgets/positions_table.py +0 -0
  267. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/widgets/quotes_table.py +0 -0
  268. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/runner/textual/widgets/repl_output.py +0 -0
  269. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/slack.py +0 -0
  270. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/time.py +0 -0
  271. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/version.py +0 -0
  272. {qubx-0.7.8 → qubx-0.7.10}/src/qubx/utils/websocket_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Qubx
3
- Version: 0.7.8
3
+ Version: 0.7.10
4
4
  Summary: Qubx - Quantitative Trading Framework
5
5
  License-File: LICENSE
6
6
  Author: Dmitry Marienko
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "Qubx"
7
- version = "0.7.8"
7
+ version = "0.7.10"
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"
@@ -255,6 +255,13 @@ class LighterClient:
255
255
  resolution_ms = self._resolution_to_milliseconds(resolution)
256
256
  start_timestamp = end_timestamp - (count_back * resolution_ms)
257
257
 
258
+ start_td = cast(pd.Timestamp, pd.Timestamp(start_timestamp, unit="ms"))
259
+ end_td = cast(pd.Timestamp, pd.Timestamp(end_timestamp, unit="ms"))
260
+ tf = pd.Timedelta(resolution)
261
+ if start_td + tf > end_td:
262
+ start_td = end_td - tf
263
+ start_timestamp = int(start_td.timestamp() * 1000) # type: ignore
264
+
258
265
  start_timestamp_str = (
259
266
  cast(pd.Timestamp, pd.Timestamp(start_timestamp, unit="ms")).strftime("%Y-%m-%d %H:%M:%S")
260
267
  if start_timestamp is not None
@@ -262,7 +262,8 @@ class XLighterDataReader(DataReader):
262
262
  max_history_start = stop_ts - self._max_history
263
263
  if start_ts < max_history_start:
264
264
  logger.debug(
265
- f"Adjusting start time from {start_ts} to {max_history_start} due to max_history={self._max_history}"
265
+ f"Adjusting start time from {start_ts} to {max_history_start} "
266
+ f"due to max_history={self._max_history}"
266
267
  )
267
268
  start_ts = max_history_start
268
269
 
@@ -337,6 +338,92 @@ class XLighterDataReader(DataReader):
337
338
  logger.info(f"Fetched {len(df)} funding payment records for {len(instruments_to_fetch)} symbols")
338
339
  return df
339
340
 
341
+ def get_candles(
342
+ self,
343
+ exchange: str,
344
+ symbols: list[str] | None = None,
345
+ start: str | pd.Timestamp | None = None,
346
+ stop: str | pd.Timestamp | None = None,
347
+ timeframe: str = "1d",
348
+ ) -> pd.DataFrame:
349
+ """
350
+ Get candlestick data for symbols within specified time range.
351
+
352
+ Args:
353
+ exchange: Exchange name (should be "LIGHTER")
354
+ symbols: List of symbols in Qubx format (e.g., ["BTCUSDC", "ETHUSDC"]). If None, fetches all symbols.
355
+ start: Start time (ISO format or timestamp)
356
+ stop: Stop time (ISO format or timestamp)
357
+ timeframe: Timeframe for candles (e.g., "1m", "5m", "1h", "1d")
358
+
359
+ Returns:
360
+ DataFrame with MultiIndex [timestamp, symbol] and columns:
361
+ - open: Opening price
362
+ - high: Highest price
363
+ - low: Lowest price
364
+ - close: Closing price
365
+ - volume: Trading volume
366
+ """
367
+ if exchange.upper() != "LIGHTER":
368
+ logger.warning(f"Exchange {exchange} not supported by XLighterDataReader")
369
+ return pd.DataFrame(columns=["open", "high", "low", "close", "volume"])
370
+
371
+ # Handle time range
372
+ start_ts = pd.Timestamp(start) if start else pd.Timestamp.now() - pd.Timedelta(days=7)
373
+ stop_ts = pd.Timestamp(stop) if stop else pd.Timestamp.now()
374
+
375
+ # Apply max_history limitation
376
+ if self._max_history:
377
+ max_history_start = stop_ts - self._max_history
378
+ if start_ts < max_history_start:
379
+ logger.debug(
380
+ f"Adjusting start time from {start_ts} to {max_history_start} "
381
+ f"due to max_history={self._max_history}"
382
+ )
383
+ start_ts = max_history_start
384
+
385
+ # Get instruments to fetch
386
+ instruments_to_fetch = self._get_instruments_for_symbols(symbols)
387
+ if not instruments_to_fetch:
388
+ logger.warning("No instruments found for the specified symbols")
389
+ return pd.DataFrame(columns=["open", "high", "low", "close", "volume"])
390
+
391
+ # Fetch candle data for each instrument
392
+ all_candle_data = []
393
+
394
+ for instrument in instruments_to_fetch:
395
+ try:
396
+ # Fetch OHLCV data using existing method
397
+ ohlcv_list = self._fetch_ohlcv(instrument, timeframe, start_ts, stop_ts)
398
+
399
+ if not ohlcv_list:
400
+ logger.debug(f"No candle data found for {instrument.symbol}")
401
+ continue
402
+
403
+ # Convert to DataFrame
404
+ df = pd.DataFrame(
405
+ ohlcv_list,
406
+ columns=["timestamp", "open", "high", "low", "close", "volume"]
407
+ )
408
+ df["symbol"] = instrument.symbol
409
+ all_candle_data.append(df)
410
+
411
+ except Exception as e:
412
+ logger.error(f"Failed to fetch candle data for {instrument.symbol}: {e}")
413
+ continue
414
+
415
+ if not all_candle_data:
416
+ logger.info("No candle data found")
417
+ return pd.DataFrame(columns=["open", "high", "low", "close", "volume"])
418
+
419
+ # Combine all DataFrames
420
+ combined_df = pd.concat(all_candle_data, ignore_index=True)
421
+ combined_df = combined_df.sort_values("timestamp")
422
+ combined_df = combined_df.set_index(["timestamp", "symbol"])
423
+
424
+ logger.info(f"Fetched {len(combined_df)} candle records for {len(instruments_to_fetch)} symbols")
425
+ return combined_df
426
+
340
427
  def _get_instrument(self, data_id: str) -> Instrument | None:
341
428
  """
342
429
  Get instrument from data ID.
@@ -365,7 +365,6 @@ class StrategyContext(IStrategyContext):
365
365
  {
366
366
  "Exchanges": "|".join(self.exchanges),
367
367
  "Total Capital": f"${self.get_total_capital():,.0f}",
368
- "Net Leverage": f"{self.get_net_leverage():.1%}",
369
368
  "Open Positions": len(open_positions),
370
369
  "Instruments": len(self._initial_instruments),
371
370
  },
@@ -414,7 +413,7 @@ class StrategyContext(IStrategyContext):
414
413
  try:
415
414
  self._notifier.notify_stop(
416
415
  {
417
- "Total Capital": f"{self.get_total_capital():.2f}",
416
+ "Total Capital": f"{self.get_total_capital():,.0f}",
418
417
  "Net Leverage": f"{self.get_net_leverage():.2%}",
419
418
  "Positions": len([p for i, p in self.get_positions().items() if abs(p.quantity) > i.min_size]),
420
419
  },
@@ -75,10 +75,6 @@ class IndicatorEmitter(Indicator):
75
75
  # Since Indicator extends TimeSeries, we can pass the wrapped_indicator directly
76
76
  super().__init__(name, wrapped_indicator)
77
77
 
78
- logger.debug(
79
- f"[IndicatorEmitter] Created emitter '{name}' wrapping '{wrapped_indicator.name}' -> metric '{self._metric_name}'"
80
- )
81
-
82
78
  def calculate(self, time: int, value: float, new_item_started: bool) -> float:
83
79
  """
84
80
  Calculate method that handles the emission logic.
@@ -117,10 +113,10 @@ class IndicatorEmitter(Indicator):
117
113
  # Emit the metric with the proper timestamp
118
114
  # Convert time to numpy datetime64 if needed
119
115
  if isinstance(time, int):
120
- timestamp = np.datetime64(time, 'ns')
116
+ timestamp = np.datetime64(time, "ns")
121
117
  else:
122
118
  timestamp = pd.Timestamp(time).to_datetime64()
123
-
119
+
124
120
  self._metric_emitter.emit(
125
121
  name=self._metric_name,
126
122
  value=float(current_value),
@@ -130,10 +126,6 @@ class IndicatorEmitter(Indicator):
130
126
  )
131
127
 
132
128
  if not self._has_emitted:
133
- logger.debug(
134
- f"[IndicatorEmitter] '{self.name}' started emitting '{self._metric_name}' "
135
- f"values from '{self._wrapped_indicator.name}'"
136
- )
137
129
  self._has_emitted = True
138
130
 
139
131
  except Exception as e:
@@ -223,4 +223,30 @@ cdef class Macd(Indicator):
223
223
  cdef object macd_line_series
224
224
  cdef object signal_line
225
225
 
226
- cpdef double calculate(self, long long time, double value, short new_item_started)
226
+ cpdef double calculate(self, long long time, double value, short new_item_started)
227
+
228
+ cdef class SuperTrend(IndicatorOHLC):
229
+ cdef int length
230
+ cdef double mult
231
+ cdef str src
232
+ cdef short wicks
233
+ cdef str atr_smoother
234
+
235
+ cdef double _prev_longstop
236
+ cdef double _prev_shortstop
237
+ cdef double _prev_direction
238
+
239
+ cdef double prev_longstop
240
+ cdef double prev_shortstop
241
+ cdef double prev_direction
242
+
243
+ cdef TimeSeries tr
244
+ cdef object atr_ma
245
+ cdef public TimeSeries utl
246
+ cdef public TimeSeries dtl
247
+
248
+ cdef _store(self)
249
+ cdef _restore(self)
250
+ cdef double calc_src(self, Bar bar)
251
+
252
+ cpdef double calculate(self, long long time, Bar bar, short new_item_started)
@@ -24,6 +24,7 @@ def rsi(series: TimeSeries, period: int = 13, smoother="ema") -> Indicator: ...
24
24
  def stdema(series: TimeSeries, period: int) -> Indicator: ...
25
25
  def cusum_filter(series: TimeSeries, target: TimeSeries) -> Indicator: ...
26
26
  def macd(series: TimeSeries, fast=12, slow=26, signal=9, method="ema", signal_method="ema") -> Indicator: ...
27
+ def super_trend(series: OHLCV, length: int = 22, mult: float = 3.0, src: str = "hl2", wicks: bool = True, atr_smoother: str = "sma") -> Indicator: ...
27
28
 
28
29
  class Sma(Indicator):
29
30
  def __init__(self, name: str, series: TimeSeries, period: int): ...
@@ -84,3 +85,13 @@ class Macd(Indicator):
84
85
  def __init__(
85
86
  self, name: str, series: TimeSeries, fast: int, slow: int, signal: int, method: str, signal_method: str
86
87
  ): ...
88
+
89
+ class SuperTrend(IndicatorOHLC):
90
+ utl: TimeSeries
91
+ dtl: TimeSeries
92
+ length: int
93
+ mult: float
94
+ src: str
95
+ wicks: bool
96
+ atr_smoother: str
97
+ def __init__(self, name: str, series: OHLCV, length: int, mult: float, src: str, wicks: bool, atr_smoother: str): ...
@@ -1553,4 +1553,219 @@ def macd(series: TimeSeries, fast=12, slow=26, signal=9, method="ema", signal_me
1553
1553
  :param signal_method: used method for averaging signal (sma, ema, tema, dema, kama)
1554
1554
  :return: macd indicator
1555
1555
  """
1556
- return Macd.wrap(series, fast, slow, signal, method, signal_method) # type: ignore
1556
+ return Macd.wrap(series, fast, slow, signal, method, signal_method) # type: ignore
1557
+
1558
+
1559
+ cdef class SuperTrend(IndicatorOHLC):
1560
+ """
1561
+ SuperTrend indicator - a trend-following indicator based on ATR
1562
+
1563
+ The SuperTrend indicator provides trend direction and support/resistance levels.
1564
+ It uses Average True Range (ATR) to calculate dynamic bands around price.
1565
+
1566
+ Returns:
1567
+ trend: +1 for uptrend, -1 for downtrend
1568
+ utl (upper trend line): longstop values during uptrend
1569
+ dtl (down trend line): shortstop values during downtrend
1570
+ """
1571
+
1572
+ def __init__(self, str name, OHLCV series, int length, double mult, str src, short wicks, str atr_smoother):
1573
+ self.length = length
1574
+ self.mult = mult
1575
+ self.src = src
1576
+ self.wicks = wicks
1577
+ self.atr_smoother = atr_smoother
1578
+
1579
+ # - working state variables (updated during calculation)
1580
+ self._prev_longstop = np.nan
1581
+ self._prev_shortstop = np.nan
1582
+ self._prev_direction = np.nan
1583
+
1584
+ # - saved state variables (for handling partial bar updates)
1585
+ self.prev_longstop = np.nan
1586
+ self.prev_shortstop = np.nan
1587
+ self.prev_direction = np.nan
1588
+
1589
+ # - create internal TR series and smooth it for ATR calculation
1590
+ self.tr = TimeSeries("tr", series.timeframe, series.max_series_length)
1591
+ self.atr_ma = smooth(self.tr, atr_smoother, length)
1592
+
1593
+ # - create output series for upper and down trend lines
1594
+ self.utl = TimeSeries("utl", series.timeframe, series.max_series_length)
1595
+ self.dtl = TimeSeries("dtl", series.timeframe, series.max_series_length)
1596
+
1597
+ super().__init__(name, series)
1598
+
1599
+ cdef _store(self):
1600
+ """Store current working state to saved state"""
1601
+ self.prev_longstop = self._prev_longstop
1602
+ self.prev_shortstop = self._prev_shortstop
1603
+ self.prev_direction = self._prev_direction
1604
+
1605
+ cdef _restore(self):
1606
+ """Restore saved state to working state"""
1607
+ self._prev_longstop = self.prev_longstop
1608
+ self._prev_shortstop = self.prev_shortstop
1609
+ self._prev_direction = self.prev_direction
1610
+
1611
+ cdef double calc_src(self, Bar bar):
1612
+ """Calculate source value based on src parameter"""
1613
+ if self.src == "close":
1614
+ return bar.close
1615
+ elif self.src == "hl2":
1616
+ return (bar.high + bar.low) / 2.0
1617
+ elif self.src == "hlc3":
1618
+ return (bar.high + bar.low + bar.close) / 3.0
1619
+ elif self.src == "ohlc4":
1620
+ return (bar.open + bar.high + bar.low + bar.close) / 4.0
1621
+ else:
1622
+ return (bar.high + bar.low) / 2.0 # - default to hl2
1623
+
1624
+ cpdef double calculate(self, long long time, Bar bar, short new_item_started):
1625
+ cdef double atr_value, src_value, longstop, shortstop
1626
+ cdef double high_price, low_price, p_high_price, p_low_price
1627
+ cdef short is_doji4price
1628
+ cdef double direction
1629
+ cdef double saved_prev_longstop, saved_prev_shortstop
1630
+ cdef double tr_value
1631
+
1632
+ # - need at least 2 bars for prev calculations
1633
+ if len(self.series) < 2:
1634
+ # - initialize on first bar
1635
+ self._prev_longstop = np.nan
1636
+ self._prev_shortstop = np.nan
1637
+ self._prev_direction = np.nan
1638
+ self._store()
1639
+ return np.nan
1640
+
1641
+ # - handle store/restore for partial bar updates
1642
+ if new_item_started:
1643
+ self._store()
1644
+ else:
1645
+ self._restore()
1646
+
1647
+ # - calculate True Range
1648
+ cdef Bar prev_bar = self.series[1]
1649
+ cdef double c1 = prev_bar.close
1650
+ cdef double h_l = abs(bar.high - bar.low)
1651
+ cdef double h_pc = abs(bar.high - c1)
1652
+ cdef double l_pc = abs(bar.low - c1)
1653
+ tr_value = max(h_l, h_pc, l_pc)
1654
+
1655
+ # - update TR series (this will automatically update ATR via smooth)
1656
+ self.tr.update(time, tr_value)
1657
+
1658
+ # - get ATR value
1659
+ atr_value = self.atr_ma[0]
1660
+ if np.isnan(atr_value):
1661
+ return np.nan
1662
+
1663
+ atr_value = abs(self.mult) * atr_value
1664
+
1665
+ # - calculate source value
1666
+ src_value = self.calc_src(bar)
1667
+
1668
+ # - determine which prices to use for wicks
1669
+ if self.wicks:
1670
+ high_price = bar.high
1671
+ low_price = bar.low
1672
+ else:
1673
+ high_price = bar.close
1674
+ low_price = bar.close
1675
+
1676
+ # - get previous bar's prices (prev_bar already retrieved above for TR)
1677
+ if self.wicks:
1678
+ p_high_price = prev_bar.high
1679
+ p_low_price = prev_bar.low
1680
+ else:
1681
+ p_high_price = prev_bar.close
1682
+ p_low_price = prev_bar.close
1683
+
1684
+ # - check for doji4price (all prices equal)
1685
+ is_doji4price = (bar.open == bar.close) and (bar.open == bar.low) and (bar.open == bar.high)
1686
+
1687
+ # - calculate basic stops
1688
+ longstop = src_value - atr_value
1689
+ shortstop = src_value + atr_value
1690
+
1691
+ # - save previous bar's stops for direction comparison
1692
+ saved_prev_longstop = self._prev_longstop
1693
+ saved_prev_shortstop = self._prev_shortstop
1694
+
1695
+ # - adjust longstop based on previous value
1696
+ if np.isnan(self._prev_longstop):
1697
+ self._prev_longstop = longstop
1698
+
1699
+ if longstop > 0:
1700
+ if is_doji4price:
1701
+ longstop = self._prev_longstop
1702
+ else:
1703
+ if p_low_price > self._prev_longstop:
1704
+ longstop = max(longstop, self._prev_longstop)
1705
+ # - else: keep calculated longstop
1706
+ else:
1707
+ longstop = self._prev_longstop
1708
+
1709
+ # - adjust shortstop based on previous value
1710
+ if np.isnan(self._prev_shortstop):
1711
+ self._prev_shortstop = shortstop
1712
+
1713
+ if shortstop > 0:
1714
+ if is_doji4price:
1715
+ shortstop = self._prev_shortstop
1716
+ else:
1717
+ if p_high_price < self._prev_shortstop:
1718
+ shortstop = min(shortstop, self._prev_shortstop)
1719
+ # - else: keep calculated shortstop
1720
+ else:
1721
+ shortstop = self._prev_shortstop
1722
+
1723
+ # - determine direction based on price breaking PREVIOUS bar's stops
1724
+ # - only check for breaks if we have valid previous stops
1725
+ if np.isnan(saved_prev_longstop) or np.isnan(saved_prev_shortstop):
1726
+ # - not enough data yet, forward fill previous direction or return NaN
1727
+ direction = self._prev_direction if not np.isnan(self._prev_direction) else np.nan
1728
+ elif low_price < saved_prev_longstop:
1729
+ direction = -1.0
1730
+ elif high_price > saved_prev_shortstop:
1731
+ direction = 1.0
1732
+ else:
1733
+ # - no break, keep previous direction (forward fill)
1734
+ direction = self._prev_direction if not np.isnan(self._prev_direction) else np.nan
1735
+
1736
+ # - update working state for next iteration
1737
+ self._prev_longstop = longstop
1738
+ self._prev_shortstop = shortstop
1739
+ self._prev_direction = direction
1740
+
1741
+ # - update utl and dtl series based on direction
1742
+ # - only update when we have a valid direction
1743
+ if direction == 1.0:
1744
+ self.utl.update(time, longstop)
1745
+ elif direction == -1.0:
1746
+ self.dtl.update(time, shortstop)
1747
+
1748
+ # - return trend direction
1749
+ return direction
1750
+
1751
+
1752
+ def super_trend(series: OHLCV, length: int = 22, mult: float = 3.0, src: str = "hl2",
1753
+ wicks: bool = True, atr_smoother: str = "sma"):
1754
+ """
1755
+ SuperTrend indicator - a trend-following indicator based on ATR
1756
+
1757
+ The SuperTrend indicator uses Average True Range (ATR) to calculate dynamic support
1758
+ and resistance levels. It provides clear trend direction and can be used for
1759
+ entry/exit signals.
1760
+
1761
+ :param series: OHLCV input series
1762
+ :param length: ATR period (default 22)
1763
+ :param mult: ATR multiplier (default 3.0)
1764
+ :param src: source calculation - 'close', 'hl2', 'hlc3', 'ohlc4' (default 'hl2')
1765
+ :param wicks: whether to use high/low (True) or close (False) for calculations (default True)
1766
+ :param atr_smoother: smoothing method for ATR - 'sma', 'ema', etc (default 'sma')
1767
+ :return: SuperTrend indicator with trend, utl, and dtl series
1768
+ """
1769
+ if not isinstance(series, OHLCV):
1770
+ raise ValueError("Series must be OHLCV !")
1771
+ return SuperTrend.wrap(series, length, mult, src, wicks, atr_smoother) # type: ignore
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes