Qubx 0.2.64__tar.gz → 0.2.65__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.
- {qubx-0.2.64 → qubx-0.2.65}/PKG-INFO +1 -1
- {qubx-0.2.64 → qubx-0.2.65}/pyproject.toml +1 -1
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/metrics.py +27 -1
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/series.pxd +6 -6
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/series.pyi +6 -1
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/ta/indicators.pxd +1 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/ta/indicators.pyi +12 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/ta/indicators.pyx +9 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/utils/charting/lookinglass.py +9 -4
- {qubx-0.2.64 → qubx-0.2.65}/README.md +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/build.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/__init__.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/_nb_magic.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/backtester/__init__.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/backtester/ome.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/backtester/optimization.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/backtester/queue.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/backtester/simulator.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/__init__.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/account.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/basics.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/context.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/exceptions.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/helpers.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/loggers.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/lookups.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/series.pyx +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/strategy.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/utils.pyi +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/core/utils.pyx +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/data/helpers.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/data/readers.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/gathering/simplest.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/impl/ccxt_connector.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/impl/ccxt_customizations.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/impl/ccxt_trading.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/impl/ccxt_utils.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/math/__init__.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/math/stats.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/pandaz/__init__.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/pandaz/ta.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/pandaz/utils.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/ta/__init__.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/trackers/__init__.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/trackers/composite.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/trackers/rebalancers.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/trackers/riskctrl.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/trackers/sizers.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/utils/__init__.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/utils/_pyxreloader.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/utils/charting/mpl_helpers.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/utils/marketdata/binance.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/utils/misc.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/utils/ntp.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/utils/runner.py +0 -0
- {qubx-0.2.64 → qubx-0.2.65}/src/qubx/utils/time.py +0 -0
|
@@ -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
|
-
|
|
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
|
|
@@ -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
|
+
|
|
@@ -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: ...
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -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
|
|
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 =
|
|
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
|
|
|
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
|