investing-algorithm-framework 7.16.9__py3-none-any.whl → 7.16.11__py3-none-any.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 investing-algorithm-framework might be problematic. Click here for more details.

@@ -1,33 +1,79 @@
1
1
  from enum import Enum
2
2
 
3
3
 
4
- default_weights = {
5
- # Profitability
6
- "total_net_gain": 3.0,
7
- "gross_loss": 0.0,
8
- "growth": 0.0,
9
- "trades_average_return": 0.0,
10
-
11
- # Risk-adjusted returns
12
- "sharpe_ratio": 1.0,
13
- "sortino_ratio": 1.0,
14
- "profit_factor": 1.0,
15
-
16
- # Risk
17
- "max_drawdown": -2.0,
18
- "max_drawdown_duration": -0.5,
19
-
20
- # Trading activity
21
- "number_of_trades": 2.0,
22
- "win_rate": 3.0,
23
-
24
- # Exposure
25
- "cumulative_exposure": 0.5,
26
- "exposure_ratio": 0.0,
27
- }
28
-
29
-
30
4
  class BacktestEvaluationFocus(Enum):
5
+ """
6
+ Enumeration for backtest evaluation focus areas.
7
+
8
+ The available metrics are:
9
+ - backtest_start_date
10
+ - backtest_end_date
11
+ - equity_curve
12
+ - final_value
13
+ - total_growth
14
+ - total_growth_percentage
15
+ - total_net_gain
16
+ - total_net_gain_percentage
17
+ - total_loss
18
+ - total_loss_percentage
19
+ - cumulative_return
20
+ - cumulative_return_series
21
+ - cagr
22
+ - sharpe_ratio
23
+ - rolling_sharpe_ratio
24
+ - sortino_ratio
25
+ - calmar_ratio
26
+ - profit_factor
27
+ - annual_volatility
28
+ - monthly_returns
29
+ - yearly_returns
30
+ - drawdown_series
31
+ - max_drawdown
32
+ - max_drawdown_absolute
33
+ - max_daily_drawdown
34
+ - max_drawdown_duration
35
+ - trades_per_year
36
+ - trade_per_day
37
+ - exposure_ratio
38
+ - cumulative_exposure
39
+ - best_trade
40
+ - worst_trade
41
+ - number_of_positive_trades
42
+ - percentage_positive_trades
43
+ - number_of_negative_trades
44
+ - percentage_negative_trades
45
+ - average_trade_duration
46
+ - average_trade_size
47
+ - average_trade_loss
48
+ - average_trade_loss_percentage
49
+ - average_trade_gain
50
+ - average_trade_gain_percentage
51
+ - average_trade_return
52
+ - average_trade_return_percentage
53
+ - median_trade_return
54
+ - number_of_trades
55
+ - number_of_trades_closed
56
+ - number_of_trades_opened
57
+ - number_of_trades_open_at_end
58
+ - win_rate
59
+ - current_win_rate
60
+ - win_loss_ratio
61
+ - current_win_loss_ratio
62
+ - percentage_winning_months
63
+ - percentage_winning_years
64
+ - average_monthly_return
65
+ - average_monthly_return_losing_months
66
+ - average_monthly_return_winning_months
67
+ - best_month
68
+ - best_year
69
+ - worst_month
70
+ - worst_year
71
+ - total_number_of_days
72
+ - current_average_trade_gain
73
+ - current_average_trade_return
74
+ - current_average_trade_duration
75
+ - current_average_trade_loss
76
+ """
31
77
  BALANCED = "balanced"
32
78
  PROFIT = "profit"
33
79
  FREQUENCY = "frequency"
@@ -73,44 +119,124 @@ class BacktestEvaluationFocus(Enum):
73
119
  return BacktestEvaluationFocus.from_string(other) == self
74
120
 
75
121
  def get_weights(self):
76
- # default / balanced
77
- base = {
78
- "total_net_gain": 3.0,
79
- "win_rate": 3.0,
80
- "number_of_trades": 2.0,
81
- "sharpe_ratio": 1.0,
82
- "sortino_ratio": 1.0,
83
- "profit_factor": 1.0,
84
- "max_drawdown": -2.0,
85
- "max_drawdown_duration": -0.5,
86
- "total_net_loss": 0.0,
87
- "total_return": 0.0,
88
- "avg_return_per_trade": 0.0,
89
- "exposure_factor": 0.5,
90
- "exposure_ratio": 0.0,
91
- "exposure_time": 0.0,
92
- }
93
-
94
- if self != BacktestEvaluationFocus.BALANCED:
95
-
96
- # apply presets
97
- if self == BacktestEvaluationFocus.PROFIT:
98
- base.update({
99
- "total_net_gain": 3.0,
100
- "win_rate": 2.0,
101
- "number_of_trades": 1.0,
102
- })
103
- elif self == BacktestEvaluationFocus.FREQUENCY:
104
- base.update({
105
- "number_of_trades": 3.0,
106
- "win_rate": 2.0,
107
- "total_net_gain": 2.0,
108
- })
109
- elif self == BacktestEvaluationFocus.RISK_ADJUSTED:
110
- base.update({
111
- "sharpe_ratio": 3.0,
112
- "sortino_ratio": 3.0,
113
- "max_drawdown": -3.0,
114
- })
115
-
116
- return base
122
+ """
123
+ Get evaluation weights for different focus areas.
124
+ Returns a dictionary with metric weights where:
125
+ - Positive weights favor higher values
126
+ - Negative weights favor lower values (penalties)
127
+ - Zero weights ignore the metric
128
+ """
129
+
130
+ if self == BacktestEvaluationFocus.BALANCED:
131
+ return {
132
+ # Core profitability metrics (moderate weight)
133
+ "total_net_gain_percentage": 2.0,
134
+ "cagr": 1.5,
135
+ "average_trade_return_percentage": 1.0,
136
+
137
+ # Risk-adjusted returns (important for balance)
138
+ "sharpe_ratio": 2.0,
139
+ "sortino_ratio": 1.5,
140
+ "calmar_ratio": 1.0,
141
+ "profit_factor": 1.5,
142
+
143
+ # Risk management (penalties for bad metrics)
144
+ "max_drawdown": -1.5,
145
+ "max_drawdown_duration": -0.5,
146
+ "annual_volatility": -0.8,
147
+
148
+ # Trading consistency
149
+ "win_rate": 1.5,
150
+ "win_loss_ratio": 1.0,
151
+ "number_of_trades": 0.8, # Some activity needed
152
+
153
+ # Efficiency metrics
154
+ "exposure_ratio": 0.5,
155
+ "trades_per_year": 0.3,
156
+ }
157
+
158
+ elif self == BacktestEvaluationFocus.PROFIT:
159
+ return {
160
+ # Maximize absolute and relative profits
161
+ "total_net_gain_percentage": 3.0,
162
+ "cagr": 2.5,
163
+ "total_net_gain": 2.0,
164
+ "average_trade_return_percentage": 1.5,
165
+ "best_trade": 1.0,
166
+
167
+ # Profit consistency
168
+ "win_rate": 2.0,
169
+ "profit_factor": 2.0,
170
+ "percentage_positive_trades": 1.0,
171
+
172
+ # Risk secondary (but still important)
173
+ "sharpe_ratio": 1.0,
174
+ "max_drawdown": -1.0,
175
+
176
+ # Activity level (need some trades)
177
+ "number_of_trades": 0.5,
178
+
179
+ # Monthly/yearly consistency
180
+ "percentage_winning_months": 0.8,
181
+ "average_monthly_return": 1.0,
182
+ }
183
+
184
+ elif self == BacktestEvaluationFocus.FREQUENCY:
185
+ return {
186
+ # High trading activity with good results
187
+ "number_of_trades": 3.0,
188
+ "trades_per_year": 2.5,
189
+ "exposure_ratio": 2.0,
190
+
191
+ # Profitability per trade (scaled for frequency)
192
+ "average_trade_return_percentage": 2.0,
193
+ "win_rate": 2.5,
194
+ "total_net_gain_percentage": 1.5,
195
+
196
+ # Consistency across many trades
197
+ "win_loss_ratio": 1.5,
198
+ "percentage_positive_trades": 1.0,
199
+
200
+ # Risk management for frequent trading
201
+ "max_drawdown": -1.5,
202
+ "sharpe_ratio": 1.0,
203
+ "profit_factor": 1.2,
204
+
205
+ # Duration efficiency
206
+ "average_trade_duration": -0.3, # Prefer shorter trades
207
+ }
208
+
209
+ elif self == BacktestEvaluationFocus.RISK_ADJUSTED:
210
+ return {
211
+ # Risk-adjusted performance metrics (highest priority)
212
+ "sharpe_ratio": 3.0,
213
+ "sortino_ratio": 2.5,
214
+ "calmar_ratio": 2.0,
215
+
216
+ # Risk management (strong penalties)
217
+ "max_drawdown": -3.0,
218
+ "max_drawdown_duration": -1.5,
219
+ "annual_volatility": -2.0,
220
+ "worst_trade": -1.0,
221
+
222
+ # Consistent performance
223
+ "win_rate": 2.0,
224
+ "win_loss_ratio": 1.5,
225
+ "percentage_winning_months": 1.5,
226
+
227
+ # Stable returns
228
+ "average_trade_return_percentage": 1.5,
229
+ "total_net_gain_percentage": 1.0,
230
+ "profit_factor": 1.8,
231
+
232
+ # Reasonable activity
233
+ "number_of_trades": 0.5,
234
+
235
+ # Downside protection
236
+ "average_trade_loss_percentage": -1.0,
237
+ "percentage_negative_trades": -1.0,
238
+ }
239
+
240
+ # Fallback to balanced if unknown focus
241
+ return self.get_weights() \
242
+ if self != BacktestEvaluationFocus.BALANCED else {}
@@ -268,6 +268,11 @@ class BacktestService:
268
268
  # Trade generation
269
269
  last_trade = None
270
270
 
271
+ # Align signals with most granular OHLCV data
272
+ close = df["Close"].reindex(index, method='ffill')
273
+ buy_signal = buy_signals[symbol].reindex(index, fill_value=False)
274
+ sell_signal = sell_signals[symbol].reindex(index, fill_value=False)
275
+
271
276
  # Loop over all timestamps in the backtest
272
277
  for i in range(len(index)):
273
278
 
@@ -139,7 +139,7 @@ def create_backtest_metrics(
139
139
  else:
140
140
  setattr(backtest_metrics, metric_name, value)
141
141
  except OperationalException as e:
142
- logger.error(f"{metric_name} failed: {e}")
142
+ logger.warning(f"{metric_name} failed: {e}")
143
143
 
144
144
  # Grouped metrics needing special handling
145
145
  if "total_net_gain" in metrics or "total_net_gain_percentage" in metrics:
@@ -150,7 +150,7 @@ def create_backtest_metrics(
150
150
  if "total_net_gain_percentage" in metrics:
151
151
  backtest_metrics.total_net_gain_percentage = total_return[1]
152
152
  except OperationalException as e:
153
- logger.error(f"total_return failed: {e}")
153
+ logger.warning(f"total_return failed: {e}")
154
154
 
155
155
  if "total_growth" in metrics or "total_growth_percentage" in metrics:
156
156
  try:
@@ -160,7 +160,7 @@ def create_backtest_metrics(
160
160
  if "total_growth_percentage" in metrics:
161
161
  backtest_metrics.total_growth_percentage = total_growth[1]
162
162
  except OperationalException as e:
163
- logger.error(f"total_growth failed: {e}")
163
+ logger.warning(f"total_growth failed: {e}")
164
164
 
165
165
  if "total_loss" in metrics or "total_loss_percentage" in metrics:
166
166
  try:
@@ -170,7 +170,7 @@ def create_backtest_metrics(
170
170
  if "total_loss_percentage" in metrics:
171
171
  backtest_metrics.total_loss_percentage = total_loss[1]
172
172
  except OperationalException as e:
173
- logger.error(f"total_loss failed: {e}")
173
+ logger.warning(f"total_loss failed: {e}")
174
174
 
175
175
  if ("average_trade_return" in metrics
176
176
  or "average_trade_return_percentage" in metrics):
@@ -182,7 +182,7 @@ def create_backtest_metrics(
182
182
  backtest_metrics.average_trade_return_percentage = \
183
183
  avg_return[1]
184
184
  except OperationalException as e:
185
- logger.error(f"average_trade_return failed: {e}")
185
+ logger.warning(f"average_trade_return failed: {e}")
186
186
 
187
187
  if ("average_trade_gain" in metrics
188
188
  or "average_trade_gain_percentage" in metrics):
@@ -193,7 +193,7 @@ def create_backtest_metrics(
193
193
  if "average_trade_gain_percentage" in metrics:
194
194
  backtest_metrics.average_trade_gain_percentage = avg_gain[1]
195
195
  except OperationalException as e:
196
- logger.error(f"average_trade_gain failed: {e}")
196
+ logger.warning(f"average_trade_gain failed: {e}")
197
197
 
198
198
  if ("average_trade_loss" in metrics
199
199
  or "average_trade_loss_percentage" in metrics):
@@ -204,7 +204,7 @@ def create_backtest_metrics(
204
204
  if "average_trade_loss_percentage" in metrics:
205
205
  backtest_metrics.average_trade_loss_percentage = avg_loss[1]
206
206
  except OperationalException as e:
207
- logger.error(f"average_trade_loss failed: {e}")
207
+ logger.warning(f"average_trade_loss failed: {e}")
208
208
 
209
209
  if ("current_average_trade_gain" in metrics
210
210
  or "get_current_average_trade_gain_percentage" in metrics):
@@ -221,7 +221,7 @@ def create_backtest_metrics(
221
221
  backtest_metrics.current_average_trade_gain_percentage = \
222
222
  current_avg_gain[1]
223
223
  except OperationalException as e:
224
- logger.error(f"current_average_trade_gain failed: {e}")
224
+ logger.warning(f"current_average_trade_gain failed: {e}")
225
225
 
226
226
  if ("current_average_trade_return" in metrics
227
227
  or "current_average_trade_return_percentage" in metrics):
@@ -237,7 +237,7 @@ def create_backtest_metrics(
237
237
  backtest_metrics.current_average_trade_return_percentage =\
238
238
  current_avg_return[1]
239
239
  except OperationalException as e:
240
- logger.error(f"current_average_trade_return failed: {e}")
240
+ logger.warning(f"current_average_trade_return failed: {e}")
241
241
 
242
242
  if "current_average_trade_duration" in metrics:
243
243
  try:
@@ -247,7 +247,7 @@ def create_backtest_metrics(
247
247
  backtest_metrics.current_average_trade_duration = \
248
248
  current_avg_duration
249
249
  except OperationalException as e:
250
- logger.error(f"current_average_trade_duration failed: {e}")
250
+ logger.warning(f"current_average_trade_duration failed: {e}")
251
251
 
252
252
  if ("current_average_trade_loss" in metrics
253
253
  or "current_average_trade_loss_percentage" in metrics):
@@ -262,7 +262,7 @@ def create_backtest_metrics(
262
262
  backtest_metrics.current_average_trade_loss_percentage = \
263
263
  current_avg_loss[1]
264
264
  except OperationalException as e:
265
- logger.error(f"current_average_trade_loss failed: {e}")
265
+ logger.warning(f"current_average_trade_loss failed: {e}")
266
266
 
267
267
  safe_set("number_of_positive_trades", get_positive_trades, backtest_run.trades)
268
268
  safe_set("percentage_positive_trades", get_positive_trades, backtest_run.trades, index=1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: investing-algorithm-framework
3
- Version: 7.16.9
3
+ Version: 7.16.11
4
4
  Summary: A framework for creating trading bots
5
5
  Author: MDUYN
6
6
  Requires-Python: >=3.10
@@ -86,7 +86,7 @@ investing_algorithm_framework/domain/__init__.py,sha256=PASQlRGcU_MDIwnLppanXGo-
86
86
  investing_algorithm_framework/domain/backtesting/__init__.py,sha256=q-NejGHzE233w5jXPhSsuLpBZ_yl3m-qb2g6FnxZaps,699
87
87
  investing_algorithm_framework/domain/backtesting/backtest.py,sha256=mS7JdPTXhw5AQrQ-krXWtpNsBbVVYkxc1lBSoPhCqoQ,15617
88
88
  investing_algorithm_framework/domain/backtesting/backtest_date_range.py,sha256=e_V7HMdtln4uu87jwwa_Yr7ZesgrpFqsEqtr0e0DTto,3186
89
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py,sha256=ZGfJm-zjsWudQMOdYKfW_2T7K3SBy-JjaMoJp9zijWE,3010
89
+ investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py,sha256=D__3I_TSxDVnGtlddmWt4wHcqut8MGyYMf1IfQZXYJ0,7547
90
90
  investing_algorithm_framework/domain/backtesting/backtest_metrics.py,sha256=HR0bEDT3xh-TQq50PLDcKhYggjtnE-JTRuY2TlXz54w,19552
91
91
  investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py,sha256=8JXdu3EgFh2f2Yc41OYwIBwlYtjFiumyAJUrN5kL078,6703
92
92
  investing_algorithm_framework/domain/backtesting/backtest_run.py,sha256=ffQgilEkyixGrGvKeCZF670OPoY3ljixlgsXPajHpZY,14310
@@ -202,7 +202,7 @@ investing_algorithm_framework/infrastructure/services/azure/__init__.py,sha256=P
202
202
  investing_algorithm_framework/infrastructure/services/azure/state_handler.py,sha256=EUk4PdVl6RQ19DuWdrC4DzgOhGcL3qiZKWgWh_obT4E,5240
203
203
  investing_algorithm_framework/services/__init__.py,sha256=9p0Y2enp6UMOlU4qJgVoojHBRARLGefNzbPxgSCN0wI,4999
204
204
  investing_algorithm_framework/services/backtesting/__init__.py,sha256=sD6JMQVuUT8NRKV77VC9jyGnHcGox0W2n9eA-4ydeHY,84
205
- investing_algorithm_framework/services/backtesting/backtest_service.py,sha256=wT2FqRCnv9HUREvTV6b9sHAZRlJ3go3FtAqfwHTwxB8,23427
205
+ investing_algorithm_framework/services/backtesting/backtest_service.py,sha256=jNd3s0qc9_PGMZAKnW0PB8kQ8Mub05n7UMcq2eB1s-8,23707
206
206
  investing_algorithm_framework/services/configuration_service.py,sha256=BCgiBlrLjMjfU4afmjYaHu9gOWNmgaxhf6RBN2XJkw0,2853
207
207
  investing_algorithm_framework/services/data_providers/__init__.py,sha256=OHVccpIYGc-1B2AkCI_2Nhsb9KMaAUrng4DHhIbFD8Y,96
208
208
  investing_algorithm_framework/services/data_providers/data_provider_service.py,sha256=Tv5W38rshK7sG7XEhp7L-McdiNWAAlvU_1ScSdt1NCE,28420
@@ -215,7 +215,7 @@ investing_algorithm_framework/services/metrics/calmar_ratio.py,sha256=clZTkG6bjZ
215
215
  investing_algorithm_framework/services/metrics/drawdown.py,sha256=VWkPXOm9CSdq6LHL6Pa9JnDObsu-1OOqgC4XRZqZO3Q,6193
216
216
  investing_algorithm_framework/services/metrics/equity_curve.py,sha256=Z3YdqEJ7x9N-RKOC4bt-1LuiKAYTCxhuE6Fx1lJyAS0,702
217
217
  investing_algorithm_framework/services/metrics/exposure.py,sha256=kQPreO5RR72DgTRdSdMPiG6onZTAWvyYTrp3hsI6vYw,6234
218
- investing_algorithm_framework/services/metrics/generate.py,sha256=DLruPIPFjgR7Pjp2hFhJfFSJ3mo3ubkXD68m0Pf46k0,15909
218
+ investing_algorithm_framework/services/metrics/generate.py,sha256=vpWxspLSjdf450horKxHPWi2Xh4jxtS940HAxgCCGv8,15931
219
219
  investing_algorithm_framework/services/metrics/mean_daily_return.py,sha256=_e5GCD5xppa_GrYMEeQB9or1WLkJIF-mdmDfes8CRjQ,2384
220
220
  investing_algorithm_framework/services/metrics/price_efficiency.py,sha256=hpt1Nyk7s_jxFeqEVeQV9FH5N4RzJx6SP072L4z9fYw,1844
221
221
  investing_algorithm_framework/services/metrics/profit_factor.py,sha256=6OMC2yx1stMrJKaVSCMWyVbVMgfurYTiue7P7QeCEHs,5178
@@ -252,8 +252,8 @@ investing_algorithm_framework/services/trade_order_evaluator/default_trade_order
252
252
  investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py,sha256=pNnmgaKMR9RY6Kxq7xS0nURKoaQDe2ehrP5GfElkkcI,1328
253
253
  investing_algorithm_framework/services/trade_service/__init__.py,sha256=AcwPyJjDRdiREnl_MWMkDSc-V-ZjXtvpHD6eQT9mc1o,68
254
254
  investing_algorithm_framework/services/trade_service/trade_service.py,sha256=OtzIS5EebByGcqDvV2AFeBjXSarvrgubMXDaVKg6Rbw,41193
255
- investing_algorithm_framework-7.16.9.dist-info/LICENSE,sha256=wbVEDvoZiMPHufRY3sLEffvAr7GH5hOIngHF8y4HFQg,11343
256
- investing_algorithm_framework-7.16.9.dist-info/METADATA,sha256=vdOz-pFs9hQYqaCugGS8xaJZP1Qde7GWzldn2ACa_Lw,19635
257
- investing_algorithm_framework-7.16.9.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
258
- investing_algorithm_framework-7.16.9.dist-info/entry_points.txt,sha256=jrPF0YksDs27vYzEvj3tXLe3OGWU24EJA05z5xHqmq8,91
259
- investing_algorithm_framework-7.16.9.dist-info/RECORD,,
255
+ investing_algorithm_framework-7.16.11.dist-info/LICENSE,sha256=wbVEDvoZiMPHufRY3sLEffvAr7GH5hOIngHF8y4HFQg,11343
256
+ investing_algorithm_framework-7.16.11.dist-info/METADATA,sha256=2zYYO7C5y7OYuUwcIVSOdmJVGIanjNbIohwd7c9cl4M,19636
257
+ investing_algorithm_framework-7.16.11.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
258
+ investing_algorithm_framework-7.16.11.dist-info/entry_points.txt,sha256=jrPF0YksDs27vYzEvj3tXLe3OGWU24EJA05z5xHqmq8,91
259
+ investing_algorithm_framework-7.16.11.dist-info/RECORD,,