Qubx 0.6.11__cp312-cp312-manylinux_2_39_x86_64.whl → 0.6.12__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/core/basics.py CHANGED
@@ -8,7 +8,6 @@ from typing import Any, Literal, Optional, TypeAlias, Union
8
8
  import numpy as np
9
9
  import pandas as pd
10
10
 
11
- from qubx import logger
12
11
  from qubx.core.exceptions import QueueTimeout
13
12
  from qubx.core.series import Bar, OrderBook, Quote, Trade, time_as_nsec
14
13
  from qubx.core.utils import prec_ceil, prec_floor, time_delta_to_str
@@ -476,6 +475,7 @@ class Position:
476
475
  self.quantity = quantity
477
476
  self.position_avg_price = pos_average_price
478
477
  self.r_pnl = r_pnl
478
+ self.__pos_incr_qty = abs(quantity)
479
479
 
480
480
  def reset(self) -> None:
481
481
  """
@@ -540,6 +540,7 @@ class Position:
540
540
  deal_pnl = 0
541
541
  quantity = self.quantity
542
542
  comms = 0
543
+ # logger.info(f"{self.instrument.symbol} exec_price={exec_price} fee_amount={fee_amount} position={position}")
543
544
 
544
545
  if quantity != position:
545
546
  pos_change = position - quantity
@@ -558,6 +559,10 @@ class Position:
558
559
  quantity += qty_closing
559
560
  self.__pos_incr_qty -= _abs_qty_close
560
561
 
562
+ # logger.info(
563
+ # f"{self.instrument.symbol} qty_closing={qty_closing} deal_pnl={deal_pnl} quantity={quantity} pos_incr_qty={self.__pos_incr_qty} position_avg_price={self.position_avg_price}"
564
+ # )
565
+
561
566
  # - reset average price to 0 if smaller than minimal price change to avoid cumulative error
562
567
  if abs(quantity) < self.instrument.lot_size:
563
568
  quantity = 0.0
@@ -567,16 +572,14 @@ class Position:
567
572
  # - if it has something to add to position let's update price and cost
568
573
  if not np.isclose(qty_opening, 0.0):
569
574
  _abs_qty_open = abs(qty_opening)
570
- try:
571
- pos_avg_price_raw = (_abs_qty_open * exec_price + self.__pos_incr_qty * self.position_avg_price) / (
572
- self.__pos_incr_qty + _abs_qty_open
573
- )
574
- except ZeroDivisionError:
575
- logger.warning(
576
- "Zero division error in position update: "
577
- f"qty_opening={qty_opening}, exec_price={exec_price}, pos_incr_qty={self.__pos_incr_qty}, position_avg_price={self.position_avg_price}"
578
- )
579
- pos_avg_price_raw = self.position_avg_price
575
+
576
+ # logger.info(
577
+ # f"{self.instrument.symbol} qty_opening={qty_opening} exec_price={exec_price} pos_incr_qty={self.__pos_incr_qty} position_avg_price={self.position_avg_price}"
578
+ # )
579
+
580
+ pos_avg_price_raw = (_abs_qty_open * exec_price + self.__pos_incr_qty * self.position_avg_price) / (
581
+ self.__pos_incr_qty + _abs_qty_open
582
+ )
580
583
 
581
584
  # - round position average price to be in line with how it's calculated by broker
582
585
  self.position_avg_price = self.instrument.round_price_down(pos_avg_price_raw)
qubx/core/loggers.py CHANGED
@@ -216,8 +216,10 @@ class _BaseIntervalDumper:
216
216
  def store(self, timestamp: np.datetime64):
217
217
  _t_ns = time_as_nsec(timestamp)
218
218
  if self._freq:
219
- _interval_start_time = int(_t_ns - _t_ns % self._freq)
220
- if _t_ns - self._last_log_time_ns >= self._freq:
219
+ # Convert freq to nanoseconds for calculation
220
+ _freq_ns = self._freq.astype("int64")
221
+ _interval_start_time = int(_t_ns - (_t_ns % _freq_ns))
222
+ if _t_ns - self._last_log_time_ns >= _freq_ns:
221
223
  self.dump(np.datetime64(_interval_start_time, "ns"), timestamp)
222
224
  self._last_log_time_ns = _interval_start_time
223
225
  else:
@@ -412,22 +414,29 @@ class SignalsLogger(_BaseIntervalDumper):
412
414
 
413
415
  class BalanceLogger(_BaseIntervalDumper):
414
416
  """
415
- Balance logger - send balance on strategy start
417
+ Balance logger - save balance information at regular intervals similar to positions
416
418
  """
417
419
 
418
420
  _writer: LogsWriter
421
+ _balance: dict[str, AssetBalance]
419
422
 
420
- def __init__(self, writer: LogsWriter) -> None:
421
- super().__init__(None) # no intervals
423
+ def __init__(self, writer: LogsWriter, interval: str) -> None:
424
+ super().__init__(interval)
422
425
  self._writer = writer
426
+ self._balance = {}
423
427
 
424
- def record_balance(self, timestamp: np.datetime64, balance: Dict[str, AssetBalance]):
428
+ def record_balance(self, timestamp: np.datetime64, balance: dict[str, AssetBalance]):
425
429
  if balance:
430
+ self._balance = balance
431
+ self.dump(timestamp, timestamp)
432
+
433
+ def dump(self, interval_start_time: np.datetime64, actual_timestamp: np.datetime64):
434
+ if self._balance:
426
435
  data = []
427
- for s, d in balance.items():
436
+ for s, d in self._balance.items():
428
437
  data.append(
429
438
  {
430
- "timestamp": timestamp,
439
+ "timestamp": str(interval_start_time),
431
440
  "currency": s,
432
441
  "total": d.total,
433
442
  "locked": d.locked,
@@ -435,9 +444,6 @@ class BalanceLogger(_BaseIntervalDumper):
435
444
  )
436
445
  self._writer.write_data("balance", data)
437
446
 
438
- def store(self, timestamp: np.datetime64):
439
- pass
440
-
441
447
  def close(self):
442
448
  self._writer.flush_data()
443
449
 
@@ -484,7 +490,7 @@ class StrategyLogging:
484
490
  self.signals_logger = SignalsLogger(logs_writer, num_signals_records_to_write)
485
491
 
486
492
  # - balance logger
487
- self.balance_logger = BalanceLogger(logs_writer)
493
+ self.balance_logger = BalanceLogger(logs_writer, positions_log_freq)
488
494
  else:
489
495
  logger.warning("Log writer is not defined - strategy activity will not be saved !")
490
496
 
@@ -534,6 +540,10 @@ class StrategyLogging:
534
540
  if self.portfolio_logger:
535
541
  self.portfolio_logger.store(timestamp)
536
542
 
543
+ # - notify balance logger
544
+ if self.balance_logger:
545
+ self.balance_logger.store(timestamp)
546
+
537
547
  # - log heartbeat
538
548
  self._log_heartbeat(timestamp)
539
549
 
@@ -43,7 +43,7 @@ class StateResolver:
43
43
  ctx.trade(instrument, -live_qty)
44
44
 
45
45
  # If live position is larger than sim position (same direction), reduce it
46
- elif abs(live_qty) > abs(sim_qty) and abs(sim_qty) > instrument.lot_size:
46
+ elif abs(live_qty) > abs(sim_qty) and abs(live_qty) > instrument.lot_size:
47
47
  qty_diff = sim_qty - live_qty
48
48
  logger.info(
49
49
  f"Reducing position for {instrument.symbol}: {live_qty} -> {sim_qty} (diff: {qty_diff})"
qubx/restorers/signal.py CHANGED
@@ -78,7 +78,12 @@ class CsvSignalRestorer(ISignalRestorer):
78
78
 
79
79
  try:
80
80
  # Read the CSV file
81
- df = pd.read_csv(file_path)
81
+ try:
82
+ df = pd.read_csv(file_path)
83
+ except Exception as e:
84
+ logger.info(f"Could not read signal file {file_path}: {e}")
85
+ return {}
86
+
82
87
  if df.empty:
83
88
  logger.info(f"No signals found in {file_path}")
84
89
  return {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: Qubx
3
- Version: 0.6.11
3
+ Version: 0.6.12
4
4
  Summary: Qubx - Quantitative Trading Framework
5
5
  Author: Dmitry Marienko
6
6
  Author-email: dmitry.marienko@xlydian.com
@@ -27,13 +27,13 @@ qubx/connectors/ccxt/reader.py,sha256=qaZIaOZkRf3Rz31ZrEqqAv4kATk5zDlSq-LK1jziBs
27
27
  qubx/connectors/ccxt/utils.py,sha256=kWeYQ1Z9TxFTbgJgBh5o77mGB1g09l2AkiFdR6guDQY,11141
28
28
  qubx/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  qubx/core/account.py,sha256=Ewzmr1jxyLROnWshtx_ngi7fna1cH42NJ5wVRyP4dmQ,10411
30
- qubx/core/basics.py,sha256=O_m9sfmLoaYuQx-3q-zv-LQOhG8ARrUSJ73i5fqr0Yw,29164
30
+ qubx/core/basics.py,sha256=sPB3dL0FYF_bYALySnUrOwGLQfKb7B9dkK3PIV1MCFM,29364
31
31
  qubx/core/context.py,sha256=t4A1sWM6dn4t-LSKOC-66x4Y-_5aGduEedO_Gmd3Yrg,20593
32
32
  qubx/core/exceptions.py,sha256=ONFzWISjWfb2S0kEIorq-3L4MdhNR-xkHftHQALOZ0U,533
33
33
  qubx/core/helpers.py,sha256=9nl9L_ZzT1HsMC9VthMqXfmuRS_37crB-9bVfIRHeOs,19631
34
34
  qubx/core/initializer.py,sha256=PUiD_cIjvGpuPjYyRpUjpwm3xNQ2Kipa8bAhbtxCQRo,3935
35
35
  qubx/core/interfaces.py,sha256=5ldBmncZHonf5ymSUbcIDjZx68127v9wEnppMSjhWow,49122
36
- qubx/core/loggers.py,sha256=q3obb5NC_9rdm3bDvZRtE3GEdiEo6vKwWlmu5O8kwCg,19065
36
+ qubx/core/loggers.py,sha256=eYhJANHYwz1heeFMa5V7jYCL196wkTSvj6c-8lkPj1Y,19567
37
37
  qubx/core/lookups.py,sha256=KBE0ab4eheA6C5C-RIND_svYhE7Glb4CSlLRjMhPNRc,15906
38
38
  qubx/core/metrics.py,sha256=2AocZUYwoZE8OvScN3ULa-wyVregzlCn722QIZUe7Q8,57784
39
39
  qubx/core/mixins/__init__.py,sha256=AMCLvfNuIb1kkQl3bhCj9jIOEl2eKcVPJeyLgrkB-rk,329
@@ -42,11 +42,11 @@ qubx/core/mixins/processing.py,sha256=Ny9EyrTuF_YBmPsAebykV54OfGMBGE1gSam9VFJJKa
42
42
  qubx/core/mixins/subscription.py,sha256=J_SX0CNw2bPy4bhxe0vswvDXY4LCkwXSaj_1PepKRLY,8540
43
43
  qubx/core/mixins/trading.py,sha256=KApWQE0zIh1jg_9HezLdR-mp3UEYIdylSyX9MajHmCc,3618
44
44
  qubx/core/mixins/universe.py,sha256=1ya2P3QZrsAVXmMXqq0t6CHGAC5iMGVD2ARUAtSfv04,10062
45
- qubx/core/series.cpython-312-x86_64-linux-gnu.so,sha256=APsllVvq0Nc5QyZAYfOIKFPVFroKbJLH7p56viwHDms,970056
45
+ qubx/core/series.cpython-312-x86_64-linux-gnu.so,sha256=oGjkxetdjaacB1CRU3AZ09xbh8T8AsNhl5DswDeWs9Q,970056
46
46
  qubx/core/series.pxd,sha256=ZA9mBrZ-ha112ZMZnBIun1kNrKjxzlaOoq4D_cDZYJw,3923
47
47
  qubx/core/series.pyi,sha256=r6o2SnYIBujmJuuoM8oEc3tbVg2Blz2CfSIdRKn9VMo,4547
48
48
  qubx/core/series.pyx,sha256=Y6GbCihO3zgUWOF9UvmyXisWLnGcVOcFl2u1ZtvM1zI,44909
49
- qubx/core/utils.cpython-312-x86_64-linux-gnu.so,sha256=HX-7iU5diG9cXb1EGWZSjuiScWsX5kN5TfbUUnvB35k,86568
49
+ qubx/core/utils.cpython-312-x86_64-linux-gnu.so,sha256=WIcuOQ9itXm60-ItviKaUZHRSlARcCz0e-925ZpzSio,86568
50
50
  qubx/core/utils.pyi,sha256=a-wS13V2p_dM1CnGq40JVulmiAhixTwVwt0ah5By0Hc,348
51
51
  qubx/core/utils.pyx,sha256=k5QHfEFvqhqWfCob89ANiJDKNG8gGbOh-O4CVoneZ8M,1696
52
52
  qubx/data/__init__.py,sha256=ELZykvpPGWc5rX7QoNyNQwMLgdKMG8MACOByA4pM5hA,549
@@ -93,18 +93,18 @@ qubx/resources/instruments/symbols-bitfinex.json,sha256=CpzoVgWzGZRN6RpUNhtJVxa3
93
93
  qubx/resources/instruments/symbols-kraken.f.json,sha256=lwNqml3H7lNUl1h3siySSyE1MRcGfqfhb6BcxLsiKr0,212258
94
94
  qubx/resources/instruments/symbols-kraken.json,sha256=RjUTvkQuuu7V1HfSQREvnA4qqkdkB3-rzykDaQds2rQ,456544
95
95
  qubx/restarts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
96
- qubx/restarts/state_resolvers.py,sha256=IGcYn_r5lt5U871-V5LxP67PL8yD7bencjGtjTSLH4U,5131
96
+ qubx/restarts/state_resolvers.py,sha256=nWdGgMXNIZbnadmAh1-cTvT5KXRKbQxXv-2Kgvp67qY,5132
97
97
  qubx/restarts/time_finders.py,sha256=r7yyRhJByV2uqdgamDRX2XClwpWWI9BNpc80t9nk6c0,2448
98
98
  qubx/restorers/__init__.py,sha256=vrnZBPJHR0-6knAccj4bK0tkjUPNRl32qiLr5Mv4aR0,911
99
99
  qubx/restorers/balance.py,sha256=ipkrRSVAscC_if6jaFtNMexHK6Z2teds2IzWdcS9yAI,3964
100
100
  qubx/restorers/factory.py,sha256=vq78vvf_ASKa-rGsV9UZlob7HCHMkiKIlLYUeCfB16g,6431
101
101
  qubx/restorers/interfaces.py,sha256=CcjBWavKq8_GIMKTSPodMa-n3wJQwcQTwyvYyNo_J3c,1776
102
102
  qubx/restorers/position.py,sha256=_I_LNPXXTshxlI9hQS2ANO54JwDwseXU_PJgMmZmFCY,4764
103
- qubx/restorers/signal.py,sha256=iruHe4ieTSV73zeJbFPubsmAEf4TzVXnkM4zjupCAJQ,6462
103
+ qubx/restorers/signal.py,sha256=DBLqA7vDhoMTAzUC4N9UerrO0GbjeHdTeMoCz7U7iI8,6621
104
104
  qubx/restorers/state.py,sha256=ePmh604fp2kRYzMNXL-TWvZOxmtTGAaKYfHJcnKResY,4042
105
105
  qubx/restorers/utils.py,sha256=We2gfqwQKWziUYhuUnjb-xo-5tSlbuHWpPQn0CEMTn0,1155
106
106
  qubx/ta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
107
- qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so,sha256=iqWHwrqYR28G5SX-XCN3d9Jssc6baqZFLoQ3qRKl-OI,654440
107
+ qubx/ta/indicators.cpython-312-x86_64-linux-gnu.so,sha256=JfqqbFytabhLARoUwoDKuGXw3uPrl6Tf0eg8L944pB4,654440
108
108
  qubx/ta/indicators.pxd,sha256=eCJ9paOxtxbDFx4U5CUhcgB1jjCQAfVqMF2FnbJ03Lo,4222
109
109
  qubx/ta/indicators.pyi,sha256=19W0uERft49In5bf9jkJHkzJYEyE9gzudN7_DJ5Vdv8,1963
110
110
  qubx/ta/indicators.pyx,sha256=FVkv5ld04TpZMT3a_kR1MU3IUuWfijzjJnh_lG78JxM,26029
@@ -138,7 +138,7 @@ qubx/utils/runner/configs.py,sha256=vtl04A42kT21hD68zpoZZw96rEC8HnFFX9en0Y85GLA,
138
138
  qubx/utils/runner/runner.py,sha256=kwigJ9MgBcOkDpYAHj2TuwQzNerwyUWaE4UmyNYxD34,37910
139
139
  qubx/utils/time.py,sha256=J0ZFGjzFL5T6GA8RPAel8hKG0sg2LZXeQ5YfDCfcMHA,10055
140
140
  qubx/utils/version.py,sha256=e52fIHyxzCiIuH7svCF6pkHuDlqL64rklqz-2XjWons,5309
141
- qubx-0.6.11.dist-info/METADATA,sha256=0jn6yriiV1zR00FDlO0QiOkGdC8vQo8Se8dbErd3jNE,4142
142
- qubx-0.6.11.dist-info/WHEEL,sha256=h1DdjcD2ZFnKGsDLjEycQhNNPJ5l-R8qdFdDSXHrAGY,110
143
- qubx-0.6.11.dist-info/entry_points.txt,sha256=VqilDTe8mVuV9SbR-yVlZJBTjbkHIL2JBgXfQw076HY,47
144
- qubx-0.6.11.dist-info/RECORD,,
141
+ qubx-0.6.12.dist-info/METADATA,sha256=wrLTsq0yccxDnyeB2sHqCU6p6jiS8A5DjHZtivEVoWI,4142
142
+ qubx-0.6.12.dist-info/WHEEL,sha256=h1DdjcD2ZFnKGsDLjEycQhNNPJ5l-R8qdFdDSXHrAGY,110
143
+ qubx-0.6.12.dist-info/entry_points.txt,sha256=VqilDTe8mVuV9SbR-yVlZJBTjbkHIL2JBgXfQw076HY,47
144
+ qubx-0.6.12.dist-info/RECORD,,
File without changes