investing-algorithm-framework 6.9.1__py3-none-any.whl → 7.19.15__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.

Files changed (192) hide show
  1. investing_algorithm_framework/__init__.py +147 -44
  2. investing_algorithm_framework/app/__init__.py +23 -6
  3. investing_algorithm_framework/app/algorithm/algorithm.py +5 -41
  4. investing_algorithm_framework/app/algorithm/algorithm_factory.py +17 -10
  5. investing_algorithm_framework/app/analysis/__init__.py +15 -0
  6. investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
  7. investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
  8. investing_algorithm_framework/app/analysis/permutation.py +116 -0
  9. investing_algorithm_framework/app/analysis/ranking.py +297 -0
  10. investing_algorithm_framework/app/app.py +1322 -707
  11. investing_algorithm_framework/app/context.py +196 -88
  12. investing_algorithm_framework/app/eventloop.py +590 -0
  13. investing_algorithm_framework/app/reporting/__init__.py +16 -5
  14. investing_algorithm_framework/app/reporting/ascii.py +57 -202
  15. investing_algorithm_framework/app/reporting/backtest_report.py +284 -170
  16. investing_algorithm_framework/app/reporting/charts/__init__.py +10 -2
  17. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  18. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  19. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +11 -26
  20. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  21. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  22. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +1 -1
  23. investing_algorithm_framework/app/reporting/generate.py +100 -114
  24. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +40 -32
  25. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +34 -27
  26. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +23 -19
  27. investing_algorithm_framework/app/reporting/tables/trades_table.py +1 -1
  28. investing_algorithm_framework/app/reporting/tables/utils.py +1 -0
  29. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +10 -16
  30. investing_algorithm_framework/app/strategy.py +315 -175
  31. investing_algorithm_framework/app/task.py +5 -3
  32. investing_algorithm_framework/cli/cli.py +30 -12
  33. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +131 -34
  34. investing_algorithm_framework/cli/initialize_app.py +20 -1
  35. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +18 -6
  36. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  37. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  38. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -2
  39. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +1 -1
  40. investing_algorithm_framework/create_app.py +3 -5
  41. investing_algorithm_framework/dependency_container.py +25 -39
  42. investing_algorithm_framework/domain/__init__.py +45 -38
  43. investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
  44. investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
  45. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
  46. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
  47. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
  48. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  49. investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
  50. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  51. investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
  52. investing_algorithm_framework/domain/config.py +27 -0
  53. investing_algorithm_framework/domain/constants.py +6 -34
  54. investing_algorithm_framework/domain/data_provider.py +200 -56
  55. investing_algorithm_framework/domain/exceptions.py +34 -1
  56. investing_algorithm_framework/domain/models/__init__.py +10 -19
  57. investing_algorithm_framework/domain/models/base_model.py +0 -6
  58. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  59. investing_algorithm_framework/domain/models/data/data_source.py +214 -0
  60. investing_algorithm_framework/domain/models/{market_data_type.py → data/data_type.py} +7 -7
  61. investing_algorithm_framework/domain/models/market/market_credential.py +6 -0
  62. investing_algorithm_framework/domain/models/order/order.py +34 -13
  63. investing_algorithm_framework/domain/models/order/order_status.py +1 -1
  64. investing_algorithm_framework/domain/models/order/order_type.py +1 -1
  65. investing_algorithm_framework/domain/models/portfolio/portfolio.py +14 -1
  66. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +5 -1
  67. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +51 -11
  68. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  69. investing_algorithm_framework/domain/models/position/position.py +9 -0
  70. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  71. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  72. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  73. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  74. investing_algorithm_framework/domain/models/snapshot_interval.py +0 -1
  75. investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
  76. investing_algorithm_framework/domain/models/time_frame.py +7 -0
  77. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  78. investing_algorithm_framework/domain/models/time_unit.py +63 -1
  79. investing_algorithm_framework/domain/models/trade/__init__.py +0 -2
  80. investing_algorithm_framework/domain/models/trade/trade.py +56 -32
  81. investing_algorithm_framework/domain/models/trade/trade_status.py +8 -2
  82. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +106 -41
  83. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +161 -99
  84. investing_algorithm_framework/domain/order_executor.py +19 -0
  85. investing_algorithm_framework/domain/portfolio_provider.py +20 -1
  86. investing_algorithm_framework/domain/services/__init__.py +0 -13
  87. investing_algorithm_framework/domain/strategy.py +1 -29
  88. investing_algorithm_framework/domain/utils/__init__.py +5 -1
  89. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  90. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  91. investing_algorithm_framework/domain/utils/polars.py +17 -14
  92. investing_algorithm_framework/download_data.py +40 -10
  93. investing_algorithm_framework/infrastructure/__init__.py +13 -25
  94. investing_algorithm_framework/infrastructure/data_providers/__init__.py +7 -4
  95. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +811 -546
  96. investing_algorithm_framework/infrastructure/data_providers/csv.py +433 -122
  97. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  98. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  99. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +81 -0
  100. investing_algorithm_framework/infrastructure/models/__init__.py +0 -13
  101. investing_algorithm_framework/infrastructure/models/order/order.py +9 -3
  102. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +27 -8
  103. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +21 -7
  104. investing_algorithm_framework/infrastructure/order_executors/__init__.py +2 -0
  105. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  106. investing_algorithm_framework/infrastructure/repositories/repository.py +16 -2
  107. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +2 -2
  108. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +6 -0
  109. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +6 -0
  110. investing_algorithm_framework/infrastructure/services/__init__.py +0 -4
  111. investing_algorithm_framework/services/__init__.py +105 -8
  112. investing_algorithm_framework/services/backtesting/backtest_service.py +536 -476
  113. investing_algorithm_framework/services/configuration_service.py +14 -4
  114. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  115. investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
  116. investing_algorithm_framework/{app/reporting → services}/metrics/__init__.py +48 -17
  117. investing_algorithm_framework/{app/reporting → services}/metrics/drawdown.py +10 -10
  118. investing_algorithm_framework/{app/reporting → services}/metrics/equity_curve.py +2 -2
  119. investing_algorithm_framework/{app/reporting → services}/metrics/exposure.py +60 -2
  120. investing_algorithm_framework/services/metrics/generate.py +358 -0
  121. investing_algorithm_framework/{app/reporting → services}/metrics/profit_factor.py +36 -0
  122. investing_algorithm_framework/{app/reporting → services}/metrics/recovery.py +2 -2
  123. investing_algorithm_framework/{app/reporting → services}/metrics/returns.py +146 -147
  124. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  125. investing_algorithm_framework/{app/reporting/metrics/sharp_ratio.py → services/metrics/sharpe_ratio.py} +6 -10
  126. investing_algorithm_framework/{app/reporting → services}/metrics/sortino_ratio.py +3 -7
  127. investing_algorithm_framework/services/metrics/trades.py +500 -0
  128. investing_algorithm_framework/services/metrics/volatility.py +97 -0
  129. investing_algorithm_framework/{app/reporting → services}/metrics/win_rate.py +70 -3
  130. investing_algorithm_framework/services/order_service/order_backtest_service.py +21 -31
  131. investing_algorithm_framework/services/order_service/order_service.py +9 -71
  132. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -2
  133. investing_algorithm_framework/services/portfolios/portfolio_service.py +3 -13
  134. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +62 -96
  135. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -3
  136. investing_algorithm_framework/services/repository_service.py +5 -2
  137. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  138. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -0
  139. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  140. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  141. investing_algorithm_framework/services/trade_service/__init__.py +7 -1
  142. investing_algorithm_framework/services/trade_service/trade_service.py +51 -29
  143. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  144. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  145. investing_algorithm_framework-7.19.15.dist-info/METADATA +537 -0
  146. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/RECORD +159 -148
  147. investing_algorithm_framework/app/reporting/evaluation.py +0 -243
  148. investing_algorithm_framework/app/reporting/metrics/risk_free_rate.py +0 -8
  149. investing_algorithm_framework/app/reporting/metrics/volatility.py +0 -69
  150. investing_algorithm_framework/cli/templates/requirements_azure_function.txt.template +0 -3
  151. investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -9
  152. investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -47
  153. investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
  154. investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -0
  155. investing_algorithm_framework/domain/models/backtesting/backtest_results.py +0 -440
  156. investing_algorithm_framework/domain/models/data_source.py +0 -21
  157. investing_algorithm_framework/domain/models/date_range.py +0 -64
  158. investing_algorithm_framework/domain/models/trade/trade_risk_type.py +0 -34
  159. investing_algorithm_framework/domain/models/trading_data_types.py +0 -48
  160. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  161. investing_algorithm_framework/domain/services/market_data_sources.py +0 -543
  162. investing_algorithm_framework/domain/services/market_service.py +0 -153
  163. investing_algorithm_framework/domain/services/observable.py +0 -51
  164. investing_algorithm_framework/domain/services/observer.py +0 -19
  165. investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -16
  166. investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -746
  167. investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -270
  168. investing_algorithm_framework/infrastructure/models/market_data_sources/pandas.py +0 -312
  169. investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
  170. investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -471
  171. investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
  172. investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
  173. investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -322
  174. investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -10
  175. investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -269
  176. investing_algorithm_framework/services/market_data_source_service/data_provider_service.py +0 -350
  177. investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -377
  178. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -296
  179. investing_algorithm_framework-6.9.1.dist-info/METADATA +0 -440
  180. /investing_algorithm_framework/{app/reporting → services}/metrics/alpha.py +0 -0
  181. /investing_algorithm_framework/{app/reporting → services}/metrics/beta.py +0 -0
  182. /investing_algorithm_framework/{app/reporting → services}/metrics/cagr.py +0 -0
  183. /investing_algorithm_framework/{app/reporting → services}/metrics/calmar_ratio.py +0 -0
  184. /investing_algorithm_framework/{app/reporting → services}/metrics/mean_daily_return.py +0 -0
  185. /investing_algorithm_framework/{app/reporting → services}/metrics/price_efficiency.py +0 -0
  186. /investing_algorithm_framework/{app/reporting → services}/metrics/standard_deviation.py +0 -0
  187. /investing_algorithm_framework/{app/reporting → services}/metrics/treynor_ratio.py +0 -0
  188. /investing_algorithm_framework/{app/reporting → services}/metrics/ulcer.py +0 -0
  189. /investing_algorithm_framework/{app/reporting → services}/metrics/value_at_risk.py +0 -0
  190. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
  191. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
  192. {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,275 @@
1
+ import os
2
+ import json
3
+
4
+ from dataclasses import dataclass, field
5
+ from typing import List, Dict
6
+ import numpy as np
7
+ import pandas as pd
8
+ from datetime import timezone
9
+
10
+ from .backtest_metrics import BacktestMetrics
11
+
12
+
13
+ @dataclass
14
+ class BacktestPermutationTest:
15
+ """
16
+ Represents the result of a permutation test on backtest metrics.
17
+
18
+ Attributes:
19
+ real_metrics (BacktestMetrics): The real backtest metrics.
20
+ permutated_metrics (List[BacktestMetrics]): A list of backtest
21
+ metrics objects from permuted backtests.
22
+ p_values (Dict[str, float]): A dictionary mapping metric names
23
+ to their permutation test p-values.
24
+ """
25
+
26
+ # Default set of metrics for permutation testing
27
+ DEFAULT_METRICS: List[str] = field(default_factory=lambda: [
28
+ "cagr",
29
+ "sharpe_ratio",
30
+ "sortino_ratio",
31
+ "calmar_ratio",
32
+ "profit_factor",
33
+ "annual_volatility",
34
+ "max_drawdown",
35
+ "win_rate",
36
+ "win_loss_ratio",
37
+ "average_monthly_return"
38
+ ])
39
+ real_metrics: BacktestMetrics = None
40
+ permutated_metrics: List[BacktestMetrics] = field(default_factory=list)
41
+ p_values: Dict[str, float] = field(default_factory=dict)
42
+ ohlcv_permutated_datasets: Dict[str, List[pd.DataFrame]] = \
43
+ field(default_factory=dict)
44
+ ohlcv_original_datasets: Dict[str, pd.DataFrame] = \
45
+ field(default_factory=dict)
46
+ backtest_start_date: pd.Timestamp = None
47
+ backtest_end_date: pd.Timestamp = None
48
+ backtest_date_range_name: str = None
49
+
50
+ def compute_p_values(
51
+ self, metrics: List[str] = None, one_sided: bool = True
52
+ ) -> None:
53
+ """
54
+ Compute p-values for the selected metrics based on the
55
+ permutation distribution.
56
+
57
+ Args:
58
+ metrics (List[str]): List of metric names to compute p-values for.
59
+ If None, uses DEFAULT_METRICS.
60
+ one_sided (bool): Whether to compute a one-sided
61
+ test (default: True).
62
+ """
63
+ if metrics is None:
64
+ metrics = self.DEFAULT_METRICS
65
+
66
+ self.p_values = {}
67
+
68
+ for metric in metrics:
69
+ real_value = getattr(self.real_metrics, metric, None)
70
+ if real_value is None:
71
+ continue
72
+
73
+ # Collect metric values across all permuted backtests
74
+ dist = np.array([
75
+ getattr(pm, metric, None)
76
+ for pm in self.permutated_metrics
77
+ if getattr(pm, metric, None) is not None
78
+ ])
79
+
80
+ if len(dist) == 0:
81
+ continue
82
+
83
+ if one_sided:
84
+ p = np.mean(dist >= real_value)
85
+ else:
86
+ p = np.mean(np.abs(dist) >= abs(real_value))
87
+
88
+ self.p_values[metric] = float(p)
89
+
90
+ def summary(
91
+ self, metrics: List[str] = None
92
+ ) -> Dict[str, Dict[str, float]]:
93
+ """
94
+ Return a summary of real values, mean permuted values, and p-values.
95
+
96
+ Args:
97
+ metrics (List[str]): List of metric names to include
98
+ in the summary. If None, uses DEFAULT_METRICS.
99
+
100
+ Returns:
101
+ Dict[str, Dict[str, float]]: A dictionary where each key
102
+ is a metric name and the value is another dictionary
103
+ with keys 'real', 'permuted_mean', and 'p_value'.
104
+ """
105
+
106
+ if metrics is None:
107
+ metrics = self.DEFAULT_METRICS
108
+
109
+ if not self.p_values: # lazy compute
110
+ self.compute_p_values(metrics=metrics)
111
+
112
+ summary_dict = {}
113
+ for metric in metrics:
114
+ real_value = getattr(self.real_metrics, metric, None)
115
+ if real_value is None:
116
+ continue
117
+
118
+ dist = np.array([
119
+ getattr(pm, metric, None)
120
+ for pm in self.permutated_metrics
121
+ if getattr(pm, metric, None) is not None
122
+ ])
123
+
124
+ # Filter out inf / nan
125
+ dist = dist[np.isfinite(dist)]
126
+
127
+ real_value = getattr(self.real_metrics, metric, None)
128
+
129
+ if real_value is None or not np.isfinite(real_value):
130
+ continue
131
+
132
+ if len(dist) == 0:
133
+ continue
134
+
135
+ summary_dict[metric] = {
136
+ "real": float(real_value),
137
+ "permuted_mean": float(np.mean(dist)),
138
+ "p_value": self.p_values.get(metric, None),
139
+ }
140
+
141
+ return summary_dict
142
+
143
+ def save(self, path: str) -> None:
144
+ """
145
+ Save the permutation test results to disk (JSON + Parquet).
146
+
147
+ Args:
148
+ path (str): The directory path where to save the results.
149
+
150
+ Returns:
151
+ None
152
+ """
153
+ def ensure_iso(value):
154
+ if hasattr(value, "isoformat"):
155
+ if value.tzinfo is None:
156
+ value = value.replace(tzinfo=timezone.utc)
157
+ return value.isoformat()
158
+ return value
159
+
160
+ os.makedirs(path, exist_ok=True)
161
+
162
+ # Save the real metrics
163
+ self.real_metrics.save(os.path.join(path, "original_metrics.json"))
164
+
165
+ permuted_dir = os.path.join(path, "permuted_metrics")
166
+ os.makedirs(permuted_dir, exist_ok=True)
167
+
168
+ for i, pm in enumerate(self.permutated_metrics):
169
+ pm.save(os.path.join(permuted_dir, f"permuted_{i}.json"))
170
+
171
+ # Save the P-values
172
+ with open(os.path.join(path, "p_values.json"), "w") as f:
173
+ json.dump(self.p_values, f)
174
+
175
+ # Create a metadata file to store additional info such as
176
+ # date range name, start and end dates
177
+ metadata = {
178
+ "backtest_start_date": ensure_iso(self.backtest_start_date),
179
+ "backtest_date_range_name": self.backtest_date_range_name,
180
+ "backtest_end_date": ensure_iso(self.backtest_end_date),
181
+ }
182
+
183
+ with open(os.path.join(path, "metadata.json"), "w") as f:
184
+ json.dump(metadata, f)
185
+
186
+ @staticmethod
187
+ def open(path: str) -> "BacktestPermutationTest":
188
+ """
189
+ Load the permutation test results from disk (JSON + Parquet).
190
+
191
+ Args:
192
+ path (str): The directory path where the results are saved.
193
+
194
+ Returns:
195
+ BacktestPermutationTest: The loaded permutation test results.
196
+ """
197
+ original_metrics = os.path.join(path, "original_metrics.json")
198
+
199
+ # Rehydrate BacktestMetrics
200
+ real_metrics = BacktestMetrics.open(original_metrics)
201
+
202
+ permuted_dir = os.path.join(path, "permuted_metrics")
203
+
204
+ permutated_metrics = []
205
+ if os.path.exists(permuted_dir):
206
+ for fname in os.listdir(permuted_dir):
207
+ if fname.startswith("permuted_"):
208
+ pm = BacktestMetrics.open(
209
+ os.path.join(permuted_dir, fname)
210
+ )
211
+ permutated_metrics.append(pm)
212
+
213
+ p_values_path = os.path.join(path, "p_values.json")
214
+ p_values = {}
215
+
216
+ if os.path.exists(p_values_path):
217
+ with open(p_values_path, "r") as f:
218
+ p_values = json.load(f)
219
+
220
+ # Load metadata
221
+ metadata_path = os.path.join(path, "metadata.json")
222
+ backtest_start_date = None
223
+ backtest_end_date = None
224
+ backtest_date_range_name = None
225
+
226
+ if os.path.exists(metadata_path):
227
+ with open(metadata_path, "r") as f:
228
+ metadata = json.load(f)
229
+
230
+ backtest_start_date = pd.to_datetime(
231
+ metadata.get("backtest_start_date"), utc=True
232
+ )
233
+ backtest_end_date = pd.to_datetime(
234
+ metadata.get("backtest_end_date"), utc=True
235
+ )
236
+ backtest_date_range_name = metadata.get(
237
+ "backtest_date_range_name"
238
+ )
239
+
240
+ return BacktestPermutationTest(
241
+ real_metrics=real_metrics,
242
+ permutated_metrics=permutated_metrics,
243
+ p_values=p_values,
244
+ backtest_start_date=backtest_start_date,
245
+ backtest_end_date=backtest_end_date,
246
+ backtest_date_range_name=backtest_date_range_name
247
+ )
248
+
249
+ def create_directory_name(self) -> str:
250
+ """
251
+ Create a directory name for the backtest run based on its attributes.
252
+
253
+ Returns:
254
+ str: A string representing the directory name.
255
+ """
256
+ start_str = self.real_metrics.backtest_start_date.strftime("%Y%m%d")
257
+ end_str = self.real_metrics.backtest_end_date.strftime("%Y%m%d")
258
+ dir_name = f"permutation_test_{start_str}_{end_str}"
259
+ return dir_name
260
+
261
+ def to_dict(self) -> Dict:
262
+ """
263
+ Convert the permutation test results to a dictionary.
264
+
265
+ Returns:
266
+ dict: A dictionary representation of the permutation test results.
267
+ """
268
+ return {
269
+ "real_metrics": self.real_metrics.to_dict(),
270
+ "permutated_metrics": [
271
+ pm.to_dict() for pm in self.permutated_metrics
272
+ ],
273
+ "p_values": self.p_values,
274
+ # Note: DataFrames are not included in the dict representation
275
+ }