Qubx 0.2.4__cp311-cp311-manylinux_2_35_x86_64.whl → 0.2.6__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/backtester/queue.py +2 -2
- qubx/backtester/simulator.py +6 -0
- qubx/core/basics.py +3 -2
- qubx/core/helpers.py +2 -1
- qubx/core/metrics.py +39 -27
- qubx/core/series.cpython-311-x86_64-linux-gnu.so +0 -0
- qubx/core/series.pyi +16 -0
- qubx/core/utils.cpython-311-x86_64-linux-gnu.so +0 -0
- qubx/data/readers.py +1 -1
- qubx/pandaz/ta.py +1 -1
- qubx/ta/indicators.cpython-311-x86_64-linux-gnu.so +0 -0
- qubx/ta/indicators.pxd +138 -0
- qubx/ta/indicators.pyi +7 -1
- qubx/ta/indicators.pyx +8 -82
- qubx/trackers/sizers.py +9 -1
- {qubx-0.2.4.dist-info → qubx-0.2.6.dist-info}/METADATA +1 -1
- {qubx-0.2.4.dist-info → qubx-0.2.6.dist-info}/RECORD +18 -17
- {qubx-0.2.4.dist-info → qubx-0.2.6.dist-info}/WHEEL +0 -0
qubx/backtester/queue.py
CHANGED
|
@@ -137,7 +137,7 @@ class SimulatedDataQueue:
|
|
|
137
137
|
def __iter__(self) -> Iterator:
|
|
138
138
|
logger.debug("Initializing chunks for each loader")
|
|
139
139
|
assert self._start is not None
|
|
140
|
-
self._current_time = int(pd.Timestamp(self._start).timestamp() *
|
|
140
|
+
self._current_time = int(pd.Timestamp(self._start).timestamp() * 1e9)
|
|
141
141
|
self._index_to_chunk_size = {}
|
|
142
142
|
self._index_to_iterator = {}
|
|
143
143
|
self._event_heap = []
|
|
@@ -185,7 +185,7 @@ class SimulatedDataQueue:
|
|
|
185
185
|
@_SW.watch("DataQueue")
|
|
186
186
|
def _next_chunk(self, index: int) -> list[Any]:
|
|
187
187
|
if index not in self._index_to_iterator:
|
|
188
|
-
self._index_to_iterator[index] = self._index_to_loader[index].load(self._current_time, self._stop) # type: ignore
|
|
188
|
+
self._index_to_iterator[index] = self._index_to_loader[index].load(pd.Timestamp(self._current_time, unit="ns"), self._stop) # type: ignore
|
|
189
189
|
iterator = self._index_to_iterator[index]
|
|
190
190
|
try:
|
|
191
191
|
return next(iterator)
|
qubx/backtester/simulator.py
CHANGED
|
@@ -871,6 +871,9 @@ def _run_setup(
|
|
|
871
871
|
_trigger = "bar: 0s"
|
|
872
872
|
_stop = setup.generator.index[-1] # type: ignore
|
|
873
873
|
|
|
874
|
+
# - no historical data for generated signals, so disable it
|
|
875
|
+
enable_event_batching = False
|
|
876
|
+
|
|
874
877
|
case _Types.SIGNAL_AND_TRACKER:
|
|
875
878
|
strat = _GeneratedSignalsStrategy()
|
|
876
879
|
strat.tracker = lambda ctx: setup.tracker
|
|
@@ -879,6 +882,9 @@ def _run_setup(
|
|
|
879
882
|
_trigger = "bar: 0s"
|
|
880
883
|
_stop = setup.generator.index[-1] # type: ignore
|
|
881
884
|
|
|
885
|
+
# - no historical data for generated signals, so disable it
|
|
886
|
+
enable_event_batching = False
|
|
887
|
+
|
|
882
888
|
case _:
|
|
883
889
|
raise ValueError(f"Unsupported setup type: {setup.setup_type} !")
|
|
884
890
|
|
qubx/core/basics.py
CHANGED
|
@@ -146,7 +146,8 @@ class Instrument:
|
|
|
146
146
|
take: float | None = None,
|
|
147
147
|
group: str = "",
|
|
148
148
|
comment: str = "",
|
|
149
|
-
options: dict[str, Any] = None,
|
|
149
|
+
options: dict[str, Any] | None = None, # - probably we need to remove it ?
|
|
150
|
+
**kwargs,
|
|
150
151
|
) -> Signal:
|
|
151
152
|
return Signal(
|
|
152
153
|
self,
|
|
@@ -156,7 +157,7 @@ class Instrument:
|
|
|
156
157
|
take=take,
|
|
157
158
|
group=group,
|
|
158
159
|
comment=comment,
|
|
159
|
-
options=options or {},
|
|
160
|
+
options=(options or {}) | kwargs,
|
|
160
161
|
)
|
|
161
162
|
|
|
162
163
|
def __hash__(self) -> int:
|
qubx/core/helpers.py
CHANGED
|
@@ -353,7 +353,8 @@ def set_parameters_to_object(strategy: Any, **kwargs):
|
|
|
353
353
|
raise ValueError("Internal variable can't be set from external parameter !")
|
|
354
354
|
if hasattr(strategy, k):
|
|
355
355
|
strategy.__dict__[k] = v
|
|
356
|
-
|
|
356
|
+
v_str = str(v).replace(">", "").replace("<", "")
|
|
357
|
+
_log_info += f"\n\tset <green>{k}</green> <- <red>{v_str}</red>"
|
|
357
358
|
|
|
358
359
|
if _log_info:
|
|
359
360
|
logger.info(f"<yellow>{strategy.__class__.__name__}</yellow> new parameters:" + _log_info)
|
qubx/core/metrics.py
CHANGED
|
@@ -661,19 +661,23 @@ def portfolio_metrics(
|
|
|
661
661
|
returns_on_init_bp = returns_on_init_bp[:end]
|
|
662
662
|
|
|
663
663
|
# aggregate them to daily (if we have intraday portfolio)
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
664
|
+
try:
|
|
665
|
+
if infer_series_frequency(returns) < pd.Timedelta("1D").to_timedelta64():
|
|
666
|
+
returns_daily = aggregate_returns(returns, "daily")
|
|
667
|
+
returns_on_init_bp = aggregate_returns(returns_on_init_bp, "daily")
|
|
668
|
+
except:
|
|
669
|
+
returns_daily = returns
|
|
667
670
|
|
|
668
671
|
# todo: add transaction_cost calculations
|
|
669
672
|
equity = init_cash + pft_total["Total_PnL"]
|
|
670
673
|
mdd, ddstart, ddpeak, ddrecover, dd_data = absmaxdd(equity)
|
|
671
|
-
|
|
674
|
+
execs = len(executions_log)
|
|
675
|
+
mdd_pct = 100 * dd_data / equity.cummax() if execs > 0 else pd.Series(0, index=equity.index)
|
|
672
676
|
sheet["equity"] = equity
|
|
673
677
|
sheet["gain"] = sheet["equity"].iloc[-1] - sheet["equity"].iloc[0]
|
|
674
678
|
sheet["cagr"] = cagr(returns_daily, performance_statistics_period)
|
|
675
679
|
sheet["sharpe"] = sharpe_ratio(returns_daily, risk_free, performance_statistics_period)
|
|
676
|
-
sheet["qr"] = qr(equity)
|
|
680
|
+
sheet["qr"] = qr(equity) if execs > 0 else 0
|
|
677
681
|
sheet["drawdown_usd"] = dd_data
|
|
678
682
|
sheet["drawdown_pct"] = mdd_pct
|
|
679
683
|
# 25-May-2019: MDE fixed Max DD pct calculations
|
|
@@ -709,7 +713,7 @@ def portfolio_metrics(
|
|
|
709
713
|
sheet["fees"] = pft_total["Total_Commissions"].iloc[-1]
|
|
710
714
|
|
|
711
715
|
# executions metrics
|
|
712
|
-
sheet["execs"] =
|
|
716
|
+
sheet["execs"] = execs
|
|
713
717
|
|
|
714
718
|
return sheet
|
|
715
719
|
|
|
@@ -722,6 +726,7 @@ def tearsheet(
|
|
|
722
726
|
timeframe: str | pd.Timedelta | None = None,
|
|
723
727
|
sort_by: str | None = "Sharpe",
|
|
724
728
|
sort_ascending: bool = False,
|
|
729
|
+
plot_equities: bool = True,
|
|
725
730
|
):
|
|
726
731
|
if timeframe is None:
|
|
727
732
|
timeframe = _estimate_timeframe(session)
|
|
@@ -739,19 +744,21 @@ def tearsheet(
|
|
|
739
744
|
for s in session:
|
|
740
745
|
report, mtrx = _pfl_metrics_prepare(s, account_transactions, performance_statistics_period)
|
|
741
746
|
_rs.append(report)
|
|
742
|
-
if
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
747
|
+
if plot_equities:
|
|
748
|
+
if compound:
|
|
749
|
+
# _eq.append(pd.Series(100 * mtrx["compound_returns"], name=s.trading_id))
|
|
750
|
+
compound_returns = mtrx["compound_returns"].resample(timeframe).ffill()
|
|
751
|
+
plt.plot(100 * compound_returns, label=s.name)
|
|
752
|
+
else:
|
|
753
|
+
# _eq.append(pd.Series(mtrx["equity"], name=s.trading_id))
|
|
754
|
+
equity = mtrx["equity"].resample(timeframe).ffill()
|
|
755
|
+
plt.plot(equity, label=s.name)
|
|
756
|
+
|
|
757
|
+
if plot_equities:
|
|
758
|
+
if len(session) <= 15:
|
|
759
|
+
plt.legend(ncol=max(1, len(session) // 5))
|
|
760
|
+
plt.title("Comparison of Equity Curves")
|
|
761
|
+
|
|
755
762
|
report = pd.concat(_rs, axis=1).T
|
|
756
763
|
report["id"] = [s.id for s in session]
|
|
757
764
|
report = report.set_index("id", append=True).swaplevel()
|
|
@@ -787,9 +794,11 @@ def get_equity(
|
|
|
787
794
|
return _get_single_equity(sessions)
|
|
788
795
|
|
|
789
796
|
|
|
790
|
-
def _estimate_timeframe(
|
|
797
|
+
def _estimate_timeframe(
|
|
798
|
+
session: TradingSessionResult | list[TradingSessionResult], start: str | None = None, stop: str | None = None
|
|
799
|
+
) -> str:
|
|
791
800
|
session = session[0] if isinstance(session, list) else session
|
|
792
|
-
start, end = pd.Timestamp(session.start), pd.Timestamp(session.stop)
|
|
801
|
+
start, end = pd.Timestamp(start or session.start), pd.Timestamp(stop or session.stop)
|
|
793
802
|
diff = end - start
|
|
794
803
|
if diff > pd.Timedelta("360d"):
|
|
795
804
|
return "1d"
|
|
@@ -882,6 +891,7 @@ def chart_signals(
|
|
|
882
891
|
indicators={},
|
|
883
892
|
overlay=[],
|
|
884
893
|
info=True,
|
|
894
|
+
show_trades: bool = True,
|
|
885
895
|
show_quantity: bool = False,
|
|
886
896
|
show_value: bool = False,
|
|
887
897
|
show_leverage: bool = True,
|
|
@@ -892,6 +902,8 @@ def chart_signals(
|
|
|
892
902
|
Show trading signals on chart
|
|
893
903
|
"""
|
|
894
904
|
indicators = indicators | {}
|
|
905
|
+
if timeframe is None:
|
|
906
|
+
timeframe = _estimate_timeframe(result, start, end)
|
|
895
907
|
|
|
896
908
|
executions = result.executions_log.rename(
|
|
897
909
|
columns={"instrument_id": "instrument", "filled_qty": "quantity", "price": "exec_price"}
|
|
@@ -947,13 +959,13 @@ def chart_signals(
|
|
|
947
959
|
|
|
948
960
|
indicators = {k: __resample(v) for k, v in indicators.items()}
|
|
949
961
|
|
|
950
|
-
|
|
951
|
-
["
|
|
952
|
-
|
|
962
|
+
if show_trades:
|
|
963
|
+
excs = executions[executions["instrument"] == symbol][
|
|
964
|
+
["quantity", "exec_price", "commissions", "commissions_quoted"]
|
|
965
|
+
]
|
|
966
|
+
overlay = list(overlay) + [excs]
|
|
953
967
|
|
|
954
|
-
chart = (
|
|
955
|
-
LookingGlass([bars, excs, *overlay], indicators).look(start, end, title=symbol).hover(show_info=info, h=height)
|
|
956
|
-
)
|
|
968
|
+
chart = LookingGlass([bars, *overlay], indicators).look(start, end, title=symbol).hover(show_info=info, h=height)
|
|
957
969
|
if not show_table:
|
|
958
970
|
return chart.show()
|
|
959
971
|
|
|
Binary file
|
qubx/core/series.pyi
CHANGED
|
@@ -4,6 +4,11 @@ from typing import Any, Tuple
|
|
|
4
4
|
import pandas as pd
|
|
5
5
|
|
|
6
6
|
class Bar:
|
|
7
|
+
open: float
|
|
8
|
+
high: float
|
|
9
|
+
low: float
|
|
10
|
+
close: float
|
|
11
|
+
volume: float
|
|
7
12
|
def __init__(self, time, open, high, low, close, volume, bought_volume=0): ...
|
|
8
13
|
|
|
9
14
|
class Quote:
|
|
@@ -28,8 +33,16 @@ class Locator:
|
|
|
28
33
|
def __getitem__(self, idx): ...
|
|
29
34
|
def find(self, t: str) -> Tuple[np.datetime64, Any]: ...
|
|
30
35
|
|
|
36
|
+
class Indexed:
|
|
37
|
+
def __getitem__(self, idx): ...
|
|
38
|
+
|
|
31
39
|
class TimeSeries:
|
|
40
|
+
name: str
|
|
32
41
|
loc: Locator
|
|
42
|
+
timeframe: int
|
|
43
|
+
max_series_length: int
|
|
44
|
+
times: Indexed
|
|
45
|
+
values: Indexed
|
|
33
46
|
def __init__(self, name, timeframe, max_series_length, process_every_update=True) -> None: ...
|
|
34
47
|
def __getitem__(self, idx): ...
|
|
35
48
|
def update(self, time: int, value: float) -> bool: ...
|
|
@@ -65,6 +78,9 @@ class OHLCV(TimeSeries):
|
|
|
65
78
|
def pd(self) -> pd.DataFrame: ...
|
|
66
79
|
|
|
67
80
|
class Indicator(TimeSeries):
|
|
81
|
+
name: str
|
|
82
|
+
series: TimeSeries
|
|
83
|
+
|
|
68
84
|
def update(self, time: int, value, new_item_started: bool) -> object: ...
|
|
69
85
|
|
|
70
86
|
class IndicatorOHLC(Indicator): ...
|
|
Binary file
|
qubx/data/readers.py
CHANGED
|
@@ -789,7 +789,7 @@ class QuestDBSqlCandlesBuilder(QuestDBSqlBuilder):
|
|
|
789
789
|
|
|
790
790
|
where = f"where symbol = '{symb}'"
|
|
791
791
|
w0 = f"timestamp >= '{start}'" if start else ""
|
|
792
|
-
w1 = f"timestamp
|
|
792
|
+
w1 = f"timestamp < '{end}'" if end else ""
|
|
793
793
|
|
|
794
794
|
# - fix: when no data ranges are provided we must skip empy where keyword
|
|
795
795
|
if w0 or w1:
|
qubx/pandaz/ta.py
CHANGED
|
@@ -695,7 +695,7 @@ def macd(x: pd.Series, fast=12, slow=26, signal=9, method="ema", signal_method="
|
|
|
695
695
|
return smooth(x_diff, signal_method, signal).rename("macd")
|
|
696
696
|
|
|
697
697
|
|
|
698
|
-
def atr(x: pd.
|
|
698
|
+
def atr(x: pd.DataFrame, window=14, smoother="sma", percentage=False) -> pd.Series:
|
|
699
699
|
"""
|
|
700
700
|
Average True Range indicator
|
|
701
701
|
|
|
Binary file
|
qubx/ta/indicators.pxd
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
cimport numpy as np
|
|
2
|
+
from qubx.core.series cimport Indicator, IndicatorOHLC, RollingSum, TimeSeries, OHLCV, Bar
|
|
3
|
+
|
|
4
|
+
cdef class Sma(Indicator):
|
|
5
|
+
cdef unsigned int period
|
|
6
|
+
cdef RollingSum summator
|
|
7
|
+
|
|
8
|
+
cpdef double calculate(self, long long time, double value, short new_item_started)
|
|
9
|
+
|
|
10
|
+
cdef class Ema(Indicator):
|
|
11
|
+
cdef int period
|
|
12
|
+
cdef np.ndarray __s
|
|
13
|
+
cdef int __i
|
|
14
|
+
cdef double alpha
|
|
15
|
+
cdef double alpha_1
|
|
16
|
+
cdef unsigned short init_mean
|
|
17
|
+
cdef unsigned short _init_stage
|
|
18
|
+
|
|
19
|
+
cpdef double calculate(self, long long time, double value, short new_item_started)
|
|
20
|
+
|
|
21
|
+
cdef class Tema(Indicator):
|
|
22
|
+
cdef int period
|
|
23
|
+
cdef unsigned short init_mean
|
|
24
|
+
cdef TimeSeries ser0
|
|
25
|
+
cdef Ema ema1
|
|
26
|
+
cdef Ema ema2
|
|
27
|
+
cdef Ema ema3
|
|
28
|
+
cpdef double calculate(self, long long time, double value, short new_item_started)
|
|
29
|
+
|
|
30
|
+
cdef class Dema(Indicator):
|
|
31
|
+
cdef int period
|
|
32
|
+
cdef unsigned short init_mean
|
|
33
|
+
cdef TimeSeries ser0
|
|
34
|
+
cdef Ema ema1
|
|
35
|
+
cdef Ema ema2
|
|
36
|
+
|
|
37
|
+
cpdef double calculate(self, long long time, double value, short new_item_started)
|
|
38
|
+
|
|
39
|
+
cdef class Kama(Indicator):
|
|
40
|
+
cdef int period
|
|
41
|
+
cdef int fast_span
|
|
42
|
+
cdef int slow_span
|
|
43
|
+
cdef double _S1
|
|
44
|
+
cdef double _K1
|
|
45
|
+
cdef _x_past
|
|
46
|
+
cdef RollingSum summator
|
|
47
|
+
|
|
48
|
+
cpdef double calculate(self, long long time, double value, short new_item_started)
|
|
49
|
+
|
|
50
|
+
cdef class Highest(Indicator):
|
|
51
|
+
cdef int period
|
|
52
|
+
cdef object queue
|
|
53
|
+
cpdef double calculate(self, long long time, double value, short new_item_started)
|
|
54
|
+
|
|
55
|
+
cdef class Lowest(Indicator):
|
|
56
|
+
cdef int period
|
|
57
|
+
cdef object queue
|
|
58
|
+
cpdef double calculate(self, long long time, double value, short new_item_started)
|
|
59
|
+
|
|
60
|
+
cdef class Std(Indicator):
|
|
61
|
+
cdef int period
|
|
62
|
+
cpdef double calculate(self, long long time, double value, short new_item_started)
|
|
63
|
+
|
|
64
|
+
cdef class Pewma(Indicator):
|
|
65
|
+
cdef public TimeSeries std
|
|
66
|
+
cdef double alpha, beta
|
|
67
|
+
cdef int T
|
|
68
|
+
|
|
69
|
+
cdef double _mean, _vstd, _var
|
|
70
|
+
cdef double mean, vstd, var
|
|
71
|
+
cdef long _i
|
|
72
|
+
|
|
73
|
+
cpdef double calculate(self, long long time, double value, short new_item_started)
|
|
74
|
+
|
|
75
|
+
cdef class PewmaOutliersDetector(Indicator):
|
|
76
|
+
cdef public TimeSeries upper, lower, outliers, std
|
|
77
|
+
cdef double alpha, beta, threshold
|
|
78
|
+
cdef int T
|
|
79
|
+
cdef str dist
|
|
80
|
+
|
|
81
|
+
cdef double student_t_df
|
|
82
|
+
cdef long _i
|
|
83
|
+
cdef double mean, vstd, variance
|
|
84
|
+
cdef double _mean, _vstd, _variance, _z_thr
|
|
85
|
+
|
|
86
|
+
cpdef double calculate(self, long long time, double x, short new_item_started)
|
|
87
|
+
|
|
88
|
+
cdef double _get_z_thr(self)
|
|
89
|
+
cdef double _get_alpha(self, double p_t)
|
|
90
|
+
cdef double _get_mean(self, double x, double alpha_t)
|
|
91
|
+
cdef double _get_variance(self, double x, double alpha_t)
|
|
92
|
+
cdef double _get_std(self, double variance, double mean)
|
|
93
|
+
cdef double _get_p(self, double x)
|
|
94
|
+
|
|
95
|
+
cdef class Psar(IndicatorOHLC):
|
|
96
|
+
cdef int _bull
|
|
97
|
+
cdef double _af
|
|
98
|
+
cdef double _psar
|
|
99
|
+
cdef double _lp
|
|
100
|
+
cdef double _hp
|
|
101
|
+
|
|
102
|
+
cdef int bull
|
|
103
|
+
cdef double af
|
|
104
|
+
cdef double psar
|
|
105
|
+
cdef double lp
|
|
106
|
+
cdef double hp
|
|
107
|
+
|
|
108
|
+
cdef public TimeSeries upper
|
|
109
|
+
cdef public TimeSeries lower
|
|
110
|
+
|
|
111
|
+
cdef double iaf
|
|
112
|
+
cdef double maxaf
|
|
113
|
+
|
|
114
|
+
cdef _store(self)
|
|
115
|
+
cdef _restore(self)
|
|
116
|
+
|
|
117
|
+
cpdef double calculate(self, long long time, Bar bar, short new_item_started)
|
|
118
|
+
|
|
119
|
+
cdef class Atr(IndicatorOHLC):
|
|
120
|
+
cdef short percentage
|
|
121
|
+
cdef TimeSeries tr
|
|
122
|
+
cdef Indicator ma
|
|
123
|
+
|
|
124
|
+
cpdef double calculate(self, long long time, Bar bar, short new_item_started)
|
|
125
|
+
|
|
126
|
+
cdef class Swings(IndicatorOHLC):
|
|
127
|
+
cdef double _min_l
|
|
128
|
+
cdef long long _min_t
|
|
129
|
+
cdef double _max_h
|
|
130
|
+
cdef long long _max_t
|
|
131
|
+
cdef OHLCV base
|
|
132
|
+
cdef Indicator trend
|
|
133
|
+
# tops contain upper pivot point prices
|
|
134
|
+
# tops_detection_lag contain time lag when top was actually spotted
|
|
135
|
+
cdef public TimeSeries tops, tops_detection_lag
|
|
136
|
+
cdef public TimeSeries bottoms, bottoms_detection_lag
|
|
137
|
+
|
|
138
|
+
cpdef double calculate(self, long long time, Bar bar, short new_item_started)
|
qubx/ta/indicators.pyi
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from qubx.core.series import OHLCV, Indicator, TimeSeries
|
|
1
|
+
from qubx.core.series import OHLCV, Indicator, TimeSeries, IndicatorOHLC
|
|
2
2
|
|
|
3
3
|
def sma(series: TimeSeries, period: int): ...
|
|
4
4
|
def ema(series: TimeSeries, period: int, init_mean: bool = True): ...
|
|
@@ -14,3 +14,9 @@ def psar(series: OHLCV, iaf: float = 0.02, maxaf: float = 0.2): ...
|
|
|
14
14
|
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
|
+
|
|
18
|
+
class Kama(Indicator):
|
|
19
|
+
def __init__(self, name: str, series: TimeSeries, period: int, fast_span: int = 2, slow_span: int = 30): ...
|
|
20
|
+
|
|
21
|
+
class Atr(IndicatorOHLC):
|
|
22
|
+
def __init__(self, name: str, series: OHLCV, period: int, smoother: str, percentage: bool): ...
|
qubx/ta/indicators.pyx
CHANGED
|
@@ -16,12 +16,7 @@ cdef class Sma(Indicator):
|
|
|
16
16
|
"""
|
|
17
17
|
Simple Moving Average indicator
|
|
18
18
|
"""
|
|
19
|
-
cdef unsigned int period
|
|
20
|
-
cdef RollingSum summator
|
|
21
19
|
|
|
22
|
-
"""
|
|
23
|
-
Simple moving average
|
|
24
|
-
"""
|
|
25
20
|
def __init__(self, str name, TimeSeries series, int period):
|
|
26
21
|
self.period = period
|
|
27
22
|
self.summator = RollingSum(period)
|
|
@@ -40,13 +35,6 @@ cdef class Ema(Indicator):
|
|
|
40
35
|
"""
|
|
41
36
|
Exponential moving average
|
|
42
37
|
"""
|
|
43
|
-
cdef int period
|
|
44
|
-
cdef np.ndarray __s
|
|
45
|
-
cdef int __i
|
|
46
|
-
cdef double alpha
|
|
47
|
-
cdef double alpha_1
|
|
48
|
-
cdef unsigned short init_mean
|
|
49
|
-
cdef unsigned short _init_stage
|
|
50
38
|
|
|
51
39
|
def __init__(self, str name, TimeSeries series, int period, init_mean=True):
|
|
52
40
|
self.period = period
|
|
@@ -92,12 +80,6 @@ def ema(series:TimeSeries, period: int, init_mean: bool = True):
|
|
|
92
80
|
|
|
93
81
|
|
|
94
82
|
cdef class Tema(Indicator):
|
|
95
|
-
cdef int period
|
|
96
|
-
cdef unsigned short init_mean
|
|
97
|
-
cdef TimeSeries ser0
|
|
98
|
-
cdef Ema ema1
|
|
99
|
-
cdef Ema ema2
|
|
100
|
-
cdef Ema ema3
|
|
101
83
|
|
|
102
84
|
def __init__(self, str name, TimeSeries series, int period, init_mean=True):
|
|
103
85
|
self.period = period
|
|
@@ -118,11 +100,6 @@ def tema(series:TimeSeries, period: int, init_mean: bool = True):
|
|
|
118
100
|
|
|
119
101
|
|
|
120
102
|
cdef class Dema(Indicator):
|
|
121
|
-
cdef int period
|
|
122
|
-
cdef unsigned short init_mean
|
|
123
|
-
cdef TimeSeries ser0
|
|
124
|
-
cdef Ema ema1
|
|
125
|
-
cdef Ema ema2
|
|
126
103
|
|
|
127
104
|
def __init__(self, str name, TimeSeries series, int period, init_mean=True):
|
|
128
105
|
self.period = period
|
|
@@ -142,13 +119,13 @@ def dema(series:TimeSeries, period: int, init_mean: bool = True):
|
|
|
142
119
|
|
|
143
120
|
|
|
144
121
|
cdef class Kama(Indicator):
|
|
145
|
-
cdef int period
|
|
146
|
-
cdef int fast_span
|
|
147
|
-
cdef int slow_span
|
|
148
|
-
cdef double _S1
|
|
149
|
-
cdef double _K1
|
|
150
|
-
cdef _x_past
|
|
151
|
-
cdef RollingSum summator
|
|
122
|
+
# cdef int period
|
|
123
|
+
# cdef int fast_span
|
|
124
|
+
# cdef int slow_span
|
|
125
|
+
# cdef double _S1
|
|
126
|
+
# cdef double _K1
|
|
127
|
+
# cdef _x_past
|
|
128
|
+
# cdef RollingSum summator
|
|
152
129
|
|
|
153
130
|
def __init__(self, str name, TimeSeries series, int period, int fast_span=2, int slow_span=30):
|
|
154
131
|
self.period = period
|
|
@@ -167,7 +144,7 @@ cdef class Kama(Indicator):
|
|
|
167
144
|
self._x_past[-1] = value
|
|
168
145
|
|
|
169
146
|
cdef double rs = self.summator.update(abs(value - self._x_past[-2]), new_item_started)
|
|
170
|
-
cdef double er = abs(value - self._x_past[0]) / rs
|
|
147
|
+
cdef double er = (abs(value - self._x_past[0]) / rs) if rs != 0.0 else 1.0
|
|
171
148
|
cdef double sc = (er * self._K1 + self._S1) ** 2
|
|
172
149
|
|
|
173
150
|
if self.summator.is_init_stage:
|
|
@@ -183,8 +160,6 @@ def kama(series:TimeSeries, period: int, fast_span:int=2, slow_span:int=30):
|
|
|
183
160
|
|
|
184
161
|
|
|
185
162
|
cdef class Highest(Indicator):
|
|
186
|
-
cdef int period
|
|
187
|
-
cdef queue
|
|
188
163
|
|
|
189
164
|
def __init__(self, str name, TimeSeries series, int period):
|
|
190
165
|
self.period = period
|
|
@@ -214,8 +189,6 @@ def highest(series:TimeSeries, period:int):
|
|
|
214
189
|
|
|
215
190
|
|
|
216
191
|
cdef class Lowest(Indicator):
|
|
217
|
-
cdef int period
|
|
218
|
-
cdef queue
|
|
219
192
|
|
|
220
193
|
def __init__(self, str name, TimeSeries series, int period):
|
|
221
194
|
self.period = period
|
|
@@ -246,7 +219,6 @@ def lowest(series:TimeSeries, period:int):
|
|
|
246
219
|
|
|
247
220
|
# - - - - TODO !!!!!!!
|
|
248
221
|
cdef class Std(Indicator):
|
|
249
|
-
cdef int period
|
|
250
222
|
|
|
251
223
|
def __init__(self, str name, TimeSeries series, int period):
|
|
252
224
|
self.period = period
|
|
@@ -285,13 +257,6 @@ cdef double student_t_pdf(double x, double df):
|
|
|
285
257
|
|
|
286
258
|
|
|
287
259
|
cdef class Pewma(Indicator):
|
|
288
|
-
cdef public TimeSeries std
|
|
289
|
-
cdef double alpha, beta
|
|
290
|
-
cdef int T
|
|
291
|
-
|
|
292
|
-
cdef double _mean, _vstd, _var
|
|
293
|
-
cdef double mean, vstd, var
|
|
294
|
-
cdef long _i
|
|
295
260
|
|
|
296
261
|
def __init__(self, str name, TimeSeries series, double alpha, double beta, int T):
|
|
297
262
|
self.alpha = alpha
|
|
@@ -359,15 +324,6 @@ def pewma(series:TimeSeries, alpha: float, beta: float, T:int=30):
|
|
|
359
324
|
|
|
360
325
|
|
|
361
326
|
cdef class PewmaOutliersDetector(Indicator):
|
|
362
|
-
cdef public TimeSeries upper, lower, outliers, std
|
|
363
|
-
cdef double alpha, beta, threshold
|
|
364
|
-
cdef int T
|
|
365
|
-
cdef str dist
|
|
366
|
-
|
|
367
|
-
cdef double student_t_df
|
|
368
|
-
cdef long _i
|
|
369
|
-
cdef double mean, vstd, variance
|
|
370
|
-
cdef double _mean, _vstd, _variance, _z_thr
|
|
371
327
|
|
|
372
328
|
def __init__(
|
|
373
329
|
self,
|
|
@@ -498,23 +454,6 @@ def pewma_outliers_detector(
|
|
|
498
454
|
|
|
499
455
|
|
|
500
456
|
cdef class Psar(IndicatorOHLC):
|
|
501
|
-
cdef int _bull
|
|
502
|
-
cdef double _af
|
|
503
|
-
cdef double _psar
|
|
504
|
-
cdef double _lp
|
|
505
|
-
cdef double _hp
|
|
506
|
-
|
|
507
|
-
cdef int bull
|
|
508
|
-
cdef double af
|
|
509
|
-
cdef double psar
|
|
510
|
-
cdef double lp
|
|
511
|
-
cdef double hp
|
|
512
|
-
|
|
513
|
-
cdef public TimeSeries upper
|
|
514
|
-
cdef public TimeSeries lower
|
|
515
|
-
|
|
516
|
-
cdef double iaf
|
|
517
|
-
cdef double maxaf
|
|
518
457
|
|
|
519
458
|
def __init__(self, name, series, iaf, maxaf):
|
|
520
459
|
self.iaf = iaf
|
|
@@ -636,9 +575,6 @@ def smooth(TimeSeries series, str smoother, *args, **kwargs) -> Indicator:
|
|
|
636
575
|
|
|
637
576
|
|
|
638
577
|
cdef class Atr(IndicatorOHLC):
|
|
639
|
-
cdef short percentage
|
|
640
|
-
cdef TimeSeries tr
|
|
641
|
-
cdef Indicator ma
|
|
642
578
|
|
|
643
579
|
def __init__(self, str name, OHLCV series, int period, str smoother, short percentage):
|
|
644
580
|
self.percentage = percentage
|
|
@@ -665,16 +601,6 @@ def atr(series: OHLCV, period: int = 14, smoother="sma", percentage: bool = Fals
|
|
|
665
601
|
|
|
666
602
|
|
|
667
603
|
cdef class Swings(IndicatorOHLC):
|
|
668
|
-
cdef double _min_l
|
|
669
|
-
cdef long long _min_t
|
|
670
|
-
cdef double _max_h
|
|
671
|
-
cdef long long _max_t
|
|
672
|
-
cdef OHLCV base
|
|
673
|
-
cdef Indicator trend
|
|
674
|
-
# tops contain upper pivot point prices
|
|
675
|
-
# tops_detection_lag contain time lag when top was actually spotted
|
|
676
|
-
cdef public TimeSeries tops, tops_detection_lag
|
|
677
|
-
cdef public TimeSeries bottoms, bottoms_detection_lag
|
|
678
604
|
|
|
679
605
|
def __init__(self, str name, OHLCV series, trend_indicator, **indicator_args):
|
|
680
606
|
self.base = OHLCV("base", series.timeframe, series.max_series_length)
|
qubx/trackers/sizers.py
CHANGED
|
@@ -63,7 +63,13 @@ class FixedLeverageSizer(IPositionSizer):
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
class FixedRiskSizer(IPositionSizer):
|
|
66
|
-
def __init__(
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
max_cap_in_risk: float,
|
|
69
|
+
max_allowed_position=np.inf,
|
|
70
|
+
reinvest_profit: bool = True,
|
|
71
|
+
divide_by_symbols: bool = True,
|
|
72
|
+
):
|
|
67
73
|
"""
|
|
68
74
|
Create fixed risk sizer calculator instance.
|
|
69
75
|
:param max_cap_in_risk: maximal risked capital (in percentage)
|
|
@@ -73,6 +79,7 @@ class FixedRiskSizer(IPositionSizer):
|
|
|
73
79
|
self.max_cap_in_risk = max_cap_in_risk / 100
|
|
74
80
|
self.max_allowed_position_quoted = max_allowed_position
|
|
75
81
|
self.reinvest_profit = reinvest_profit
|
|
82
|
+
self.divide_by_symbols = divide_by_symbols
|
|
76
83
|
|
|
77
84
|
def calculate_target_positions(self, ctx: StrategyContext, signals: List[Signal]) -> List[TargetPosition]:
|
|
78
85
|
t_pos = []
|
|
@@ -91,6 +98,7 @@ class FixedRiskSizer(IPositionSizer):
|
|
|
91
98
|
target_position_size = (
|
|
92
99
|
_direction
|
|
93
100
|
*min((_cap * self.max_cap_in_risk) / abs(signal.stop / _entry - 1), self.max_allowed_position_quoted) / _entry
|
|
101
|
+
/ len(ctx.instruments) if self.divide_by_symbols else 1
|
|
94
102
|
)
|
|
95
103
|
# fmt: on
|
|
96
104
|
|
|
@@ -3,27 +3,27 @@ qubx/_nb_magic.py,sha256=vfV892I5rkA_Zbno5Dsl9osPTGiXTCV3afZRDrTlcOQ,1915
|
|
|
3
3
|
qubx/backtester/__init__.py,sha256=g9K4pp4ieY4rRKE0eA_02LMo-c4vnk-QqtVn_ff9F5U,66
|
|
4
4
|
qubx/backtester/ome.py,sha256=nbKuB4t67jcK7BBu7LpoQza-ulvgtUupzVFU6MaTCn4,8690
|
|
5
5
|
qubx/backtester/optimization.py,sha256=wn48ppzziiNTG37tipY4HfRpqLKl2Y5iVCVO6NsCKRM,6588
|
|
6
|
-
qubx/backtester/queue.py,sha256=
|
|
7
|
-
qubx/backtester/simulator.py,sha256=
|
|
6
|
+
qubx/backtester/queue.py,sha256=ys9tHyrqx3NKbcoTAoOn0m0caIk5F0GpS2x7_sCikro,14997
|
|
7
|
+
qubx/backtester/simulator.py,sha256=bppxwq_F5eS4_GjR8Xki1kS5AKWr7HPHPdYUXjsfASc,33921
|
|
8
8
|
qubx/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
qubx/core/account.py,sha256=uIU-EUMuH5owWhHTmU1pZ0WrkQkiv9YiFqSZW9dFDK8,9066
|
|
10
|
-
qubx/core/basics.py,sha256=
|
|
10
|
+
qubx/core/basics.py,sha256=iwJ5wi2ENbKFqPdJIQEcSgdB6HkskzY0P4b_cf8-Avk,19897
|
|
11
11
|
qubx/core/context.py,sha256=I5bse6F4VVQbJb6jmkQsQEX_C2iht440pUK-BqOc1VU,37619
|
|
12
12
|
qubx/core/exceptions.py,sha256=W1gG_0fE3o2EMGUfOeOl3vVJDp8Z1iHLv4iZ0ThNkTs,306
|
|
13
|
-
qubx/core/helpers.py,sha256=
|
|
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=
|
|
17
|
-
qubx/core/series.cpython-311-x86_64-linux-gnu.so,sha256=
|
|
16
|
+
qubx/core/metrics.py,sha256=lPxq3k1XFcSjlLafhDEluiqkA0ISoJlckxeJnrCm2mM,36537
|
|
17
|
+
qubx/core/series.cpython-311-x86_64-linux-gnu.so,sha256=ocuR5fHMQHUcSc0HBMWoHK2kADA5RKNUenXafbyO7F8,762408
|
|
18
18
|
qubx/core/series.pxd,sha256=1KJGzun_R5GDDOynPbZER7Xg45a0JUM_S_lZ-9FFFJg,2994
|
|
19
|
-
qubx/core/series.pyi,sha256=
|
|
19
|
+
qubx/core/series.pyi,sha256=ipca4P8LJGDvkhAAczLoZbgLvcJSm-5SFKlaQI9cn9E,2254
|
|
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=
|
|
22
|
+
qubx/core/utils.cpython-311-x86_64-linux-gnu.so,sha256=C9o_sLn5cuBp6Qh8_Xwy5k90H3ls1rZwJa2qxoOrkdU,74248
|
|
23
23
|
qubx/core/utils.pyi,sha256=bT3NvsHwf0AszMh7OP8uC5StaAM1arArAE5Yd3BjLHI,167
|
|
24
24
|
qubx/core/utils.pyx,sha256=csSIwI_lwvCd8CTbsp9Q0XiRCtGAHEuF62afHOJzCGQ,1382
|
|
25
25
|
qubx/data/helpers.py,sha256=A0NGzhpXYWD92-GeB8TghwMnR0NW8bjcNJOCXybQw3g,782
|
|
26
|
-
qubx/data/readers.py,sha256=
|
|
26
|
+
qubx/data/readers.py,sha256=HzNkFI2W4Rkh2AZWsOnnsi9VlHmlBCKaigyMd0T0Jiw,39523
|
|
27
27
|
qubx/gathering/simplest.py,sha256=fSAWbogW2_eXzF9CG3zJ5Y-OtY4rCnBs0VISM0p-W7Q,1833
|
|
28
28
|
qubx/impl/ccxt_connector.py,sha256=ja_0WJDyZfkzqhNvoV-c5CCg15YnbRIThxw0TTNdwcc,13066
|
|
29
29
|
qubx/impl/ccxt_customizations.py,sha256=WUhDT9x2SYuFrOyBIbk2D9Q_U_5QZhtLomLq88Egf_c,6230
|
|
@@ -32,17 +32,18 @@ qubx/impl/ccxt_utils.py,sha256=Zfx5dltbvm_1zv4NqtU6uAX4-tFjgH2UYRHKBnsBfQ0,3966
|
|
|
32
32
|
qubx/math/__init__.py,sha256=QRat0pqEqmbJF7CIHBuqnB1xUek4CsDDhv7SpgssnFw,57
|
|
33
33
|
qubx/math/stats.py,sha256=xgzBU3PLxQ42jBMpI57UVuQ2uGGRYxGKuEd4eiGvjnE,1695
|
|
34
34
|
qubx/pandaz/__init__.py,sha256=Iw5uzicYGSC3FEKZ-W1O5-7cXq_P0kH11-EcXV0zZhs,175
|
|
35
|
-
qubx/pandaz/ta.py,sha256=
|
|
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=
|
|
39
|
-
qubx/ta/indicators.
|
|
40
|
-
qubx/ta/indicators.
|
|
38
|
+
qubx/ta/indicators.cpython-311-x86_64-linux-gnu.so,sha256=Jgmwhehxp6BwnkAuMVMcwVqRnK0mKjadfm3PRppntoQ,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
|
|
41
42
|
qubx/trackers/__init__.py,sha256=-Hmlc8Mpdi_t6kyeW4ZtmYkHzrULaIb6gUsAkhUxA-4,169
|
|
42
43
|
qubx/trackers/composite.py,sha256=rYQCaxu4IKLf5sS3DCyUUJaSUQwbCy6UMoVjsS-481A,6222
|
|
43
44
|
qubx/trackers/rebalancers.py,sha256=dp-jemxczT8ndkjjzljsIgGnnA6lZQhsf5c4YdmHKCw,6048
|
|
44
45
|
qubx/trackers/riskctrl.py,sha256=8P5iyaJ-PKhIX8GkGBpncQdxnefL1QWRWG-Jx1PFTuk,12595
|
|
45
|
-
qubx/trackers/sizers.py,sha256=
|
|
46
|
+
qubx/trackers/sizers.py,sha256=rOroN9e__C0oIQKaJ6CDlBrHFSlcZYBve7dgFQPi1vs,6111
|
|
46
47
|
qubx/utils/__init__.py,sha256=AL2YibJ3tqBKsZZLUjM9N2J5yy-Kq__k_44oTODQ5sM,321
|
|
47
48
|
qubx/utils/_pyxreloader.py,sha256=FyqGzfSpZGYziB8JYS5AP3cLRAvJSIPAKgwQn0E4YQ0,12017
|
|
48
49
|
qubx/utils/charting/lookinglass.py,sha256=1RxWf6c3rx0RxxeNPcFVtCtlwvh-Fxicc5fpF4aIIzo,38501
|
|
@@ -52,6 +53,6 @@ qubx/utils/misc.py,sha256=PaWH-nsZeULM0vFvnq_xeypC3vrIb6xWNRExaz1WS9w,10557
|
|
|
52
53
|
qubx/utils/ntp.py,sha256=LZo4FPVY3rqLUV9VWkLcZaPOpUDFC8Qleynmfggg9No,1758
|
|
53
54
|
qubx/utils/runner.py,sha256=Czo01KUCc9Oj9TIcs03d6Qh7fOpQV5w8oH6UDZ6Yqn0,9539
|
|
54
55
|
qubx/utils/time.py,sha256=ioIos3VLA-iYI8LiQIn2m-t0Y37CEQAHFw_-C9Uwo0o,5188
|
|
55
|
-
qubx-0.2.
|
|
56
|
-
qubx-0.2.
|
|
57
|
-
qubx-0.2.
|
|
56
|
+
qubx-0.2.6.dist-info/METADATA,sha256=AwUYllCgKhz_ZahoInUVQHIXeqRk-sh7DM2in3ordhM,2572
|
|
57
|
+
qubx-0.2.6.dist-info/WHEEL,sha256=MLOa6LysROdjgj4FVxsHitAnIh8Be2D_c9ZSBHKrz2M,110
|
|
58
|
+
qubx-0.2.6.dist-info/RECORD,,
|
|
File without changes
|