Qubx 0.6.43__cp312-cp312-manylinux_2_39_x86_64.whl → 0.6.47__cp312-cp312-manylinux_2_39_x86_64.whl

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.

qubx/backtester/utils.py CHANGED
@@ -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():
qubx/core/context.py CHANGED
@@ -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
 
qubx/core/initializer.py CHANGED
@@ -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 {}
qubx/core/interfaces.py CHANGED
@@ -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."""
qubx/core/lookups.py CHANGED
@@ -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):
qubx/core/metrics.py CHANGED
@@ -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()
qubx/pandaz/ta.py CHANGED
@@ -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
 
qubx/pandaz/utils.py CHANGED
@@ -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"])
qubx/restorers/balance.py CHANGED
@@ -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:
qubx/restorers/signal.py CHANGED
@@ -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
 
@@ -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
 
@@ -11,7 +11,7 @@ qubx/backtester/runner.py,sha256=TnNM0t8PgBE_gnCOZZTIOc28a3RqtXmp2Xj4Gq5j6bo,205
11
11
  qubx/backtester/simulated_data.py,sha256=niujaMRj__jf4IyzCZrSBR5ZoH1VUbvsZHSewHftdmI,17240
12
12
  qubx/backtester/simulated_exchange.py,sha256=Xg0yv21gq4q9CeCeZoupcenNEBORrxpb93ONZEGL2xk,8076
13
13
  qubx/backtester/simulator.py,sha256=cSbW42X-YlAutZlOQ3Y4mAJWXr_1WomYprtWZVMe3Uk,9225
14
- qubx/backtester/utils.py,sha256=nHrgKcIkyp5gz8wrPwMp1fRItUtQfvOPjxZhcaCwN-o,32729
14
+ qubx/backtester/utils.py,sha256=dDaS7Wrc_xZXWuAqKDql7L3NRMvUkTxpuuAwS0SYA1c,32721
15
15
  qubx/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  qubx/cli/commands.py,sha256=EwGqbNvY5VRCEO9T1w0GgqtcEvPFYMW96KzC-FPUvDM,7259
17
17
  qubx/cli/deploy.py,sha256=pQ9FPOsywDyy8jOjLfrgYTTkKQ-MCixCzbgsG68Q3_0,8319
@@ -36,27 +36,27 @@ qubx/connectors/tardis/utils.py,sha256=epThu9DwqbDb7BgScH6fHa_FVpKUaItOqp3JwtKGc
36
36
  qubx/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  qubx/core/account.py,sha256=4_XskMLR9Uh-rpJBDYMrceYiGMvAZw56k1ve-unIW8w,19417
38
38
  qubx/core/basics.py,sha256=PaprFa6sscrYPZfxImWOPLfVoVlu3PtcXTWbz0ZMtEk,28864
39
- qubx/core/context.py,sha256=ZetAOnQ9pfUiMAFln02zJr4FePTjKaY5OSIVwaMPhnE,22822
39
+ qubx/core/context.py,sha256=zkDJIilM7Et-gWjtTtQTOZ5zIwwx-Ueqhsxdy5pQZnk,22950
40
40
  qubx/core/deque.py,sha256=3PsmJ5LF76JpsK4Wp5LLogyE15rKn6EDCkNOOWT6EOk,6203
41
41
  qubx/core/errors.py,sha256=LENtlgmVzxxUFNCsuy4PwyHYhkZkxuZQ2BPif8jaGmw,1411
42
42
  qubx/core/exceptions.py,sha256=11wQC3nnNLsl80zBqbE6xiKCqm31kctqo6W_gdnZkg8,581
43
43
  qubx/core/helpers.py,sha256=m7JrZaBckXHb4zjhKpdCbxFe3kfda-SCNLfagyq7Ve4,19158
44
- qubx/core/initializer.py,sha256=PUiD_cIjvGpuPjYyRpUjpwm3xNQ2Kipa8bAhbtxCQRo,3935
45
- qubx/core/interfaces.py,sha256=CzIl8tB6ImQkDcZEmhpstwHPOCY8NhZxXmBHLQUAieI,58253
44
+ qubx/core/initializer.py,sha256=YgTBs5LpIk6ZFdmMD8zCnJnVNcMh1oeYvt157jhwyMg,4242
45
+ qubx/core/interfaces.py,sha256=BaWE9ln6mZk3dU2Da7fMe02-hijVjbYZOvWJx6ueyLo,58830
46
46
  qubx/core/loggers.py,sha256=0g33jfipGFShSMrXBoYVzL0GfTzI36mwBJqHNUHmhdo,13342
47
- qubx/core/lookups.py,sha256=n5ZjjEhhRvmidCB-Cubr1b0Opm6lf_QVZNEWa_BOQG0,19376
48
- qubx/core/metrics.py,sha256=IUTVDfO7HW68GGoLsj6kkLz8ueg-bnXRiWflIts8K_w,60264
47
+ qubx/core/lookups.py,sha256=aEuyZqd_N4cQ-oHz3coEHcdX9Yb0cP5-NwDuj-DQyNk,19477
48
+ qubx/core/metrics.py,sha256=74xIecCvlxVXl0gy0JvgjJ2X5gg-RMmVZw9hQikkHE0,60269
49
49
  qubx/core/mixins/__init__.py,sha256=AMCLvfNuIb1kkQl3bhCj9jIOEl2eKcVPJeyLgrkB-rk,329
50
50
  qubx/core/mixins/market.py,sha256=lBappEimPhIuI0vmUvwVlIztkYjlEjJBpP-AdpfudII,3948
51
51
  qubx/core/mixins/processing.py,sha256=cmjD0PcQv3gFP6oILfNgdNgw7Tez0fUZu_nFn6680VI,24979
52
52
  qubx/core/mixins/subscription.py,sha256=V_g9wCPQ8S5SHkU-qOZ84cV5nReAUrV7DoSNAGG0LPY,10372
53
53
  qubx/core/mixins/trading.py,sha256=idfRPaqrvkfMxzu9mXr9i_xfqLee-ZAOrERxkxv6Ruo,7256
54
54
  qubx/core/mixins/universe.py,sha256=kMhBvGzkRBhuJJKr0Ld8w_BLHBuVIqCVwFGo9myWzT8,9966
55
- qubx/core/series.cpython-312-x86_64-linux-gnu.so,sha256=2uARlfcc4K8NiLaTtsm0Qj2Q7ZF6qt_Bj2HiLVdqOOg,978280
55
+ qubx/core/series.cpython-312-x86_64-linux-gnu.so,sha256=JkQx9VlTWM0UireyO6YMr_h-GU2zel2bKxJKB1Cb2bY,978280
56
56
  qubx/core/series.pxd,sha256=jBdMwgO8J4Zrue0e_xQ5RlqTXqihpzQNu6V3ckZvvpY,3978
57
57
  qubx/core/series.pyi,sha256=RaHm_oHHiWiNUMJqVfx5FXAXniGLsHxUFOUpacn7GC0,4604
58
58
  qubx/core/series.pyx,sha256=7cM3zZThW59waHiYcZmMxvYj-HYD7Ej_l7nKA4emPjE,46477
59
- qubx/core/utils.cpython-312-x86_64-linux-gnu.so,sha256=Ey7MzVkDVcV2s0Oq8TYRk9mLs9I9MMvNCj2DnXDVQuI,86568
59
+ qubx/core/utils.cpython-312-x86_64-linux-gnu.so,sha256=y3JeYgqNumyIcpG_h9iD1KwNnCMa_nIYNHb1r-j3c64,86568
60
60
  qubx/core/utils.pyi,sha256=a-wS13V2p_dM1CnGq40JVulmiAhixTwVwt0ah5By0Hc,348
61
61
  qubx/core/utils.pyx,sha256=k5QHfEFvqhqWfCob89ANiJDKNG8gGbOh-O4CVoneZ8M,1696
62
62
  qubx/data/__init__.py,sha256=ELZykvpPGWc5rX7QoNyNQwMLgdKMG8MACOByA4pM5hA,549
@@ -101,8 +101,8 @@ qubx/notifications/composite.py,sha256=fa-rvHEn6k-Fma5N7cT-7Sk7hzVyB0KDs2ktDyoyL
101
101
  qubx/notifications/slack.py,sha256=FZ0zfTA-zRHOsOsIVBtM7mkt4m-adiuiV5yrwliS9RM,7976
102
102
  qubx/notifications/throttler.py,sha256=8jnymPQbrgtN1rD7REQa2sA9teSWTqkk_uT9oaknOyc,5618
103
103
  qubx/pandaz/__init__.py,sha256=6BYz6gSgxjNa7WP1XqWflYG7WIq1ppSD9h1XGR5M5YQ,682
104
- qubx/pandaz/ta.py,sha256=ufUi0gOoQfVyqAfScY7M3ahzvwxOq5k6joGlp3h-KLg,92121
105
- qubx/pandaz/utils.py,sha256=f7airZjUKHkhKICL0HF_R9Vvcswdgef7Yr35Xb_Ppzs,23407
104
+ qubx/pandaz/ta.py,sha256=sIX9YxxB2S2nWU4vnS4rXFuEI5WSY76Ky1TFwf9RhMw,92154
105
+ qubx/pandaz/utils.py,sha256=rg28KfcbWw4NelaI196OiAq0VM6DsszO5nrGSDPQFWk,23972
106
106
  qubx/resources/_build.py,sha256=XE7XNuDqfXPc2OriLobKXmPMvwa7Z8AKAD-18fnf0e4,8802
107
107
  qubx/resources/instruments/symbols-binance.cm.json,sha256=rNI3phNeeRY95_IE7_0Um9d5U4jUtEijZQ_PaYg5cdw,25127
108
108
  qubx/resources/instruments/symbols-binance.json,sha256=Qx_XckgsWNhmmV8_t5DpG0AeGkuTyt1uiif2EeeBDIg,939678
@@ -115,15 +115,15 @@ qubx/restarts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
115
  qubx/restarts/state_resolvers.py,sha256=GJ617qwpulqMp_-WhpmsLozQobxgt5lU4ZGOIUaUzas,5606
116
116
  qubx/restarts/time_finders.py,sha256=r7yyRhJByV2uqdgamDRX2XClwpWWI9BNpc80t9nk6c0,2448
117
117
  qubx/restorers/__init__.py,sha256=vrnZBPJHR0-6knAccj4bK0tkjUPNRl32qiLr5Mv4aR0,911
118
- qubx/restorers/balance.py,sha256=Oz-LY7s8tdSIJl83z9rII-DpX5xa8-qiqjcDYfW-KiE,6456
118
+ qubx/restorers/balance.py,sha256=yLV1vBki0XhBxrOhgaJBHuuL8VmIii82LAWgLxusbcE,6967
119
119
  qubx/restorers/factory.py,sha256=eoijcUHDaBVPHSfkjyo1AHvWTvvs0kj7jJbF_NE30aw,6737
120
120
  qubx/restorers/interfaces.py,sha256=CcjBWavKq8_GIMKTSPodMa-n3wJQwcQTwyvYyNo_J3c,1776
121
- qubx/restorers/position.py,sha256=xmaqaQQrmzF0VEHAZK35cXb6WKYRnDDapaN-EgZcH8U,8090
122
- qubx/restorers/signal.py,sha256=9TAaJOEKPjZXuciFFVn6Z8a-Z8CfVSjRGFRcwEgbPLY,10745
121
+ qubx/restorers/position.py,sha256=jMJjq2ZJwHpAlG45bMy49WvkYK5UylDiExt7nVpxCfg,8703
122
+ qubx/restorers/signal.py,sha256=x-VLSnb863U517sPeZhGvfuIOUfmYVhANg5P7lio_Mg,11630
123
123
  qubx/restorers/state.py,sha256=dLaVnUwRCNRkUqbYyi0RfZs3Q3AdglkI_qTtQ8GDD5Y,7289
124
124
  qubx/restorers/utils.py,sha256=We2gfqwQKWziUYhuUnjb-xo-5tSlbuHWpPQn0CEMTn0,1155
125
125
  qubx/ta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
- qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so,sha256=zmLwgKSXapcBtD14PesLtIk6IosgoSa_EDk7hXEHhsY,654440
126
+ qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so,sha256=ND4ZL0LzatfcJmXJbZm71hDdfiN3jC-ZZm3ND8xB0mY,654440
127
127
  qubx/ta/indicators.pxd,sha256=Goo0_N0Xnju8XGo3Xs-3pyg2qr_0Nh5C-_26DK8U_IE,4224
128
128
  qubx/ta/indicators.pyi,sha256=19W0uERft49In5bf9jkJHkzJYEyE9gzudN7_DJ5Vdv8,1963
129
129
  qubx/ta/indicators.pyx,sha256=Xgpew46ZxSXsdfSEWYn3A0Q35MLsopB9n7iyCsXTufs,25969
@@ -160,8 +160,8 @@ qubx/utils/runner/factory.py,sha256=8IxKvsEL0opx5OlO4XEQgAlmu05sgxXxkY4G2MQdLbg,
160
160
  qubx/utils/runner/runner.py,sha256=DhICMOfgE0i7VEC7pNMSEzIyZbb-TbOa6FbR1obZpyc,28901
161
161
  qubx/utils/time.py,sha256=J0ZFGjzFL5T6GA8RPAel8hKG0sg2LZXeQ5YfDCfcMHA,10055
162
162
  qubx/utils/version.py,sha256=e52fIHyxzCiIuH7svCF6pkHuDlqL64rklqz-2XjWons,5309
163
- qubx-0.6.43.dist-info/LICENSE,sha256=qwMHOSJ2TD0nx6VUJvFhu1ynJdBfNozRMt6tnSul-Ts,35140
164
- qubx-0.6.43.dist-info/METADATA,sha256=Sb3LqQD68z6op6bweM4Anv5tLmWrJgQQek4w3rsKJqc,4492
165
- qubx-0.6.43.dist-info/WHEEL,sha256=UckHTmFUCaLKpi4yFY8Dewu0c6XkY-KvEAGzGOnaWo8,110
166
- qubx-0.6.43.dist-info/entry_points.txt,sha256=VqilDTe8mVuV9SbR-yVlZJBTjbkHIL2JBgXfQw076HY,47
167
- qubx-0.6.43.dist-info/RECORD,,
163
+ qubx-0.6.47.dist-info/LICENSE,sha256=qwMHOSJ2TD0nx6VUJvFhu1ynJdBfNozRMt6tnSul-Ts,35140
164
+ qubx-0.6.47.dist-info/METADATA,sha256=2ktGWorjJkcW1Wyc9oil6HOgAbAsJk4QHmqdwbRpNJE,4531
165
+ qubx-0.6.47.dist-info/WHEEL,sha256=UckHTmFUCaLKpi4yFY8Dewu0c6XkY-KvEAGzGOnaWo8,110
166
+ qubx-0.6.47.dist-info/entry_points.txt,sha256=VqilDTe8mVuV9SbR-yVlZJBTjbkHIL2JBgXfQw076HY,47
167
+ qubx-0.6.47.dist-info/RECORD,,
File without changes
File without changes