Qubx 0.2.64__cp311-cp311-manylinux_2_35_x86_64.whl → 0.2.65__cp311-cp311-manylinux_2_35_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/metrics.py CHANGED
@@ -11,6 +11,8 @@ import re
11
11
  from scipy import stats
12
12
  from scipy.stats import norm
13
13
  from statsmodels.regression.linear_model import OLS
14
+ from copy import copy
15
+ from itertools import chain
14
16
 
15
17
  import plotly.graph_objects as go
16
18
 
@@ -918,10 +920,13 @@ def chart_signals(
918
920
  end = executions.index[-1]
919
921
 
920
922
  if portfolio is not None:
921
- pnl = portfolio.filter(regex=f"{symbol}_PnL").loc[start:].cumsum()
923
+ symbol_count = len(portfolio.filter(like="_PnL").columns)
924
+ pnl = portfolio.filter(regex=f"{symbol}_PnL").cumsum() + result.capital / symbol_count
925
+ pnl = pnl.loc[start:]
922
926
  if apply_commissions:
923
927
  comm = portfolio.filter(regex=f"{symbol}_Commissions").loc[start:].cumsum()
924
928
  pnl -= comm.values
929
+ pnl = (pnl / pnl.iloc[0] - 1) * 100
925
930
  indicators["PnL"] = ["area", "green", pnl]
926
931
  if show_quantity:
927
932
  pos = portfolio.filter(regex=f"{symbol}_Pos").loc[start:]
@@ -1015,3 +1020,24 @@ def get_symbol_pnls(
1015
1020
  pnls.append(s.portfolio_log.filter(like="_PnL").cumsum().iloc[-1])
1016
1021
 
1017
1022
  return pd.DataFrame(pnls, index=[s.name for s in session])
1023
+
1024
+
1025
+ def combine_sessions(sessions: list[TradingSessionResult], name: str = "Portfolio") -> TradingSessionResult:
1026
+ session = copy(sessions[0])
1027
+ session.name = name
1028
+ session.instruments = list(set(chain.from_iterable([e.instruments for e in sessions])))
1029
+ session.portfolio_log = pd.concat(
1030
+ [e.portfolio_log.loc[:, (e.portfolio_log != 0).any(axis=0)] for e in sessions], axis=1
1031
+ )
1032
+ # remove duplicated columns, keep first
1033
+ session.portfolio_log = session.portfolio_log.loc[:, ~session.portfolio_log.columns.duplicated()]
1034
+ session.executions_log = pd.concat([s.executions_log for s in sessions], axis=0).sort_index()
1035
+ session.signals_log = pd.concat([s.signals_log for s in sessions], axis=0).sort_index()
1036
+ # remove duplicated rows
1037
+ session.executions_log = (
1038
+ session.executions_log.set_index("instrument_id", append=True).drop_duplicates().reset_index("instrument_id")
1039
+ )
1040
+ session.signals_log = (
1041
+ session.signals_log.set_index("instrument_id", append=True).drop_duplicates().reset_index("instrument_id")
1042
+ )
1043
+ return session
qubx/core/series.pxd CHANGED
@@ -36,11 +36,6 @@ cdef class Indicator(TimeSeries):
36
36
  cdef public TimeSeries series
37
37
  cdef public TimeSeries parent
38
38
 
39
-
40
- cdef class IndicatorOHLC(Indicator):
41
- pass
42
-
43
-
44
39
  cdef class RollingSum:
45
40
  """
46
41
  Rolling fast summator
@@ -49,7 +44,7 @@ cdef class RollingSum:
49
44
  cdef np.ndarray __s
50
45
  cdef unsigned int __i
51
46
  cdef double rsum
52
- cdef unsigned short is_init_stage
47
+ cdef public unsigned short is_init_stage
53
48
 
54
49
  cpdef double update(RollingSum self, double value, short new_item_started)
55
50
 
@@ -110,3 +105,8 @@ cdef class Quote:
110
105
  cdef public double ask_size
111
106
 
112
107
  cpdef double mid_price(Quote self)
108
+
109
+
110
+ cdef class IndicatorOHLC(Indicator):
111
+ pass
112
+
qubx/core/series.pyi CHANGED
@@ -83,6 +83,11 @@ class Indicator(TimeSeries):
83
83
 
84
84
  def update(self, time: int, value, new_item_started: bool) -> object: ...
85
85
 
86
- class IndicatorOHLC(Indicator): ...
86
+ class IndicatorOHLC(Indicator):
87
+ series: OHLCV
87
88
 
88
89
  def time_as_nsec(time: Any) -> np.datetime64: ...
90
+
91
+ class RollingSum:
92
+ def __init__(self, period: int) -> None: ...
93
+ def update(self, value: float, new_item_started: bool) -> float: ...
qubx/ta/indicators.pxd CHANGED
@@ -134,5 +134,6 @@ cdef class Swings(IndicatorOHLC):
134
134
  # tops_detection_lag contain time lag when top was actually spotted
135
135
  cdef public TimeSeries tops, tops_detection_lag
136
136
  cdef public TimeSeries bottoms, bottoms_detection_lag
137
+ cdef public TimeSeries middles, deltas
137
138
 
138
139
  cpdef double calculate(self, long long time, Bar bar, short new_item_started)
qubx/ta/indicators.pyi CHANGED
@@ -15,8 +15,20 @@ def smooth(series: TimeSeries, smoother: str, *args, **kwargs) -> Indicator: ...
15
15
  def atr(series: OHLCV, period: int = 14, smoother="sma", percentage: bool = False): ...
16
16
  def swings(series: OHLCV, trend_indicator, **indicator_args): ...
17
17
 
18
+ class Sma(Indicator):
19
+ def __init__(self, name: str, series: TimeSeries, period: int): ...
20
+
21
+ class Ema(Indicator):
22
+ def __init__(self, name: str, series: TimeSeries, period: int, init_mean: bool = True): ...
23
+
18
24
  class Kama(Indicator):
19
25
  def __init__(self, name: str, series: TimeSeries, period: int, fast_span: int = 2, slow_span: int = 30): ...
20
26
 
21
27
  class Atr(IndicatorOHLC):
22
28
  def __init__(self, name: str, series: OHLCV, period: int, smoother: str, percentage: bool): ...
29
+
30
+ class Swings(IndicatorOHLC):
31
+ tops: TimeSeries
32
+ bottoms: TimeSeries
33
+ middles: TimeSeries
34
+ deltas: TimeSeries
qubx/ta/indicators.pyx CHANGED
@@ -612,6 +612,9 @@ cdef class Swings(IndicatorOHLC):
612
612
  self.bottoms = TimeSeries("bottoms", series.timeframe, series.max_series_length)
613
613
  self.bottoms_detection_lag = TimeSeries("bottoms_lag", series.timeframe, series.max_series_length)
614
614
 
615
+ self.middles = TimeSeries("middles", series.timeframe, series.max_series_length)
616
+ self.deltas = TimeSeries("deltas", series.timeframe, series.max_series_length)
617
+
615
618
  self._min_l = +np.inf
616
619
  self._max_h = -np.inf
617
620
  self._max_t = 0
@@ -630,6 +633,9 @@ cdef class Swings(IndicatorOHLC):
630
633
  if self._max_t > 0:
631
634
  self.tops.update(self._max_t, self._max_h)
632
635
  self.tops_detection_lag.update(self._max_t, time - self._max_t)
636
+ if len(self.bottoms) > 0:
637
+ self.middles.update(time, (self.tops[0] + self.bottoms[0]) / 2)
638
+ self.deltas.update(time, self.tops[0] - self.bottoms[0])
633
639
 
634
640
  if bar.low <= self._min_l:
635
641
  self._min_l = bar.low
@@ -642,6 +648,9 @@ cdef class Swings(IndicatorOHLC):
642
648
  if self._min_t > 0:
643
649
  self.bottoms.update(self._min_t, self._min_l)
644
650
  self.bottoms_detection_lag.update(self._min_t, time - self._min_t)
651
+ if len(self.tops) > 0:
652
+ self.middles.update(time, (self.tops[0] + self.bottoms[0]) / 2)
653
+ self.deltas.update(time, self.tops[0] - self.bottoms[0])
645
654
 
646
655
  if bar.high >= self._max_h:
647
656
  self._max_h = bar.high
@@ -4,6 +4,7 @@ import plotly.graph_objects as go
4
4
  from plotly.graph_objs.graph_objs import FigureWidget
5
5
  from plotly.subplots import make_subplots
6
6
 
7
+ from qubx.core.series import OHLCV, TimeSeries
7
8
  from qubx.utils.charting.mpl_helpers import ohlc_plot
8
9
  from qubx.utils.charting.mpl_helpers import plot_trends
9
10
  from qubx.utils.charting.mpl_helpers import subplot
@@ -110,7 +111,7 @@ def install_plotly_helpers():
110
111
  arrowcolor=c,
111
112
  )
112
113
 
113
- def custom_hover(v, h=600, n=2, legend=False, show_info=True):
114
+ def hover(v, h=600, n=2, legend=False, show_info=True):
114
115
  return (
115
116
  v.update_traces(xaxis="x1")
116
117
  .update_layout(
@@ -140,7 +141,7 @@ def install_plotly_helpers():
140
141
  )
141
142
  )
142
143
 
143
- FigureWidget.hover = custom_hover # type: ignore
144
+ FigureWidget.hover = hover # type: ignore
144
145
  FigureWidget.rline = rline # type: ignore
145
146
  FigureWidget.rlinex = rlinex # type: ignore
146
147
  FigureWidget.rline_ = rline_ # type: ignore
@@ -287,7 +288,8 @@ class LookingGlassMatplotLib(AbstractLookingGlass):
287
288
  else:
288
289
  _lbl = y.name if hasattr(y, "name") and y.name else ("%s_%d" % (study_name, k))
289
290
 
290
- if isinstance(y, pd.DataFrame):
291
+ if isinstance(y, (pd.DataFrame, OHLCV)):
292
+ y = y.pd() if isinstance(y, OHLCV) else y
291
293
 
292
294
  yy = y[zoom] if zoom else y
293
295
 
@@ -429,6 +431,7 @@ class LookingGlassMatplotLib(AbstractLookingGlass):
429
431
  for _col in yy.columns:
430
432
  self.__plot_as_type(yy[_col], plot_style, self._n_style, _col)
431
433
  else:
434
+ y = y.pd() if isinstance(y, TimeSeries) else y
432
435
  yy = y[zoom] if zoom else y
433
436
  self.__plot_as_type(yy, plot_style, self._n_style, _lbl)
434
437
 
@@ -561,7 +564,8 @@ class LookingGlassPlotly(AbstractLookingGlass):
561
564
  )
562
565
  self.fig.add_trace(go.Scatter(**_args), row=row, col=col)
563
566
 
564
- if isinstance(y, pd.DataFrame):
567
+ if isinstance(y, (pd.DataFrame, OHLCV)):
568
+ y = y.pd() if isinstance(y, OHLCV) else y
565
569
  yy = y[zoom] if zoom else y
566
570
 
567
571
  # candlesticks
@@ -812,6 +816,7 @@ class LookingGlassPlotly(AbstractLookingGlass):
812
816
  for _col in yy.columns:
813
817
  self.__plot_as_type(yy[_col], row, col, plot_style, _col)
814
818
  else:
819
+ y = y.pd() if isinstance(y, TimeSeries) else y
815
820
  yy = y[zoom] if zoom else y
816
821
  self.__plot_as_type(yy, row, col, plot_style, _lbl)
817
822
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Qubx
3
- Version: 0.2.64
3
+ Version: 0.2.65
4
4
  Summary: Qubx - quantitative trading framework
5
5
  Home-page: https://github.com/dmarienko/Qubx
6
6
  Author: Dmitry Marienko
@@ -13,13 +13,13 @@ qubx/core/exceptions.py,sha256=W1gG_0fE3o2EMGUfOeOl3vVJDp8Z1iHLv4iZ0ThNkTs,306
13
13
  qubx/core/helpers.py,sha256=JAB1kcKO8l--gxg9_W2BzWW1N4x72sicw1oBszKYcbo,12657
14
14
  qubx/core/loggers.py,sha256=6P3NwoyHe_pTW8Kdvmi8eHoCxHAWm7LEvwgmX-vZKR4,16530
15
15
  qubx/core/lookups.py,sha256=qGOSb2SIxmgVPGJFLJnPDI1P9hCzFMVipRDcVLUm8qk,14599
16
- qubx/core/metrics.py,sha256=0PKXaG9hjoHAjAgrEqI9O1RAQshav5LSUIOewx3DX1A,36713
17
- qubx/core/series.cpython-311-x86_64-linux-gnu.so,sha256=wNnq-w1OGvdxwydX5wPZ8b4KUe7AR1G2lVXJZPIugvE,762408
18
- qubx/core/series.pxd,sha256=1KJGzun_R5GDDOynPbZER7Xg45a0JUM_S_lZ-9FFFJg,2994
19
- qubx/core/series.pyi,sha256=ipca4P8LJGDvkhAAczLoZbgLvcJSm-5SFKlaQI9cn9E,2254
16
+ qubx/core/metrics.py,sha256=ekralqco-lEW1wryUChQJXck4NXeBS_6o4raC9HIi-c,38010
17
+ qubx/core/series.cpython-311-x86_64-linux-gnu.so,sha256=cDOxqeo2LYUOJO0zdJstmVcwmtBEvtxdOaBl2HfiG5w,762504
18
+ qubx/core/series.pxd,sha256=34g0Q3VCGfqgseCIwOXj4QdIhwT1PO_Nt0nJjNCHBd8,3001
19
+ qubx/core/series.pyi,sha256=CHPQYl96qzfJkZwh1ZIUpLlnUxxfPsxCV54h_Bkwrok,2409
20
20
  qubx/core/series.pyx,sha256=7R0SQ3OKfxqdxlbBXAG1ld3WS-vrMT9us1Tvq21GLFY,31116
21
21
  qubx/core/strategy.py,sha256=RY2fdg7lvLquTxfUamewO51yaRVTElJLmariTB4e3CQ,10901
22
- qubx/core/utils.cpython-311-x86_64-linux-gnu.so,sha256=DZnZmsfZ11OXsi86SSNzp7l5M0RtHatqf1M1qSoWaS0,82504
22
+ qubx/core/utils.cpython-311-x86_64-linux-gnu.so,sha256=a4hfxig8tIu_qYfNl3GaQYOZHyYNq69Qkm-UuhpZYr0,82504
23
23
  qubx/core/utils.pyi,sha256=DAjyRVPJSxK4Em-9wui2F0yYHfP5tI5DjKavXNOnHa8,276
24
24
  qubx/core/utils.pyx,sha256=gWLTJzFvvqYlVjSbGccAizdkLinAUEVUcfF-eKWpCHo,1660
25
25
  qubx/data/helpers.py,sha256=A0NGzhpXYWD92-GeB8TghwMnR0NW8bjcNJOCXybQw3g,782
@@ -35,10 +35,10 @@ qubx/pandaz/__init__.py,sha256=Iw5uzicYGSC3FEKZ-W1O5-7cXq_P0kH11-EcXV0zZhs,175
35
35
  qubx/pandaz/ta.py,sha256=NthiiueUoqWGRcjovcKKThcCcdImZn3JRdWDA2vL28k,85075
36
36
  qubx/pandaz/utils.py,sha256=XB28Zwv3cXWbKFXbcV5QGj_d6w-i8Yo4LYkX8aPuCHo,19613
37
37
  qubx/ta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- qubx/ta/indicators.cpython-311-x86_64-linux-gnu.so,sha256=Ll6Je0gMYYxKCZerSiRKi_O2VZUQScAykrLW9SBrdhY,555560
39
- qubx/ta/indicators.pxd,sha256=N1XyVos-5IsMF4A0_nmaSRlocHnui5bFJGwJCSTkwd4,3898
40
- qubx/ta/indicators.pyi,sha256=2OL0BIKNCPQfe61TEI_nu4qgFYSjGc9ERGDoQlojWo8,1334
41
- qubx/ta/indicators.pyx,sha256=HW0NZb7DTY6_6eSrkvOOp7hg2ZATM0L_GZwm4VnKRJo,22647
38
+ qubx/ta/indicators.cpython-311-x86_64-linux-gnu.so,sha256=JIJfyHATfo-Cu82ly-AypZhP7UIbewsGbWFEj7MxZqM,563816
39
+ qubx/ta/indicators.pxd,sha256=N_p3HNlBDq5S7fRNgN4ZmmdslMBTS_nvn-vNn7T0vHQ,3941
40
+ qubx/ta/indicators.pyi,sha256=l63i_sk3wpy7HHAjgspOPjuIi6QLVPMfgpAHGHSKOcs,1670
41
+ qubx/ta/indicators.pyx,sha256=Cf2_38EnxqqwRXoGqfEoE3th9b4G0VllZ5jtnNYr2_E,23251
42
42
  qubx/trackers/__init__.py,sha256=-Hmlc8Mpdi_t6kyeW4ZtmYkHzrULaIb6gUsAkhUxA-4,169
43
43
  qubx/trackers/composite.py,sha256=rYQCaxu4IKLf5sS3DCyUUJaSUQwbCy6UMoVjsS-481A,6222
44
44
  qubx/trackers/rebalancers.py,sha256=dp-jemxczT8ndkjjzljsIgGnnA6lZQhsf5c4YdmHKCw,6048
@@ -46,13 +46,13 @@ qubx/trackers/riskctrl.py,sha256=8P5iyaJ-PKhIX8GkGBpncQdxnefL1QWRWG-Jx1PFTuk,125
46
46
  qubx/trackers/sizers.py,sha256=Wkpclaw7mfR_eMc9xMjN2AFgvOpY1jzaz3wDQvs9H0U,5988
47
47
  qubx/utils/__init__.py,sha256=AL2YibJ3tqBKsZZLUjM9N2J5yy-Kq__k_44oTODQ5sM,321
48
48
  qubx/utils/_pyxreloader.py,sha256=FyqGzfSpZGYziB8JYS5AP3cLRAvJSIPAKgwQn0E4YQ0,12017
49
- qubx/utils/charting/lookinglass.py,sha256=V9EiNXBdeL39ru_M6hPyHTOzFHoZsXiFPLjG4rWjkFk,38826
49
+ qubx/utils/charting/lookinglass.py,sha256=cyNGy-df5f9tg-gskb1IFUv7Nh8etn5a3BubdeQh7Ew,39111
50
50
  qubx/utils/charting/mpl_helpers.py,sha256=z6vL0IllTZeD-Oe-alVW-kYOP_deHyyoFcukvOVwSz8,35354
51
51
  qubx/utils/marketdata/binance.py,sha256=36dl4rxOAGTeY3uoONmiPanj8BkP0oBdDiH-URJJo9A,10993
52
52
  qubx/utils/misc.py,sha256=Av0mhrPCy5NZRrRmjOAhTKusa8wVdL7vCQtEy9bVnz4,10450
53
53
  qubx/utils/ntp.py,sha256=LZo4FPVY3rqLUV9VWkLcZaPOpUDFC8Qleynmfggg9No,1758
54
54
  qubx/utils/runner.py,sha256=Czo01KUCc9Oj9TIcs03d6Qh7fOpQV5w8oH6UDZ6Yqn0,9539
55
55
  qubx/utils/time.py,sha256=ioIos3VLA-iYI8LiQIn2m-t0Y37CEQAHFw_-C9Uwo0o,5188
56
- qubx-0.2.64.dist-info/METADATA,sha256=9EPXQJcE04ZWj_spVTcJ4GsSmWe8Zul0Y6wxKC7Hlnw,2573
57
- qubx-0.2.64.dist-info/WHEEL,sha256=MLOa6LysROdjgj4FVxsHitAnIh8Be2D_c9ZSBHKrz2M,110
58
- qubx-0.2.64.dist-info/RECORD,,
56
+ qubx-0.2.65.dist-info/METADATA,sha256=Ej6wJoKYygR3GPWrkKuLjgIORFZF2JRphvXyNN_Iyvo,2573
57
+ qubx-0.2.65.dist-info/WHEEL,sha256=MLOa6LysROdjgj4FVxsHitAnIh8Be2D_c9ZSBHKrz2M,110
58
+ qubx-0.2.65.dist-info/RECORD,,
File without changes