Qubx 0.2.4__tar.gz → 0.2.6__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.

Files changed (56) hide show
  1. {qubx-0.2.4 → qubx-0.2.6}/PKG-INFO +1 -1
  2. {qubx-0.2.4 → qubx-0.2.6}/pyproject.toml +1 -1
  3. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/backtester/queue.py +2 -2
  4. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/backtester/simulator.py +6 -0
  5. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/basics.py +3 -2
  6. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/helpers.py +2 -1
  7. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/metrics.py +39 -27
  8. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/series.pyi +16 -0
  9. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/data/readers.py +1 -1
  10. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/pandaz/ta.py +1 -1
  11. qubx-0.2.6/src/qubx/ta/indicators.pxd +138 -0
  12. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/ta/indicators.pyi +7 -1
  13. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/ta/indicators.pyx +8 -82
  14. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/trackers/sizers.py +9 -1
  15. {qubx-0.2.4 → qubx-0.2.6}/README.md +0 -0
  16. {qubx-0.2.4 → qubx-0.2.6}/build.py +0 -0
  17. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/__init__.py +0 -0
  18. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/_nb_magic.py +0 -0
  19. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/backtester/__init__.py +0 -0
  20. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/backtester/ome.py +0 -0
  21. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/backtester/optimization.py +0 -0
  22. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/__init__.py +0 -0
  23. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/account.py +0 -0
  24. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/context.py +0 -0
  25. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/exceptions.py +0 -0
  26. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/loggers.py +0 -0
  27. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/lookups.py +0 -0
  28. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/series.pxd +0 -0
  29. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/series.pyx +0 -0
  30. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/strategy.py +0 -0
  31. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/utils.pyi +0 -0
  32. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/core/utils.pyx +0 -0
  33. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/data/helpers.py +0 -0
  34. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/gathering/simplest.py +0 -0
  35. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/impl/ccxt_connector.py +0 -0
  36. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/impl/ccxt_customizations.py +0 -0
  37. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/impl/ccxt_trading.py +0 -0
  38. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/impl/ccxt_utils.py +0 -0
  39. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/math/__init__.py +0 -0
  40. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/math/stats.py +0 -0
  41. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/pandaz/__init__.py +0 -0
  42. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/pandaz/utils.py +0 -0
  43. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/ta/__init__.py +0 -0
  44. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/trackers/__init__.py +0 -0
  45. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/trackers/composite.py +0 -0
  46. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/trackers/rebalancers.py +0 -0
  47. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/trackers/riskctrl.py +0 -0
  48. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/utils/__init__.py +0 -0
  49. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/utils/_pyxreloader.py +0 -0
  50. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/utils/charting/lookinglass.py +0 -0
  51. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/utils/charting/mpl_helpers.py +0 -0
  52. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/utils/marketdata/binance.py +0 -0
  53. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/utils/misc.py +0 -0
  54. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/utils/ntp.py +0 -0
  55. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/utils/runner.py +0 -0
  56. {qubx-0.2.4 → qubx-0.2.6}/src/qubx/utils/time.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: Qubx
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Qubx - quantitative trading framework
5
5
  Home-page: https://github.com/dmarienko/Qubx
6
6
  Author: Dmitry Marienko
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "Qubx"
3
- version = "0.2.4"
3
+ version = "0.2.6"
4
4
  description = "Qubx - quantitative trading framework"
5
5
  authors = ["Dmitry Marienko <dmitry@gmail.com>", "Yuriy Arabskyy <yuriy.arabskyy@gmail.com>"]
6
6
  readme = "README.md"
@@ -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() * 1_000_000)
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)
@@ -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
 
@@ -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:
@@ -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
- _log_info += f"\n\tset <green>{k}</green> <- <red>{v}</red>"
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)
@@ -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
- if infer_series_frequency(returns) < pd.Timedelta("1D").to_timedelta64():
665
- returns_daily = aggregate_returns(returns, "daily")
666
- returns_on_init_bp = aggregate_returns(returns_on_init_bp, "daily")
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
- mdd_pct = 100 * dd_data / equity.cummax()
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"] = len(executions_log)
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 compound:
743
- # _eq.append(pd.Series(100 * mtrx["compound_returns"], name=s.trading_id))
744
- compound_returns = mtrx["compound_returns"].resample(timeframe).ffill()
745
- plt.plot(100 * compound_returns, label=s.name)
746
- else:
747
- # _eq.append(pd.Series(mtrx["equity"], name=s.trading_id))
748
- equity = mtrx["equity"].resample(timeframe).ffill()
749
- plt.plot(equity, label=s.name)
750
-
751
- if len(session) <= 15:
752
- plt.legend(ncol=max(1, len(session) // 5))
753
-
754
- plt.title("Comparison of Equity Curves")
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(session: TradingSessionResult | list[TradingSessionResult]) -> str:
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
- excs = executions[executions["instrument"] == symbol][
951
- ["quantity", "exec_price", "commissions", "commissions_quoted"]
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
 
@@ -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): ...
@@ -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 <= '{end}'" if end else ""
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:
@@ -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.Series, window=14, smoother="sma", percentage=False) -> pd.Series:
698
+ def atr(x: pd.DataFrame, window=14, smoother="sma", percentage=False) -> pd.Series:
699
699
  """
700
700
  Average True Range indicator
701
701
 
@@ -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)
@@ -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): ...
@@ -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)
@@ -63,7 +63,13 @@ class FixedLeverageSizer(IPositionSizer):
63
63
 
64
64
 
65
65
  class FixedRiskSizer(IPositionSizer):
66
- def __init__(self, max_cap_in_risk: float, max_allowed_position=np.inf, reinvest_profit=True):
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
 
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