Qubx 0.6.84__cp312-cp312-manylinux_2_39_x86_64.whl → 0.6.85__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.

@@ -130,6 +130,7 @@ class CcxtAccountProcessor(BasicAccountProcessor):
130
130
 
131
131
  if not self.exchange_manager.exchange.isSandboxModeEnabled:
132
132
  # - start polling tasks
133
+ self._loop.submit(self.exchange_manager.exchange.load_markets()).result()
133
134
  self._polling_tasks["balance"] = self._loop.submit(
134
135
  self._poller("balance", self._update_balance, self.balance_interval)
135
136
  )
@@ -9,7 +9,7 @@ from qubx.core.exceptions import BadRequest
9
9
  class HyperliquidCcxtBroker(CcxtBroker):
10
10
  """
11
11
  HyperLiquid-specific broker that handles market order slippage requirements.
12
-
12
+
13
13
  HyperLiquid requires a price even for market orders to calculate max slippage.
14
14
  This broker automatically calculates slippage-protected prices for market orders.
15
15
  """
@@ -17,7 +17,7 @@ class HyperliquidCcxtBroker(CcxtBroker):
17
17
  def __init__(
18
18
  self,
19
19
  *args,
20
- market_order_slippage: float = 0.05, # 5% default slippage
20
+ market_order_slippage: float = 0.01, # 5% default slippage
21
21
  **kwargs,
22
22
  ):
23
23
  super().__init__(*args, **kwargs)
@@ -38,21 +38,29 @@ class HyperliquidCcxtBroker(CcxtBroker):
38
38
  if order_type.lower() == "market" and price is None:
39
39
  quote = self.data_provider.get_quote(instrument)
40
40
  if quote is None:
41
- logger.warning(f"[<y>{instrument.symbol}</y>] :: Quote is not available for market order slippage calculation.")
42
- raise BadRequest(f"Quote is not available for market order slippage calculation for {instrument.symbol}")
41
+ logger.warning(
42
+ f"[<y>{instrument.symbol}</y>] :: Quote is not available for market order slippage calculation."
43
+ )
44
+ raise BadRequest(
45
+ f"Quote is not available for market order slippage calculation for {instrument.symbol}"
46
+ )
43
47
 
44
48
  # Get slippage from options or use default
45
49
  slippage = options.get("slippage", self.market_order_slippage)
46
-
50
+
47
51
  # Calculate slippage-protected price
48
52
  if order_side.upper() == "BUY":
49
53
  # For buy orders, add slippage to ask price to ensure execution
50
54
  price = quote.ask * (1 + slippage)
51
- logger.debug(f"[<y>{instrument.symbol}</y>] :: Market BUY order: using slippage-protected price {price:.6f} (ask: {quote.ask:.6f}, slippage: {slippage:.1%})")
55
+ logger.debug(
56
+ f"[<y>{instrument.symbol}</y>] :: Market BUY order: using slippage-protected price {price:.6f} (ask: {quote.ask:.6f}, slippage: {slippage:.1%})"
57
+ )
52
58
  else: # SELL
53
59
  # For sell orders, subtract slippage from bid price to ensure execution
54
60
  price = quote.bid * (1 - slippage)
55
- logger.debug(f"[<y>{instrument.symbol}</y>] :: Market SELL order: using slippage-protected price {price:.6f} (bid: {quote.bid:.6f}, slippage: {slippage:.1%})")
61
+ logger.debug(
62
+ f"[<y>{instrument.symbol}</y>] :: Market SELL order: using slippage-protected price {price:.6f} (bid: {quote.bid:.6f}, slippage: {slippage:.1%})"
63
+ )
56
64
 
57
65
  # Call parent implementation with calculated price
58
66
  payload = super()._prepare_order_payload(
@@ -64,6 +72,6 @@ class HyperliquidCcxtBroker(CcxtBroker):
64
72
  if "slippage" in options:
65
73
  # HyperLiquid accepts slippage as a percentage (e.g., 0.05 for 5%)
66
74
  params["px"] = price # Explicit price for slippage calculation
67
-
75
+
68
76
  payload["params"] = params
69
- return payload
77
+ return payload
@@ -65,7 +65,7 @@ class OrderBookDataHandler(BaseDataTypeHandler):
65
65
 
66
66
  # Notify all listeners
67
67
  self._data_provider.notify_data_arrival(sub_type, dt_64(ob.time, "ns"))
68
-
68
+
69
69
  channel.send((instrument, sub_type, ob, False))
70
70
  return True
71
71
 
@@ -150,7 +150,7 @@ class OrderBookDataHandler(BaseDataTypeHandler):
150
150
  ) -> SubscriptionConfiguration:
151
151
  """
152
152
  Prepare subscription configuration for individual instruments.
153
-
153
+
154
154
  Creates separate subscriber functions for each instrument to enable independent
155
155
  WebSocket streams without waiting for all instruments. This follows the same
156
156
  pattern as the OHLC handler for proper individual stream management.
@@ -169,10 +169,10 @@ class OrderBookDataHandler(BaseDataTypeHandler):
169
169
  try:
170
170
  # Watch orderbook for single instrument
171
171
  ccxt_ob = await self._exchange_manager.exchange.watch_order_book(symbol)
172
-
172
+
173
173
  # Use private processing method to avoid duplication
174
174
  self._process_orderbook(ccxt_ob, inst, sub_type, channel, depth, tick_size_pct)
175
-
175
+
176
176
  except Exception as e:
177
177
  logger.error(
178
178
  f"<yellow>{exchange_id}</yellow> Error in individual orderbook subscription for {inst.symbol}: {e}"
@@ -186,13 +186,15 @@ class OrderBookDataHandler(BaseDataTypeHandler):
186
186
  # Create individual unsubscriber if exchange supports it
187
187
  un_watch_method = getattr(self._exchange_manager.exchange, "un_watch_order_book", None)
188
188
  if un_watch_method is not None and callable(un_watch_method):
189
-
189
+
190
190
  def create_individual_unsubscriber(symbol=ccxt_symbol, exchange_id=self._exchange_id):
191
191
  async def individual_unsubscriber():
192
192
  try:
193
193
  await self._exchange_manager.exchange.un_watch_order_book(symbol)
194
194
  except Exception as e:
195
- logger.error(f"<yellow>{exchange_id}</yellow> Error unsubscribing orderbook for {symbol}: {e}")
195
+ logger.error(
196
+ f"<yellow>{exchange_id}</yellow> Error unsubscribing orderbook for {symbol}: {e}"
197
+ )
196
198
 
197
199
  return individual_unsubscriber
198
200
 
@@ -27,6 +27,40 @@ class TradeDataHandler(BaseDataTypeHandler):
27
27
  def data_type(self) -> str:
28
28
  return "trade"
29
29
 
30
+ def _process_trade(self, trades: list, instrument: Instrument, sub_type: str, channel: CtrlChannel):
31
+ """
32
+ Process trades with synthetic quote generation.
33
+
34
+ This method handles the common logic for processing trade data that's shared between
35
+ bulk and individual subscription approaches.
36
+
37
+ Args:
38
+ trades: List of CCXT trade dictionaries
39
+ instrument: Instrument these trades belong to
40
+ sub_type: Subscription type string
41
+ channel: Control channel to send data through
42
+ """
43
+ for trade in trades:
44
+ converted_trade = ccxt_convert_trade(trade)
45
+
46
+ # Notify all listeners
47
+ self._data_provider.notify_data_arrival(sub_type, dt_64(converted_trade.time, "ns"))
48
+
49
+ channel.send((instrument, sub_type, converted_trade, False))
50
+
51
+ # Generate synthetic quote if no quote/orderbook subscription exists
52
+ if len(trades) > 0 and not (
53
+ self._data_provider.has_subscription(instrument, DataType.ORDERBOOK)
54
+ or self._data_provider.has_subscription(instrument, DataType.QUOTE)
55
+ ):
56
+ last_trade = trades[-1]
57
+ converted_trade = ccxt_convert_trade(last_trade)
58
+ _price = converted_trade.price
59
+ _time = converted_trade.time
60
+ _s2 = instrument.tick_size / 2.0
61
+ _bid, _ask = _price - _s2, _price + _s2
62
+ self._data_provider._last_quotes[instrument] = Quote(_time, _bid, _ask, 0.0, 0.0)
63
+
30
64
  def prepare_subscription(
31
65
  self, name: str, sub_type: str, channel: CtrlChannel, instruments: Set[Instrument], **params
32
66
  ) -> SubscriptionConfiguration:
@@ -42,6 +76,21 @@ class TradeDataHandler(BaseDataTypeHandler):
42
76
  Returns:
43
77
  SubscriptionConfiguration with subscriber and unsubscriber functions
44
78
  """
79
+ # Use exchange-specific approach based on capabilities
80
+ if self._exchange_manager.exchange.has.get("watchTradesForSymbols", False):
81
+ return self._prepare_subscription_for_instruments(name, sub_type, channel, instruments)
82
+ else:
83
+ # Fall back to individual instrument subscriptions
84
+ return self._prepare_subscription_for_individual_instruments(name, sub_type, channel, instruments)
85
+
86
+ def _prepare_subscription_for_instruments(
87
+ self,
88
+ name: str,
89
+ sub_type: str,
90
+ channel: CtrlChannel,
91
+ instruments: Set[Instrument],
92
+ ) -> SubscriptionConfiguration:
93
+ """Prepare subscription configuration for multiple instruments using bulk API."""
45
94
  _instr_to_ccxt_symbol = {i: instrument_to_ccxt_symbol(i) for i in instruments}
46
95
  _symbol_to_instrument = {_instr_to_ccxt_symbol[i]: i for i in instruments}
47
96
 
@@ -52,31 +101,13 @@ class TradeDataHandler(BaseDataTypeHandler):
52
101
  exch_symbol = trades[0]["symbol"]
53
102
  instrument = ccxt_find_instrument(exch_symbol, self._exchange_manager.exchange, _symbol_to_instrument)
54
103
 
55
- for trade in trades:
56
- converted_trade = ccxt_convert_trade(trade)
57
-
58
- # Notify all listeners
59
- self._data_provider.notify_data_arrival(sub_type, dt_64(converted_trade.time, "ns"))
60
-
61
- channel.send((instrument, sub_type, converted_trade, False))
62
-
63
- if len(trades) > 0 and not (
64
- self._data_provider.has_subscription(instrument, DataType.ORDERBOOK)
65
- or self._data_provider.has_subscription(instrument, DataType.QUOTE)
66
- ):
67
- last_trade = trades[-1]
68
- converted_trade = ccxt_convert_trade(last_trade)
69
- _price = converted_trade.price
70
- _time = converted_trade.time
71
- _s2 = instrument.tick_size / 2.0
72
- _bid, _ask = _price - _s2, _price + _s2
73
- self._data_provider._last_quotes[instrument] = Quote(_time, _bid, _ask, 0.0, 0.0)
104
+ # Use private processing method to avoid duplication
105
+ self._process_trade(trades, instrument, sub_type, channel)
74
106
 
75
107
  async def un_watch_trades(instruments_batch: list[Instrument]):
76
108
  symbols = [_instr_to_ccxt_symbol[i] for i in instruments_batch]
77
109
  await self._exchange_manager.exchange.un_watch_trades_for_symbols(symbols)
78
110
 
79
- # Return subscription configuration instead of calling _listen_to_stream directly
80
111
  return SubscriptionConfiguration(
81
112
  subscription_type=sub_type,
82
113
  subscriber_func=create_market_type_batched_subscriber(watch_trades, instruments),
@@ -85,6 +116,71 @@ class TradeDataHandler(BaseDataTypeHandler):
85
116
  requires_market_type_batching=True,
86
117
  )
87
118
 
119
+ def _prepare_subscription_for_individual_instruments(
120
+ self,
121
+ name: str,
122
+ sub_type: str,
123
+ channel: CtrlChannel,
124
+ instruments: Set[Instrument],
125
+ ) -> SubscriptionConfiguration:
126
+ """
127
+ Prepare subscription configuration for individual instruments.
128
+
129
+ Creates separate subscriber functions for each instrument to enable independent
130
+ WebSocket streams without waiting for all instruments. This follows the same
131
+ pattern as the orderbook handler for proper individual stream management.
132
+ """
133
+ _instr_to_ccxt_symbol = {i: instrument_to_ccxt_symbol(i) for i in instruments}
134
+
135
+ individual_subscribers = {}
136
+ individual_unsubscribers = {}
137
+
138
+ for instrument in instruments:
139
+ ccxt_symbol = _instr_to_ccxt_symbol[instrument]
140
+
141
+ # Create individual subscriber for this instrument using closure
142
+ def create_individual_subscriber(inst=instrument, symbol=ccxt_symbol, exchange_id=self._exchange_id):
143
+ async def individual_subscriber():
144
+ try:
145
+ # Watch trades for single instrument
146
+ trades = await self._exchange_manager.exchange.watch_trades(symbol)
147
+
148
+ # Use private processing method to avoid duplication
149
+ self._process_trade(trades, inst, sub_type, channel)
150
+
151
+ except Exception as e:
152
+ logger.error(
153
+ f"<yellow>{exchange_id}</yellow> Error in individual trade subscription for {inst.symbol}: {e}"
154
+ )
155
+ raise # Let connection manager handle retries
156
+
157
+ return individual_subscriber
158
+
159
+ individual_subscribers[instrument] = create_individual_subscriber()
160
+
161
+ # Create individual unsubscriber if exchange supports it
162
+ un_watch_method = getattr(self._exchange_manager.exchange, "un_watch_trades", None)
163
+ if un_watch_method is not None and callable(un_watch_method):
164
+
165
+ def create_individual_unsubscriber(symbol=ccxt_symbol, exchange_id=self._exchange_id):
166
+ async def individual_unsubscriber():
167
+ try:
168
+ await self._exchange_manager.exchange.un_watch_trades(symbol)
169
+ except Exception as e:
170
+ logger.error(f"<yellow>{exchange_id}</yellow> Error unsubscribing trades for {symbol}: {e}")
171
+
172
+ return individual_unsubscriber
173
+
174
+ individual_unsubscribers[instrument] = create_individual_unsubscriber()
175
+
176
+ return SubscriptionConfiguration(
177
+ subscription_type=sub_type,
178
+ instrument_subscribers=individual_subscribers,
179
+ instrument_unsubscribers=individual_unsubscribers if individual_unsubscribers else None,
180
+ stream_name=name,
181
+ requires_market_type_batching=False,
182
+ )
183
+
88
184
  async def warmup(self, instruments: Set[Instrument], channel: CtrlChannel, warmup_period: str, **params) -> None:
89
185
  """
90
186
  Fetch historical trade data for warmup during backtesting.
qubx/core/helpers.py CHANGED
@@ -233,10 +233,16 @@ class CachedMarketDataHolder:
233
233
  if series:
234
234
  total_vol = trade.size
235
235
  bought_vol = total_vol if trade.side == 1 else 0.0
236
+ volume_quote = trade.price * trade.size
237
+ bought_volume_quote = volume_quote if trade.side == 1 else 0.0
236
238
  for ser in series.values():
237
- if len(ser) > 0 and ser[0].time > trade.time:
238
- continue
239
- ser.update(trade.time, trade.price, total_vol, bought_vol)
239
+ if len(ser) > 0:
240
+ current_bar_start = floor_t64(np.datetime64(ser[0].time, 'ns'), np.timedelta64(ser.timeframe, 'ns'))
241
+ trade_bar_start = floor_t64(np.datetime64(trade.time, 'ns'), np.timedelta64(ser.timeframe, 'ns'))
242
+ if trade_bar_start < current_bar_start:
243
+ # Trade belongs to a previous bar - skip it
244
+ continue
245
+ ser.update(trade.time, trade.price, total_vol, bought_vol, volume_quote, bought_volume_quote, 1)
240
246
 
241
247
  def finalize_ohlc_for_instruments(self, time: dt_64, instruments: list[Instrument]):
242
248
  """
@@ -235,10 +235,16 @@ class SubscriptionManager(ISubscriptionManager):
235
235
  self._pending_warmups[(sub, instr)] = _warmup_period
236
236
 
237
237
  # TODO: think about appropriate handling of timeouts
238
- _data_provider.warmup(self._pending_warmups.copy())
238
+ _warmup_configs = self._get_pending_warmups_for_exchange(_data_provider.exchange())
239
+ _data_provider.warmup(_warmup_configs)
239
240
 
240
241
  self._pending_warmups.clear()
241
242
 
243
+ def _get_pending_warmups_for_exchange(self, exchange: str) -> dict[tuple[str, Instrument], str]:
244
+ return {
245
+ (sub, instr): period for (sub, instr), period in self._pending_warmups.items() if instr.exchange == exchange
246
+ }
247
+
242
248
  def _get_data_provider(self, exchange: str) -> IDataProvider:
243
249
  if exchange in self._exchange_to_data_provider:
244
250
  return self._exchange_to_data_provider[exchange]
qubx/core/series.pxd CHANGED
@@ -63,7 +63,7 @@ cdef class Bar:
63
63
  cdef public double bought_volume_quote # volume bought (in quote asset) if presented
64
64
  cdef public int trade_count # number of trades in this bar
65
65
 
66
- cpdef Bar update(Bar self, double price, double volume, double volume_quote=*, double bought_volume=*, double bought_volume_quote=*, int trade_count=*)
66
+ cpdef Bar update(Bar self, double price, double volume, double bought_volume=*, double volume_quote=*, double bought_volume_quote=*, int trade_count=*)
67
67
 
68
68
  cpdef dict to_dict(Bar self, unsigned short skip_time=*)
69
69
 
@@ -78,8 +78,9 @@ cdef class OHLCV(TimeSeries):
78
78
  cdef public TimeSeries volume_quote
79
79
  cdef public TimeSeries bvolume_quote
80
80
  cdef public TimeSeries trade_count
81
+ cdef public dict columns
81
82
 
82
- cpdef short update(OHLCV self, long long time, double price, double volume=*, double bvolume=*)
83
+ cpdef short update(OHLCV self, long long time, double price, double volume=*, double bvolume=*, double volume_quote=*, double bought_volume_quote=*, int trade_count=*)
83
84
 
84
85
  cpdef short update_by_bar(
85
86
  OHLCV self, long long time, double open, double high, double low, double close,
qubx/core/series.pyi CHANGED
@@ -31,8 +31,8 @@ class Bar:
31
31
  self,
32
32
  price: float,
33
33
  volume: float,
34
- volume_quote: float = 0,
35
34
  bought_volume: float = 0,
35
+ volume_quote: float = 0,
36
36
  bought_volume_quote: float = 0,
37
37
  trade_count: int = 0,
38
38
  ) -> "Bar": ...
@@ -127,7 +127,8 @@ class OHLCV(TimeSeries):
127
127
 
128
128
  def __init__(self, name, timeframe, max_series_length: int | float = np.inf) -> None: ...
129
129
  def __len__(self) -> int: ...
130
- def update(self, time: int, price: float, volume: float = 0.0, bvolume: float = 0.0) -> bool: ...
130
+ def update(self, time: int, price: float, volume: float = 0.0, bvolume: float = 0.0,
131
+ volume_quote: float = 0.0, bought_volume_quote: float = 0.0, trade_count: int = 0) -> bool: ...
131
132
  def update_by_bar(
132
133
  self,
133
134
  time: int,
qubx/core/series.pyx CHANGED
@@ -773,7 +773,7 @@ cdef class Bar:
773
773
  self.bought_volume += bought_volume
774
774
  self.volume_quote += volume_quote
775
775
  self.bought_volume_quote += bought_volume_quote
776
- self.trade_count = trade_count # Use latest trade count (cumulative)
776
+ self.trade_count += trade_count # Increment trade count
777
777
  return self
778
778
 
779
779
  cpdef dict to_dict(self, unsigned short skip_time=0):
@@ -1020,6 +1020,22 @@ cdef class OHLCV(TimeSeries):
1020
1020
  self.volume_quote = TimeSeries('volume_quote', timeframe, max_series_length)
1021
1021
  self.bvolume_quote = TimeSeries('bvolume_quote', timeframe, max_series_length)
1022
1022
  self.trade_count = TimeSeries('trade_count', timeframe, max_series_length)
1023
+ self.columns = {
1024
+ "open": self.open,
1025
+ "high": self.high,
1026
+ "low": self.low,
1027
+ "close": self.close,
1028
+ "volume": self.volume,
1029
+ "bvolume": self.bvolume,
1030
+ "volume_quote": self.volume_quote,
1031
+ "bvolume_quote": self.bvolume_quote,
1032
+ "trade_count": self.trade_count,
1033
+ }
1034
+
1035
+ def __getitem__(self, idx):
1036
+ if isinstance(idx, str):
1037
+ return self.columns[idx]
1038
+ return super().__getitem__(idx)
1023
1039
 
1024
1040
  cpdef object append_data(self,
1025
1041
  np.ndarray times,
@@ -1114,18 +1130,25 @@ cdef class OHLCV(TimeSeries):
1114
1130
  self.trade_count._update_last_item(time, value.trade_count)
1115
1131
  self._is_new_item = False
1116
1132
 
1117
- cpdef short update(self, long long time, double price, double volume=0.0, double bvolume=0.0):
1133
+ cpdef short update(self, long long time, double price, double volume=0.0, double bvolume=0.0,
1134
+ double volume_quote=0.0, double bought_volume_quote=0.0, int trade_count=0):
1118
1135
  cdef Bar b
1119
1136
  bar_start_time = floor_t64(time, self.timeframe)
1120
1137
 
1121
1138
  if not self.times:
1122
- self._add_new_item(bar_start_time, Bar(bar_start_time, price, price, price, price, volume=volume, bought_volume=bvolume))
1139
+ self._add_new_item(bar_start_time, Bar(bar_start_time, price, price, price, price,
1140
+ volume=volume, bought_volume=bvolume,
1141
+ volume_quote=volume_quote, bought_volume_quote=bought_volume_quote,
1142
+ trade_count=trade_count))
1123
1143
 
1124
1144
  # Here we disable first notification because first item may be incomplete
1125
1145
  self._is_new_item = False
1126
1146
 
1127
1147
  elif (_dt := time - self.times[0]) >= self.timeframe:
1128
- b = Bar(bar_start_time, price, price, price, price, volume=volume, bought_volume=bvolume)
1148
+ b = Bar(bar_start_time, price, price, price, price,
1149
+ volume=volume, bought_volume=bvolume,
1150
+ volume_quote=volume_quote, bought_volume_quote=bought_volume_quote,
1151
+ trade_count=trade_count)
1129
1152
 
1130
1153
  # - add new item
1131
1154
  self._add_new_item(bar_start_time, b)
@@ -1138,7 +1161,9 @@ cdef class OHLCV(TimeSeries):
1138
1161
  if _dt < 0:
1139
1162
  raise ValueError(f"Attempt to update past data at {time_to_str(time)} !")
1140
1163
 
1141
- self._update_last_item(bar_start_time, self[0].update(price, volume, bvolume))
1164
+ self._update_last_item(bar_start_time, self[0].update(price, volume, bvolume,
1165
+ volume_quote, bought_volume_quote,
1166
+ trade_count))
1142
1167
 
1143
1168
  # - update indicators by new data
1144
1169
  self._update_indicators(bar_start_time, self[0], False)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Qubx
3
- Version: 0.6.84
3
+ Version: 0.6.85
4
4
  Summary: Qubx - Quantitative Trading Framework
5
5
  License-File: LICENSE
6
6
  Author: Dmitry Marienko
@@ -20,7 +20,7 @@ qubx/cli/misc.py,sha256=tP28QxLEzuP8R2xnt8g3JTs9Z7aYy4iVWY4g3VzKTsQ,14777
20
20
  qubx/cli/release.py,sha256=Kz5aykF9FlAeUgXF59_Mu3vRFxa_qF9dq0ySqLlKKqo,41362
21
21
  qubx/cli/tui.py,sha256=N15UiNEdnWOWYh8E9DNlQCDWdoyP6rMGMhEItogPW88,16491
22
22
  qubx/connectors/ccxt/__init__.py,sha256=HEQ7lM9HS8sED_zfsAHrhFT7F9E7NFGAecwZwNr-TDE,65
23
- qubx/connectors/ccxt/account.py,sha256=bhC2YDHT-CZTph91Pxo54ABOn1k7ROmxyWQyiCu-0ck,25310
23
+ qubx/connectors/ccxt/account.py,sha256=VyPQGlELm1lFXD4sf4zD8xaC9ZQpneB8gAVm-di4hhE,25396
24
24
  qubx/connectors/ccxt/adapters/__init__.py,sha256=4qwWer4C9fTIZKUYmcgbMFEwFuYp34UKbb-AoQNvXjc,178
25
25
  qubx/connectors/ccxt/adapters/polling_adapter.py,sha256=UrOAoIfgtoRw7gj6Bmk5lPs_UI8iOxr88DAfp5o5CF8,8962
26
26
  qubx/connectors/ccxt/broker.py,sha256=I6I_BynhwHadKa62nBGmDoj5LhFiEzbUq-1ZDwE25aM,16512
@@ -35,7 +35,7 @@ qubx/connectors/ccxt/exchanges/binance/exchange.py,sha256=UrhATX_NiclNTSG6ePdk8k
35
35
  qubx/connectors/ccxt/exchanges/bitfinex/bitfinex.py,sha256=Tq-OR06JBU2yADUQc7m80oUyyUQ-cckzc4GyVI_1ey0,10745
36
36
  qubx/connectors/ccxt/exchanges/bitfinex/bitfinex_account.py,sha256=zrnA6GJiNddoM5JF-SlFFO4FpITDO5rGaU9ipshUvAY,1603
37
37
  qubx/connectors/ccxt/exchanges/hyperliquid/__init__.py,sha256=j6OB7j_yKRgitQAeJX3jkTrJ7NVnbTKtoumMiH_9u4I,166
38
- qubx/connectors/ccxt/exchanges/hyperliquid/broker.py,sha256=R9R19U6-tkHwT_vI_7Sn2cDirHhDo9LpE8k0g4f6syQ,3005
38
+ qubx/connectors/ccxt/exchanges/hyperliquid/broker.py,sha256=yoht8KsiMS3F6rkyfwQ3prBn-OgnY6tCDwYJQbsq1t0,3130
39
39
  qubx/connectors/ccxt/exchanges/hyperliquid/hyperliquid.py,sha256=w2uidBYpIUWoZsAeXiITdc77EIGN4cE3bXsGAb4JpaE,20353
40
40
  qubx/connectors/ccxt/exchanges/kraken/kraken.py,sha256=ntxf41aPY0JqxT5jn-979fgQih2TvMbr_p9fvCJB9QE,414
41
41
  qubx/connectors/ccxt/factory.py,sha256=hX5WODwwo49J3YjGulpDyq1SKq9VvWH3otzFw6avS5I,6259
@@ -46,9 +46,9 @@ qubx/connectors/ccxt/handlers/funding_rate.py,sha256=OLlocxRBEMxQ7LAtxZgQcdn3LIZ
46
46
  qubx/connectors/ccxt/handlers/liquidation.py,sha256=0QbLeZwpdEgd30PwQoWXUBKG25rrNQ0OgPz8fPLRA6U,3915
47
47
  qubx/connectors/ccxt/handlers/ohlc.py,sha256=668mHEuLb-sT-Rk0miNsvKTETfoo0XK9aAAqNa4uTCY,17706
48
48
  qubx/connectors/ccxt/handlers/open_interest.py,sha256=K3gPI51zUtxvED3V-sfFmK1Lsa-vx7k-Q-UE_eLsoRM,9957
49
- qubx/connectors/ccxt/handlers/orderbook.py,sha256=VuRgIAshKoHhRrI9DKW7xuVnzsOTNvboz3y5QRkvcII,9147
49
+ qubx/connectors/ccxt/handlers/orderbook.py,sha256=67cJt1aW778lMch8abkzvkhRKKnfHOqJ6IDufx-NoZk,9129
50
50
  qubx/connectors/ccxt/handlers/quote.py,sha256=JwQ8mXMpFMdFEpQTx3x_Xaj6VHZanC6_JI6tB9l_yDw,4464
51
- qubx/connectors/ccxt/handlers/trade.py,sha256=VspCqw13r9SyvF0N3a31YKIVTzUx5IjFtMDaQeSxblM,4519
51
+ qubx/connectors/ccxt/handlers/trade.py,sha256=doBxWHvRQIhRbr5PqzQnoVMBarjWlezk75DPlu__JVg,8841
52
52
  qubx/connectors/ccxt/reader.py,sha256=uUG1I_ejzTf0f4bCAHpLhBzTUqtNX-JvJGFA4bi7-WU,26602
53
53
  qubx/connectors/ccxt/subscription_config.py,sha256=jbMZ_9US3nvrp6LCVmMXLQnAjXH0xIltzUSPqXJZvgs,3865
54
54
  qubx/connectors/ccxt/subscription_manager.py,sha256=9ZfA6bR6YwtlZqVs6yymKPvYSvy5x3yBuHA_LDbiKgc,13285
@@ -64,7 +64,7 @@ qubx/core/context.py,sha256=Yq039nsKmf-IeyvcRdnMs_phVmehoCUqFvQuVnm-d4s,26074
64
64
  qubx/core/deque.py,sha256=3PsmJ5LF76JpsK4Wp5LLogyE15rKn6EDCkNOOWT6EOk,6203
65
65
  qubx/core/errors.py,sha256=LENtlgmVzxxUFNCsuy4PwyHYhkZkxuZQ2BPif8jaGmw,1411
66
66
  qubx/core/exceptions.py,sha256=11wQC3nnNLsl80zBqbE6xiKCqm31kctqo6W_gdnZkg8,581
67
- qubx/core/helpers.py,sha256=3rY7VkeL8WQ3hj7f8_rbSYNuQftlT0IDpTUARGP5ORM,21622
67
+ qubx/core/helpers.py,sha256=xFcgXtNGvaz8SvLeHToePMx0tx85eX3H4ymxjz66ekg,22129
68
68
  qubx/core/initializer.py,sha256=VVti9UJDJCg0125Mm09S6Tt1foHK4XOBL0mXgDmvXes,7724
69
69
  qubx/core/interfaces.py,sha256=AwSS92BbfUXw92TLvB75Wn5bxYByHAAXie2GAC70vUQ,67824
70
70
  qubx/core/loggers.py,sha256=eYijsR02S5u1Hv21vjIk_dOUwOMv0fiBDYwEmFhAoWk,14344
@@ -73,16 +73,16 @@ qubx/core/metrics.py,sha256=o3Bd25G2GwqxVlIpryScCrQISCYjsVWiIWAUUsW2siQ,79274
73
73
  qubx/core/mixins/__init__.py,sha256=AMCLvfNuIb1kkQl3bhCj9jIOEl2eKcVPJeyLgrkB-rk,329
74
74
  qubx/core/mixins/market.py,sha256=w9OEDfy0r9xnb4KdKA-PuFsCQugNo4pMLQivGFeyqGw,6356
75
75
  qubx/core/mixins/processing.py,sha256=UqKwOzkDXvmcjvffrsR8vugq_XacwW9rcGM0TcBd9RM,44485
76
- qubx/core/mixins/subscription.py,sha256=GcZKHHzjPyYFLAEpE7j4fpLDOlAhFKojQEYfFO3QarY,11064
76
+ qubx/core/mixins/subscription.py,sha256=2nUikNNPsMExS9yO38kNt7jk1vKE-RPm0b3h1bU6gjE,11397
77
77
  qubx/core/mixins/trading.py,sha256=7KwxHiPWkXGnkHS4VLaxOZ7BHULkvvqPS8ooAG1NTxM,9477
78
78
  qubx/core/mixins/universe.py,sha256=UBa3OIr2XvlK04O7YUG9c66CY8AZ5rQDSZov1rnUSjQ,10512
79
79
  qubx/core/mixins/utils.py,sha256=P71cLuqKjId8989MwOL_BtvvCnnwOFMkZyB1SY-0Ork,147
80
- qubx/core/series.cpython-312-x86_64-linux-gnu.so,sha256=8ZkkWVeQYTU8zECQk_GcFY-xrE5a2c_2XkebE6DRDv0,1019592
81
- qubx/core/series.pxd,sha256=PvnUEupOsZg8u81U5Amd-nbfmWQ0-PwZwc7yUoaZpoQ,4739
82
- qubx/core/series.pyi,sha256=RkM-F3AyrzT7m1H2UmOvZmmcOzU2eBeEWf2c0GUZe2o,5437
83
- qubx/core/series.pyx,sha256=wAn7L9HIkvVl-1Tt7bgdWhec7xy4AiHSXyDsrA4a29U,51703
80
+ qubx/core/series.cpython-312-x86_64-linux-gnu.so,sha256=OvwHlIEZOnDhfIH_uRteowsd3l6OMYM6cywPPS4vSkQ,1027944
81
+ qubx/core/series.pxd,sha256=4WpEexOBwZdKvqI81yR7wCnQh2rKgVZp9Y0ejr8B3E4,4841
82
+ qubx/core/series.pyi,sha256=30F48-oMp-XuySh5pyuoZcV20R4yODRpYBgLv7yxhjY,5534
83
+ qubx/core/series.pyx,sha256=9d3XjPAyI6qYLXuKXqZ3HrxBir1CRVste8GpGvgqYL4,52876
84
84
  qubx/core/stale_data_detector.py,sha256=NHnnG9NkcivC93n8QMwJUzFVQv2ziUaN-fg76ppng_c,17118
85
- qubx/core/utils.cpython-312-x86_64-linux-gnu.so,sha256=6GJhlrgU-RNB-f0jFB0M_opcBBL1VSW9toMwRe02aJ4,86568
85
+ qubx/core/utils.cpython-312-x86_64-linux-gnu.so,sha256=XhIYTnpumKGrmjGzWNiU9_IJMENmqJmJZhWVmt4YS58,86568
86
86
  qubx/core/utils.pyi,sha256=a-wS13V2p_dM1CnGq40JVulmiAhixTwVwt0ah5By0Hc,348
87
87
  qubx/core/utils.pyx,sha256=UR9achMR-LARsztd2eelFsDsFH3n0gXACIKoGNPI9X4,1766
88
88
  qubx/data/__init__.py,sha256=BlyZ99esHLDmFA6ktNkuIce9RZO99TA1IMKWe94aI8M,599
@@ -156,7 +156,7 @@ qubx/restorers/signal.py,sha256=5nK5ji8AucyWrFBK9uW619YCI_vPRGFnuDu8JnG3B_Y,1451
156
156
  qubx/restorers/state.py,sha256=I1VIN0ZcOjigc3WMHIYTNJeAAbN9YB21MDcMl04ZWmY,8018
157
157
  qubx/restorers/utils.py,sha256=We2gfqwQKWziUYhuUnjb-xo-5tSlbuHWpPQn0CEMTn0,1155
158
158
  qubx/ta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
159
- qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so,sha256=uSg7tVxyrtB12THM5RHP7qhz6bTLWREW9Ygd7Wq1d1k,804392
159
+ qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so,sha256=4SOZrbMeBBi7ep88enwT7hZh-KwHlyL_T4muHfF41bw,804392
160
160
  qubx/ta/indicators.pxd,sha256=r9mYcpDFxn3RW5lVJ497Fiq2-eqD4k7VX0-Q0xcftkk,4913
161
161
  qubx/ta/indicators.pyi,sha256=T87VwJrBJK8EuqtoW1Dhjri05scH_Y6BXN9kex6G7mQ,2881
162
162
  qubx/ta/indicators.pyx,sha256=jJMZ7D2kUspwiZITBnFuNPNvzqUm26EtEfSzBWSrLvQ,39286
@@ -211,8 +211,8 @@ qubx/utils/runner/factory.py,sha256=hmtUDYNFQwVQffHEfxgrlmKwOGLcFQ6uJIH_ZLscpIY,
211
211
  qubx/utils/runner/runner.py,sha256=xc575tmIOWuI1UN1YTdBsW5iA3WdhaQzR0XawV1UcPo,34337
212
212
  qubx/utils/time.py,sha256=xOWl_F6dOLFCmbB4xccLIx5yVt5HOH-I8ZcuowXjtBQ,11797
213
213
  qubx/utils/version.py,sha256=e52fIHyxzCiIuH7svCF6pkHuDlqL64rklqz-2XjWons,5309
214
- qubx-0.6.84.dist-info/METADATA,sha256=MEeOsYkrBJ8pNc2NtGHb_SEmOaAZdkQfMKamRVaP2cA,5909
215
- qubx-0.6.84.dist-info/WHEEL,sha256=RA6gLSyyVpI0R7d3ofBrM1iY5kDUsPwh15AF0XpvgQo,110
216
- qubx-0.6.84.dist-info/entry_points.txt,sha256=VqilDTe8mVuV9SbR-yVlZJBTjbkHIL2JBgXfQw076HY,47
217
- qubx-0.6.84.dist-info/licenses/LICENSE,sha256=qwMHOSJ2TD0nx6VUJvFhu1ynJdBfNozRMt6tnSul-Ts,35140
218
- qubx-0.6.84.dist-info/RECORD,,
214
+ qubx-0.6.85.dist-info/METADATA,sha256=ZOHrVXgrzG-6FeBQhqdnU41BZI1KUaCvdDAERuUw4EY,5909
215
+ qubx-0.6.85.dist-info/WHEEL,sha256=RA6gLSyyVpI0R7d3ofBrM1iY5kDUsPwh15AF0XpvgQo,110
216
+ qubx-0.6.85.dist-info/entry_points.txt,sha256=VqilDTe8mVuV9SbR-yVlZJBTjbkHIL2JBgXfQw076HY,47
217
+ qubx-0.6.85.dist-info/licenses/LICENSE,sha256=qwMHOSJ2TD0nx6VUJvFhu1ynJdBfNozRMt6tnSul-Ts,35140
218
+ qubx-0.6.85.dist-info/RECORD,,
File without changes