Qubx 0.6.43__tar.gz → 0.6.47__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 (164) hide show
  1. {qubx-0.6.43 → qubx-0.6.47}/PKG-INFO +3 -2
  2. {qubx-0.6.43 → qubx-0.6.47}/pyproject.toml +3 -2
  3. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/utils.py +1 -1
  4. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/context.py +3 -0
  5. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/initializer.py +7 -0
  6. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/interfaces.py +18 -0
  7. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/lookups.py +2 -1
  8. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/metrics.py +2 -2
  9. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/pandaz/ta.py +3 -3
  10. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/pandaz/utils.py +15 -0
  11. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restorers/balance.py +33 -18
  12. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restorers/position.py +34 -15
  13. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restorers/signal.py +30 -7
  14. {qubx-0.6.43 → qubx-0.6.47}/LICENSE +0 -0
  15. {qubx-0.6.43 → qubx-0.6.47}/README.md +0 -0
  16. {qubx-0.6.43 → qubx-0.6.47}/build.py +0 -0
  17. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/__init__.py +0 -0
  18. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/_nb_magic.py +0 -0
  19. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/__init__.py +0 -0
  20. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/account.py +0 -0
  21. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/broker.py +0 -0
  22. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/data.py +0 -0
  23. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/management.py +0 -0
  24. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/ome.py +0 -0
  25. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/optimization.py +0 -0
  26. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/runner.py +0 -0
  27. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/simulated_data.py +0 -0
  28. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/simulated_exchange.py +0 -0
  29. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/backtester/simulator.py +0 -0
  30. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/cli/__init__.py +0 -0
  31. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/cli/commands.py +0 -0
  32. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/cli/deploy.py +0 -0
  33. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/cli/misc.py +0 -0
  34. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/cli/release.py +0 -0
  35. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/__init__.py +0 -0
  36. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/account.py +0 -0
  37. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/broker.py +0 -0
  38. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/data.py +0 -0
  39. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/exceptions.py +0 -0
  40. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/exchanges/__init__.py +0 -0
  41. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/exchanges/binance/broker.py +0 -0
  42. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/exchanges/binance/exchange.py +0 -0
  43. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py +0 -0
  44. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py +0 -0
  45. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/exchanges/kraken/kraken.py +0 -0
  46. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/factory.py +0 -0
  47. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/reader.py +0 -0
  48. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/ccxt/utils.py +0 -0
  49. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/tardis/data.py +0 -0
  50. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/connectors/tardis/utils.py +0 -0
  51. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/__init__.py +0 -0
  52. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/account.py +0 -0
  53. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/basics.py +0 -0
  54. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/deque.py +0 -0
  55. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/errors.py +0 -0
  56. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/exceptions.py +0 -0
  57. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/helpers.py +0 -0
  58. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/loggers.py +0 -0
  59. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/mixins/__init__.py +0 -0
  60. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/mixins/market.py +0 -0
  61. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/mixins/processing.py +0 -0
  62. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/mixins/subscription.py +0 -0
  63. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/mixins/trading.py +0 -0
  64. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/mixins/universe.py +0 -0
  65. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/series.pxd +0 -0
  66. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/series.pyi +0 -0
  67. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/series.pyx +0 -0
  68. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/utils.pyi +0 -0
  69. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/core/utils.pyx +0 -0
  70. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/data/__init__.py +0 -0
  71. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/data/composite.py +0 -0
  72. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/data/helpers.py +0 -0
  73. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/data/hft.py +0 -0
  74. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/data/readers.py +0 -0
  75. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/data/registry.py +0 -0
  76. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/data/tardis.py +0 -0
  77. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/emitters/__init__.py +0 -0
  78. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/emitters/base.py +0 -0
  79. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/emitters/composite.py +0 -0
  80. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/emitters/csv.py +0 -0
  81. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/emitters/prometheus.py +0 -0
  82. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/emitters/questdb.py +0 -0
  83. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/exporters/__init__.py +0 -0
  84. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/exporters/composite.py +0 -0
  85. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/exporters/formatters/__init__.py +0 -0
  86. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/exporters/formatters/base.py +0 -0
  87. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/exporters/formatters/incremental.py +0 -0
  88. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/exporters/formatters/slack.py +0 -0
  89. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/exporters/redis_streams.py +0 -0
  90. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/exporters/slack.py +0 -0
  91. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/features/__init__.py +0 -0
  92. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/features/core.py +0 -0
  93. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/features/orderbook.py +0 -0
  94. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/features/price.py +0 -0
  95. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/features/trades.py +0 -0
  96. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/features/utils.py +0 -0
  97. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/gathering/simplest.py +0 -0
  98. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/health/__init__.py +0 -0
  99. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/health/base.py +0 -0
  100. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/loggers/__init__.py +0 -0
  101. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/loggers/csv.py +0 -0
  102. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/loggers/factory.py +0 -0
  103. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/loggers/inmemory.py +0 -0
  104. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/loggers/mongo.py +0 -0
  105. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/math/__init__.py +0 -0
  106. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/math/stats.py +0 -0
  107. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/notifications/__init__.py +0 -0
  108. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/notifications/composite.py +0 -0
  109. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/notifications/slack.py +0 -0
  110. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/notifications/throttler.py +0 -0
  111. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/pandaz/__init__.py +0 -0
  112. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/resources/_build.py +0 -0
  113. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/resources/instruments/symbols-binance.cm.json +0 -0
  114. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/resources/instruments/symbols-binance.json +0 -0
  115. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/resources/instruments/symbols-binance.um.json +0 -0
  116. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/resources/instruments/symbols-bitfinex.f.json +0 -0
  117. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/resources/instruments/symbols-bitfinex.json +0 -0
  118. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/resources/instruments/symbols-kraken.f.json +0 -0
  119. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/resources/instruments/symbols-kraken.json +0 -0
  120. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restarts/__init__.py +0 -0
  121. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restarts/state_resolvers.py +0 -0
  122. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restarts/time_finders.py +0 -0
  123. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restorers/__init__.py +0 -0
  124. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restorers/factory.py +0 -0
  125. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restorers/interfaces.py +0 -0
  126. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restorers/state.py +0 -0
  127. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/restorers/utils.py +0 -0
  128. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/ta/__init__.py +0 -0
  129. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/ta/indicators.pxd +0 -0
  130. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/ta/indicators.pyi +0 -0
  131. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/ta/indicators.pyx +0 -0
  132. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/trackers/__init__.py +0 -0
  133. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/trackers/advanced.py +0 -0
  134. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/trackers/composite.py +0 -0
  135. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/trackers/rebalancers.py +0 -0
  136. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/trackers/riskctrl.py +0 -0
  137. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/trackers/sizers.py +0 -0
  138. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/__init__.py +0 -0
  139. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/_pyxreloader.py +0 -0
  140. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/charting/lookinglass.py +0 -0
  141. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  142. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/collections.py +0 -0
  143. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/marketdata/binance.py +0 -0
  144. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/marketdata/ccxt.py +0 -0
  145. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/marketdata/dukas.py +0 -0
  146. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/misc.py +0 -0
  147. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/ntp.py +0 -0
  148. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/numbers_utils.py +0 -0
  149. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/orderbook.py +0 -0
  150. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/plotting/__init__.py +0 -0
  151. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/plotting/dashboard.py +0 -0
  152. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/plotting/data.py +0 -0
  153. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/plotting/interfaces.py +0 -0
  154. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/plotting/renderers/__init__.py +0 -0
  155. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/plotting/renderers/plotly.py +0 -0
  156. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/questdb.py +0 -0
  157. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/runner/__init__.py +0 -0
  158. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/runner/_jupyter_runner.pyt +0 -0
  159. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/runner/accounts.py +0 -0
  160. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/runner/configs.py +0 -0
  161. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/runner/factory.py +0 -0
  162. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/runner/runner.py +0 -0
  163. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/time.py +0 -0
  164. {qubx-0.6.43 → qubx-0.6.47}/src/qubx/utils/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: Qubx
3
- Version: 0.6.43
3
+ Version: 0.6.47
4
4
  Summary: Qubx - Quantitative Trading Framework
5
5
  Author: Dmitry Marienko
6
6
  Author-email: dmitry.marienko@xlydian.com
@@ -11,7 +11,6 @@ Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
13
  Requires-Dist: aiohttp (>=3.10.11,<3.11.0)
14
- Requires-Dist: bitfinex-api-py (>=3.0.4,<4.0.0)
15
14
  Requires-Dist: ccxt (>=4.2.68,<5.0.0)
16
15
  Requires-Dist: croniter (>=2.0.5,<3.0.0)
17
16
  Requires-Dist: cython (==3.0.8)
@@ -40,6 +39,7 @@ Requires-Dist: pymongo (>=4.6.1,<5.0.0)
40
39
  Requires-Dist: python-binance (>=1.0.19,<2.0.0)
41
40
  Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
42
41
  Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
42
+ Requires-Dist: qubx-bitfinex-api (>=3.0.7,<4.0.0)
43
43
  Requires-Dist: questdb (>=2.0.3,<3.0.0)
44
44
  Requires-Dist: redis (>=5.2.1,<6.0.0)
45
45
  Requires-Dist: scikit-learn (>=1.4.2,<2.0.0)
@@ -50,6 +50,7 @@ Requires-Dist: statsmodels (>=0.14.2,<0.15.0)
50
50
  Requires-Dist: tabulate (>=0.9.0,<0.10.0)
51
51
  Requires-Dist: toml (>=0.10.2,<0.11.0)
52
52
  Requires-Dist: tqdm
53
+ Requires-Dist: websockets (==15.0.1)
53
54
  Project-URL: Repository, https://github.com/xLydianSoftware/Qubx
54
55
  Description-Content-Type: text/markdown
55
56
 
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "Qubx"
7
- version = "0.6.43"
7
+ version = "0.6.47"
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"
@@ -72,7 +72,8 @@ ipywidgets = "^8.1.5"
72
72
  questdb = "^2.0.3"
73
73
  orjson = "^3.10.15"
74
74
  aiohttp = "~3.10.11"
75
- bitfinex-api-py = "^3.0.4"
75
+ websockets = "15.0.1"
76
+ qubx-bitfinex-api = "^3.0.7"
76
77
 
77
78
  [tool.ruff.lint]
78
79
  extend-select = [ "I",]
@@ -218,7 +218,7 @@ def find_instruments_and_exchanges(
218
218
  exchange: ExchangeName_t | None,
219
219
  ) -> tuple[list[Instrument], list[ExchangeName_t]]:
220
220
  _instrs: list[Instrument] = []
221
- _exchanges = [] if exchange is None else [exchange.lower()]
221
+ _exchanges = [] if exchange is None else [exchange]
222
222
  for i in instruments:
223
223
  match i:
224
224
  case str():
@@ -198,6 +198,9 @@ class StrategyContext(IStrategyContext):
198
198
  self.strategy.on_init(self.initializer)
199
199
  self._strategy_state.is_on_init_called = True
200
200
 
201
+ if subscription_warmup := self.initializer.get_subscription_warmup():
202
+ self.set_warmup(subscription_warmup)
203
+
201
204
  if base_sub := self.initializer.get_base_subscription():
202
205
  self.set_base_subscription(base_sub)
203
206
 
@@ -31,6 +31,7 @@ class BasicStrategyInitializer(IStrategyInitializer):
31
31
  mismatch_resolver: Optional[StateResolverProtocol] = None
32
32
  auto_subscribe: Optional[bool] = None
33
33
  simulation: Optional[bool] = None
34
+ subscription_warmup: Optional[dict[Any, str]] = None
34
35
 
35
36
  # Additional configuration that might be needed
36
37
  config: Dict[str, Any] = field(default_factory=dict)
@@ -106,3 +107,9 @@ class BasicStrategyInitializer(IStrategyInitializer):
106
107
 
107
108
  def get_pending_instrument_subscriptions(self) -> dict[str, set[Instrument]]:
108
109
  return self._pending_instrument_subscriptions
110
+
111
+ def set_subscription_warmup(self, configs: dict[Any, str]) -> None:
112
+ self.subscription_warmup = configs
113
+
114
+ def get_subscription_warmup(self) -> dict[Any, str]:
115
+ return self.subscription_warmup if self.subscription_warmup else {}
@@ -1695,6 +1695,24 @@ class IStrategyInitializer:
1695
1695
  """
1696
1696
  ...
1697
1697
 
1698
+ def set_subscription_warmup(self, configs: dict[Any, str]) -> None:
1699
+ """
1700
+ Set the warmup period for the subscription.
1701
+
1702
+ This method is used to set the warmup period for the subscription in on_init stage.
1703
+ Then this config is used to translate warmup configs to StrategyContext
1704
+
1705
+ Args:
1706
+ configs: A dictionary of subscription types and their warmup periods.
1707
+ """
1708
+ ...
1709
+
1710
+ def get_subscription_warmup(self) -> dict[Any, str]:
1711
+ """
1712
+ Get the warmup period for the subscription.
1713
+ """
1714
+ ...
1715
+
1698
1716
 
1699
1717
  class IStrategy(metaclass=Mixable):
1700
1718
  """Base class for trading strategies."""
@@ -283,13 +283,14 @@ class InstrumentsLookup:
283
283
  query_exchanges=query_exchanges,
284
284
  )
285
285
 
286
+ #todo: temporaty disabled ccxt call to exchange, due to conectivity issues. Revert for bitfinex live usage
286
287
  def _update_bitfinex(self, path: str, query_exchanges: bool = False):
287
288
  self._ccxt_update(
288
289
  path,
289
290
  "bitfinex.f",
290
291
  {"bitfinex.f": "bitfinex"},
291
292
  keep_types=[MarketType.SWAP],
292
- query_exchanges=query_exchanges,
293
+ query_exchanges=False,
293
294
  )
294
295
 
295
296
  def _update_bitmex(self, path: str, query_exchanges: bool = False):
@@ -1406,8 +1406,8 @@ def chart_signals(
1406
1406
  if show_leverage:
1407
1407
  leverage = calculate_leverage(portfolio, result.capital, start, symbol)
1408
1408
  indicators["Leverage"] = ["area", "cyan", leverage]
1409
- symbol_count = len(portfolio.filter(like="_PnL").columns)
1410
- pnl = portfolio.filter(regex=f"{symbol}_PnL").cumsum() + result.capital / symbol_count
1409
+ # symbol_count = len(portfolio.filter(like="_PnL").columns)
1410
+ pnl = portfolio.filter(regex=f"{symbol}_PnL").cumsum() + result.capital # / symbol_count
1411
1411
  pnl = pnl.loc[start:]
1412
1412
  if apply_commissions:
1413
1413
  comm = portfolio.filter(regex=f"{symbol}_Commissions").loc[start:].cumsum()
@@ -1337,11 +1337,11 @@ def rolling_rank(x, period, pctls=(25, 50, 75)):
1337
1337
  raise ValueError(f"Period {period} exceeds number of data records {len(x)} ")
1338
1338
 
1339
1339
  if isinstance(x, pd.DataFrame):
1340
- z = pd.DataFrame.from_dict({c: rolling_rank(s, period, pctls) for c, s in x.iteritems()})
1340
+ z = pd.DataFrame.from_dict({c: rolling_rank(s, period, pctls) for c, s in x.items()})
1341
1341
  elif isinstance(x, pd.Series):
1342
- z = pd.Series(_rolling_rank(x.values, period, pctls), x.index, name=x.name)
1342
+ z = pd.Series(_rolling_rank(np.ascontiguousarray(x.values), period, pctls), x.index, name=x.name)
1343
1343
  else:
1344
- z = _rolling_rank(x.values, period, pctls)
1344
+ z = _rolling_rank(np.ascontiguousarray(x), period, pctls)
1345
1345
  return z
1346
1346
 
1347
1347
 
@@ -590,9 +590,11 @@ class OhlcDict(dict):
590
590
  print(str(d))
591
591
  """
592
592
 
593
+ _orig: dict[str, pd.DataFrame | pd.Series | OHLCV]
593
594
  _fields: Set[str]
594
595
 
595
596
  def __init__(self, orig: dict[str, pd.DataFrame | pd.Series | OHLCV]):
597
+ self._orig = orig
596
598
  _o_copy = {}
597
599
  _lst = []
598
600
  if isinstance(orig, dict):
@@ -639,3 +641,16 @@ class OhlcDict(dict):
639
641
 
640
642
  def __repr__(self) -> str:
641
643
  return self.display(3, 3)
644
+
645
+ def __reduce__(self):
646
+ """
647
+ For joblib Parallel compatibility - defines how to pickle the object
648
+ """
649
+ # Return the class, constructor arguments, and additional state
650
+ data = {k: v for k, v in self.items()}
651
+ return (self.__class__, (data,), {"_orig": self._orig})
652
+
653
+ def __setstate__(self, state):
654
+ """Restore object from pickle state"""
655
+ # Recreate the object using the constructor
656
+ self.__init__(state["_orig"])
@@ -8,6 +8,7 @@ from various sources.
8
8
  import os
9
9
  from pathlib import Path
10
10
  from pymongo import MongoClient
11
+ from datetime import datetime, timedelta
11
12
 
12
13
  import pandas as pd
13
14
 
@@ -152,13 +153,16 @@ class MongoDBBalanceRestorer(IBalanceRestorer):
152
153
  Example: {'USDT': AssetBalance(total=100000.0, locked=0.0)}
153
154
  """
154
155
  try:
155
- match_query = {
156
+ now = datetime.utcnow()
157
+ lookup_range = now - timedelta(days=7)
158
+ base_match = {
156
159
  "log_type": "balance",
157
160
  "strategy_name": self.strategy_name,
161
+ "timestamp": {"$gte": lookup_range}
158
162
  }
159
163
 
160
164
  latest_run_doc = (
161
- self.collection.find(match_query, {"run_id": 1, "timestamp": 1})
165
+ self.collection.find(base_match, {"run_id": 1, "timestamp": 1})
162
166
  .sort("timestamp", -1)
163
167
  .limit(1)
164
168
  )
@@ -172,23 +176,34 @@ class MongoDBBalanceRestorer(IBalanceRestorer):
172
176
 
173
177
  logger.info(f"Restoring balances from MongoDB for run_id: {latest_run_id}")
174
178
 
175
- query = {**match_query, "run_id": latest_run_id}
176
- logs = self.collection.find(query).sort("timestamp", 1)
177
-
178
- balances = {}
179
-
180
- for log in logs:
179
+ pipeline = [
180
+ {"$match": {**base_match, "run_id": latest_run_id}},
181
+ {"$sort": {"timestamp": -1}},
182
+ {
183
+ "$group": {
184
+ "_id": "$currency",
185
+ "doc": {"$first": "$$ROOT"}
186
+ }
187
+ }
188
+ ]
189
+
190
+ cursor = self.collection.aggregate(pipeline)
191
+ balances: dict[str, AssetBalance] = {}
192
+
193
+ for entry in cursor:
194
+ log = entry["doc"]
181
195
  currency = log.get("currency")
182
- if currency:
183
- total = log.get("total", 0.0)
184
- locked = log.get("locked", 0.0)
185
-
186
- balance = AssetBalance(
187
- total=total,
188
- locked=locked,
189
- )
190
- balance.free = total - locked
191
- balances[currency] = balance
196
+ if not currency:
197
+ continue
198
+ total = log.get("total", 0.0)
199
+ locked = log.get("locked", 0.0)
200
+
201
+ balance = AssetBalance(
202
+ total=total,
203
+ locked=locked,
204
+ )
205
+ balance.free = total - locked
206
+ balances[currency] = balance
192
207
 
193
208
  return balances
194
209
  except Exception as e:
@@ -8,6 +8,7 @@ for restoring positions from various sources.
8
8
  import os
9
9
  from pathlib import Path
10
10
  from pymongo import MongoClient
11
+ from datetime import datetime, timedelta
11
12
 
12
13
  import pandas as pd
13
14
 
@@ -168,13 +169,17 @@ class MongoDBPositionRestorer(IPositionRestorer):
168
169
  A dictionary mapping instruments to positions.
169
170
  """
170
171
  try:
171
- match_query = {
172
+ now = datetime.utcnow()
173
+ lookup_range = now - timedelta(days=7)
174
+
175
+ base_match = {
172
176
  "log_type": "positions",
173
177
  "strategy_name": self.strategy_name,
178
+ "timestamp": {"$gte": lookup_range}
174
179
  }
175
180
 
176
181
  latest_run_doc = (
177
- self.collection.find(match_query, {"run_id": 1, "timestamp": 1})
182
+ self.collection.find(base_match, {"run_id": 1, "timestamp": 1})
178
183
  .sort("timestamp", -1)
179
184
  .limit(1)
180
185
  )
@@ -188,20 +193,34 @@ class MongoDBPositionRestorer(IPositionRestorer):
188
193
 
189
194
  logger.info(f"Restoring positions from MongoDB for run_id: {latest_run_id}")
190
195
 
191
- query = {**match_query, "run_id": latest_run_id}
192
- logs = self.collection.find(query).sort("timestamp", 1)
193
-
194
- positions = {}
195
- seen_keys = set()
196
-
197
- for log in logs:
198
- key = (log.get("symbol"), log.get("exchange"), log.get("market_type"))
199
- if None in key or key in seen_keys:
196
+ pipeline = [
197
+ {"$match": {**base_match, "run_id": latest_run_id}},
198
+ {"$sort": {"timestamp": -1}},
199
+ {
200
+ "$group": {
201
+ "_id": {
202
+ "symbol": "$symbol",
203
+ "exchange": "$exchange",
204
+ "market_type": "$market_type"
205
+ },
206
+ "doc": {"$first": "$$ROOT"}
207
+ }
208
+ }
209
+ ]
210
+
211
+ cursor = self.collection.aggregate(pipeline)
212
+
213
+ positions: dict[Instrument, Position] = {}
214
+
215
+ for entry in cursor:
216
+ log = entry["doc"]
217
+
218
+ symbol = log.get("symbol")
219
+ exchange = log.get("exchange")
220
+ market_type = log.get("market_type")
221
+
222
+ if not (symbol and exchange and market_type):
200
223
  continue
201
- seen_keys.add(key)
202
-
203
- symbol = log["symbol"]
204
- exchange = log["exchange"]
205
224
 
206
225
  instrument = lookup.find_symbol(exchange, symbol)
207
226
  if instrument is None:
@@ -208,13 +208,16 @@ class MongoDBSignalRestorer(ISignalRestorer):
208
208
  A dictionary mapping instruments to lists of signals.
209
209
  """
210
210
  try:
211
- match_query = {
211
+ now = datetime.utcnow()
212
+ lookup_range = now - timedelta(days=30)
213
+ base_match = {
212
214
  "log_type": "signals",
213
215
  "strategy_name": self.strategy_name,
216
+ "timestamp": {"$gte": lookup_range}
214
217
  }
215
218
 
216
219
  latest_run_doc = (
217
- self.collection.find(match_query, {"run_id": 1, "timestamp": 1})
220
+ self.collection.find(base_match, {"run_id": 1, "timestamp": 1})
218
221
  .sort("timestamp", -1)
219
222
  .limit(1)
220
223
  )
@@ -228,12 +231,33 @@ class MongoDBSignalRestorer(ISignalRestorer):
228
231
 
229
232
  logger.info(f"Restoring signals from MongoDB for run_id: {latest_run_id}")
230
233
 
231
- query = {**match_query, "run_id": latest_run_id}
232
- logs = self.collection.find(query).sort("timestamp", 1)
233
-
234
+ pipeline = [
235
+ {"$match": {"log_type": "signals", "strategy_name": self.strategy_name, "run_id": latest_run_id}},
236
+ {"$sort": {"timestamp": -1}},
237
+ {
238
+ "$group": {
239
+ "_id": {
240
+ "symbol": "$symbol",
241
+ "exchange": "$exchange",
242
+ "market_type": "$market_type",
243
+ },
244
+ "signals": {"$push": "$$ROOT"}
245
+ }
246
+ },
247
+ {
248
+ "$project": {
249
+ "signals": {"$slice": ["$signals", 20]}
250
+ }
251
+ },
252
+ {"$unwind": "$signals"}
253
+ ]
254
+
255
+ cursor = self.collection.aggregate(pipeline)
256
+
234
257
  result: dict[Instrument, list[TargetPosition]] = {}
235
258
 
236
- for log in logs:
259
+ for entry in cursor:
260
+ log = entry["signals"]
237
261
  try:
238
262
  instrument = lookup.find_symbol(log["exchange"], log["symbol"])
239
263
  if instrument is None:
@@ -281,7 +305,6 @@ class MongoDBSignalRestorer(ISignalRestorer):
281
305
  )
282
306
 
283
307
  result.setdefault(instrument, []).append(target_position)
284
-
285
308
  except Exception as e:
286
309
  logger.exception(f"Failed to process signal document: {e}")
287
310
 
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
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
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
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
File without changes
File without changes
File without changes
File without changes
File without changes