investing-algorithm-framework 1.3.1__py3-none-any.whl → 7.25.6__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.
Files changed (282) hide show
  1. investing_algorithm_framework/__init__.py +195 -16
  2. investing_algorithm_framework/analysis/__init__.py +16 -0
  3. investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
  4. investing_algorithm_framework/analysis/data.py +170 -0
  5. investing_algorithm_framework/analysis/markdown.py +91 -0
  6. investing_algorithm_framework/analysis/ranking.py +298 -0
  7. investing_algorithm_framework/app/__init__.py +31 -4
  8. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  9. investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
  10. investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
  11. investing_algorithm_framework/app/app.py +2233 -264
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1724 -0
  14. investing_algorithm_framework/app/eventloop.py +620 -0
  15. investing_algorithm_framework/app/reporting/__init__.py +27 -0
  16. investing_algorithm_framework/app/reporting/ascii.py +921 -0
  17. investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
  18. investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
  19. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  20. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  21. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
  22. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  23. investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
  24. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  25. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
  26. investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
  27. investing_algorithm_framework/app/reporting/generate.py +185 -0
  28. investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
  29. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
  30. investing_algorithm_framework/app/reporting/tables/stop_loss_table.py +0 -0
  31. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
  32. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
  33. investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
  34. investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
  35. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
  36. investing_algorithm_framework/app/stateless/action_handlers/__init__.py +6 -3
  37. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
  38. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
  39. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  40. investing_algorithm_framework/app/stateless/exception_handler.py +1 -1
  41. investing_algorithm_framework/app/strategy.py +873 -52
  42. investing_algorithm_framework/app/task.py +5 -3
  43. investing_algorithm_framework/app/web/__init__.py +2 -1
  44. investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
  45. investing_algorithm_framework/app/web/controllers/orders.py +4 -3
  46. investing_algorithm_framework/app/web/controllers/portfolio.py +1 -1
  47. investing_algorithm_framework/app/web/controllers/positions.py +3 -3
  48. investing_algorithm_framework/app/web/create_app.py +4 -2
  49. investing_algorithm_framework/app/web/error_handler.py +1 -1
  50. investing_algorithm_framework/app/web/schemas/order.py +2 -2
  51. investing_algorithm_framework/app/web/schemas/position.py +1 -0
  52. investing_algorithm_framework/cli/__init__.py +0 -0
  53. investing_algorithm_framework/cli/cli.py +231 -0
  54. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  55. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  56. investing_algorithm_framework/cli/initialize_app.py +603 -0
  57. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  58. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  59. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  60. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  61. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  62. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  63. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  64. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  65. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  66. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  67. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  68. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  69. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  70. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  71. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  72. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  73. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  74. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  75. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  76. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  77. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  78. investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
  79. investing_algorithm_framework/create_app.py +43 -9
  80. investing_algorithm_framework/dependency_container.py +121 -33
  81. investing_algorithm_framework/domain/__init__.py +109 -22
  82. investing_algorithm_framework/domain/algorithm_id.py +69 -0
  83. investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
  84. investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
  85. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
  86. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
  87. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
  88. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  89. investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
  90. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  91. investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
  92. investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
  93. investing_algorithm_framework/domain/config.py +60 -138
  94. investing_algorithm_framework/domain/constants.py +23 -34
  95. investing_algorithm_framework/domain/data_provider.py +334 -0
  96. investing_algorithm_framework/domain/data_structures.py +42 -0
  97. investing_algorithm_framework/domain/decimal_parsing.py +40 -0
  98. investing_algorithm_framework/domain/exceptions.py +51 -1
  99. investing_algorithm_framework/domain/models/__init__.py +29 -14
  100. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  101. investing_algorithm_framework/domain/models/base_model.py +3 -1
  102. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  103. investing_algorithm_framework/domain/models/data/data_source.py +222 -0
  104. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  105. investing_algorithm_framework/domain/models/event.py +35 -0
  106. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  107. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  108. investing_algorithm_framework/domain/models/order/__init__.py +3 -4
  109. investing_algorithm_framework/domain/models/order/order.py +243 -86
  110. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  111. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  112. investing_algorithm_framework/domain/models/portfolio/__init__.py +7 -2
  113. investing_algorithm_framework/domain/models/portfolio/portfolio.py +134 -1
  114. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -37
  115. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
  116. investing_algorithm_framework/domain/models/position/__init__.py +3 -2
  117. investing_algorithm_framework/domain/models/position/position.py +29 -0
  118. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  119. investing_algorithm_framework/domain/models/position/{position_cost.py → position_snapshot.py} +16 -8
  120. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  121. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  122. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  123. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  124. investing_algorithm_framework/domain/models/strategy_profile.py +33 -0
  125. investing_algorithm_framework/domain/models/time_frame.py +94 -98
  126. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  127. investing_algorithm_framework/domain/models/time_unit.py +111 -2
  128. investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
  129. investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
  130. investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
  131. investing_algorithm_framework/domain/models/trade/trade.py +389 -0
  132. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  133. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  134. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  135. investing_algorithm_framework/domain/order_executor.py +112 -0
  136. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  137. investing_algorithm_framework/domain/services/__init__.py +11 -0
  138. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  139. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  140. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  141. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  142. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  143. investing_algorithm_framework/domain/strategy.py +1 -29
  144. investing_algorithm_framework/domain/utils/__init__.py +16 -4
  145. investing_algorithm_framework/domain/utils/csv.py +22 -0
  146. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  147. investing_algorithm_framework/domain/utils/dates.py +57 -0
  148. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  149. investing_algorithm_framework/domain/utils/polars.py +53 -0
  150. investing_algorithm_framework/domain/utils/random.py +29 -0
  151. investing_algorithm_framework/download_data.py +244 -0
  152. investing_algorithm_framework/infrastructure/__init__.py +39 -11
  153. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  154. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
  155. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  156. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  157. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  158. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +87 -13
  159. investing_algorithm_framework/infrastructure/models/__init__.py +13 -4
  160. investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
  161. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
  162. investing_algorithm_framework/infrastructure/models/order/order.py +73 -73
  163. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  164. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  165. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +3 -2
  166. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
  167. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +57 -3
  168. investing_algorithm_framework/infrastructure/models/position/__init__.py +2 -2
  169. investing_algorithm_framework/infrastructure/models/position/position.py +16 -11
  170. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
  171. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  172. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  173. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  174. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  175. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  176. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  177. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  178. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  179. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  180. investing_algorithm_framework/infrastructure/repositories/__init__.py +13 -5
  181. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  182. investing_algorithm_framework/infrastructure/repositories/order_repository.py +32 -19
  183. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
  184. investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
  185. investing_algorithm_framework/infrastructure/repositories/position_repository.py +47 -4
  186. investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
  187. investing_algorithm_framework/infrastructure/repositories/repository.py +85 -31
  188. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  189. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  190. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  191. investing_algorithm_framework/infrastructure/services/__init__.py +9 -2
  192. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  193. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
  194. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  195. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  196. investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
  197. investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
  198. investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
  199. investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
  200. investing_algorithm_framework/services/__init__.py +127 -10
  201. investing_algorithm_framework/services/configuration_service.py +95 -0
  202. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  203. investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
  204. investing_algorithm_framework/services/market_credential_service.py +40 -0
  205. investing_algorithm_framework/services/metrics/__init__.py +119 -0
  206. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  207. investing_algorithm_framework/services/metrics/beta.py +0 -0
  208. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  209. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  210. investing_algorithm_framework/services/metrics/drawdown.py +218 -0
  211. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  212. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  213. investing_algorithm_framework/services/metrics/generate.py +358 -0
  214. investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
  215. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  216. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  217. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  218. investing_algorithm_framework/services/metrics/returns.py +452 -0
  219. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  220. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  221. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  222. investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
  223. investing_algorithm_framework/services/metrics/trades.py +473 -0
  224. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  225. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  226. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  227. investing_algorithm_framework/services/metrics/volatility.py +118 -0
  228. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  229. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  230. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  231. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  232. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  233. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  234. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  235. investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
  236. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  237. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  238. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  239. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  240. investing_algorithm_framework/services/positions/__init__.py +7 -0
  241. investing_algorithm_framework/services/positions/position_service.py +210 -0
  242. investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
  243. investing_algorithm_framework/services/repository_service.py +8 -2
  244. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  245. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
  246. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  247. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  248. investing_algorithm_framework/services/trade_service/__init__.py +9 -0
  249. investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
  250. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  251. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  252. investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
  253. investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
  254. {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
  255. investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
  256. investing_algorithm_framework/app/algorithm.py +0 -410
  257. investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
  258. investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
  259. investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -76
  260. investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
  261. investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
  262. investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
  263. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  264. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -205
  265. investing_algorithm_framework/domain/singleton.py +0 -9
  266. investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
  267. investing_algorithm_framework/infrastructure/models/position/position_cost.py +0 -32
  268. investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
  269. investing_algorithm_framework/infrastructure/repositories/position_cost_repository.py +0 -16
  270. investing_algorithm_framework/infrastructure/services/market_service.py +0 -422
  271. investing_algorithm_framework/services/market_data_service.py +0 -75
  272. investing_algorithm_framework/services/order_service.py +0 -464
  273. investing_algorithm_framework/services/portfolio_service.py +0 -105
  274. investing_algorithm_framework/services/position_cost_service.py +0 -5
  275. investing_algorithm_framework/services/position_service.py +0 -50
  276. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -219
  277. investing_algorithm_framework/setup_logging.py +0 -40
  278. investing_algorithm_framework-1.3.1.dist-info/AUTHORS.md +0 -8
  279. investing_algorithm_framework-1.3.1.dist-info/METADATA +0 -172
  280. investing_algorithm_framework-1.3.1.dist-info/RECORD +0 -103
  281. investing_algorithm_framework-1.3.1.dist-info/top_level.txt +0 -1
  282. {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
@@ -0,0 +1,162 @@
1
+ import json
2
+ import os
3
+ from dataclasses import dataclass
4
+ from logging import getLogger
5
+ from pathlib import Path
6
+
7
+ logger = getLogger(__name__)
8
+
9
+
10
+ @dataclass
11
+ class BacktestSummaryMetrics:
12
+ """
13
+ Represents the summarized results of a backtest,
14
+ focusing on key headline performance and risk metrics.
15
+
16
+ Attributes:
17
+ total_net_gain (float): Total net gain from the backtest.
18
+ total_net_gain_percentage (float): Total net gain percentage
19
+ from the backtest.
20
+ total_loss (float): Total gross loss from all trades.
21
+ total_loss_percentage (float): Total gross loss percentage.
22
+ total_growth (float): Total growth from the backtest.
23
+ total_growth_percentage (float): Total growth percentage
24
+ from the backtest.
25
+ average_net_gain (float): Average returns across multiple backtests.
26
+ average_net_gain_percentage (float): Average return percentage across
27
+ multiple backtests.
28
+ average_growth (float): Average growth across multiple backtests.
29
+ average_growth_percentage (float): Average growth percentage across
30
+ multiple backtests.
31
+ average_loss (float): Average loss across multiple backtests.
32
+ average_loss_percentage (float): Average loss percentage across
33
+ multiple backtests.
34
+ average_trade_return (float): Average return per trade.
35
+ average_trade_return_percentage (float): Average return percentage
36
+ per trade.
37
+ average_trade_loss (float): Total gross loss from all trades.
38
+ average_trade_loss_percentage (float): Average trade loss percentage.
39
+ average_trade_gain (float): Average gain from winning trades.
40
+ average_trade_gain_percentage (float): Average gain percentage
41
+ cagr (float): Compound annual growth rate of the backtest.
42
+ sharpe_ratio (float): Sharpe ratio, risk-adjusted return.
43
+ sortino_ratio (float): Sortino ratio, downside-risk adjusted return.
44
+ calmar_ratio (float): CAGR relative to max drawdown.
45
+ profit_factor (float): Total profit / total loss.
46
+ annual_volatility (float): Annualized volatility of returns.
47
+ max_drawdown (float): Maximum drawdown observed.
48
+ max_drawdown_duration (int): Duration of the maximum drawdown.
49
+ trades_per_year (float): Average trades executed per year.
50
+ win_rate (float): Percentage of winning trades.
51
+ current_win_rate (float): Win rate over recent trades.
52
+ win_loss_ratio (float): Ratio of average win to average loss.
53
+ current_win_loss_ratio (float): Win/loss ratio over recent trades.
54
+ number_of_trades (int): Total number of trades executed.
55
+ cumulative_exposure (float): Total exposure over the backtest period.
56
+ exposure_ratio (float): Ratio of exposure to available capital.
57
+ """
58
+ total_net_gain: float = None
59
+ total_net_gain_percentage: float = None
60
+ total_growth: float = None
61
+ total_growth_percentage: float = None
62
+ total_loss: float = None
63
+ total_loss_percentage: float = None
64
+ average_net_gain: float = None
65
+ average_net_gain_percentage: float = None
66
+ average_growth: float = None
67
+ average_growth_percentage: float = None
68
+ average_loss: float = None
69
+ average_loss_percentage: float = None
70
+ average_trade_return: float = None
71
+ average_trade_return_percentage: float = None
72
+ average_trade_loss: float = None
73
+ average_trade_loss_percentage: float = None
74
+ average_trade_gain: float = None
75
+ average_trade_gain_percentage: float = None
76
+ cagr: float = None
77
+ sharpe_ratio: float = None
78
+ sortino_ratio: float = None
79
+ calmar_ratio: float = None
80
+ profit_factor: float = None
81
+ annual_volatility: float = None
82
+ max_drawdown: float = None
83
+ max_drawdown_duration: int = None
84
+ trades_per_year: float = None
85
+ win_rate: float = None
86
+ current_win_rate: float = None
87
+ win_loss_ratio: float = None
88
+ current_win_loss_ratio: float = None
89
+ number_of_trades: int = None
90
+ number_of_trades_closed: int = None
91
+ cumulative_exposure: float = None
92
+ exposure_ratio: float = None
93
+
94
+ def to_dict(self) -> dict:
95
+ """
96
+ Convert the BacktestSummaryMetrics instance to a dictionary.
97
+ """
98
+ return {
99
+ "total_net_gain": self.total_net_gain,
100
+ "total_net_gain_percentage": self.total_net_gain_percentage,
101
+ "total_growth": self.total_growth,
102
+ "total_growth_percentage": self.total_growth_percentage,
103
+ "total_loss": self.total_loss,
104
+ "total_loss_percentage": self.total_loss_percentage,
105
+ "average_loss": self.average_loss,
106
+ "average_loss_percentage": self.average_loss_percentage,
107
+ "average_net_gain": self.average_net_gain,
108
+ "average_net_gain_percentage": self.average_net_gain_percentage,
109
+ "average_growth": self.average_growth,
110
+ "average_growth_percentage": self.average_growth_percentage,
111
+ "average_trade_return": self.average_trade_return,
112
+ "average_trade_return_percentage":
113
+ self.average_trade_return_percentage,
114
+ "average_trade_loss": self.average_trade_loss,
115
+ "average_trade_loss_percentage":
116
+ self.average_trade_loss_percentage,
117
+ "average_trade_gain": self.average_trade_gain,
118
+ "average_trade_gain_percentage":
119
+ self.average_trade_gain_percentage,
120
+ "cagr": self.cagr,
121
+ "sharpe_ratio": self.sharpe_ratio,
122
+ "sortino_ratio": self.sortino_ratio,
123
+ "calmar_ratio": self.calmar_ratio,
124
+ "profit_factor": self.profit_factor,
125
+ "annual_volatility": self.annual_volatility,
126
+ "max_drawdown": self.max_drawdown,
127
+ "max_drawdown_duration": self.max_drawdown_duration,
128
+ "trades_per_year": self.trades_per_year,
129
+ "win_rate": self.win_rate,
130
+ "current_win_rate": self.current_win_rate,
131
+ "win_loss_ratio": self.win_loss_ratio,
132
+ "current_win_loss_ratio": self.current_win_loss_ratio,
133
+ "number_of_trades": self.number_of_trades,
134
+ "number_of_trades_closed": self.number_of_trades_closed,
135
+ "cumulative_exposure": self.cumulative_exposure,
136
+ "exposure_ratio": self.exposure_ratio,
137
+ }
138
+
139
+ def save(self, file_path: str | Path) -> None:
140
+ """
141
+ Save the summary metrics to a JSON file.
142
+ """
143
+ with open(file_path, 'w') as file:
144
+ json.dump(self.to_dict(), file, indent=4, default=str)
145
+
146
+ @staticmethod
147
+ def open(file_path: str | Path) -> 'BacktestSummaryMetrics':
148
+ """
149
+ Load summary metrics from a JSON file.
150
+ """
151
+ if not os.path.exists(file_path):
152
+ raise FileNotFoundError(f"Metrics file not found at {file_path}")
153
+
154
+ with open(file_path, 'r') as file:
155
+ data = json.load(file)
156
+
157
+ return BacktestSummaryMetrics(**data)
158
+
159
+ def __repr__(self):
160
+ return json.dumps(
161
+ self.to_dict(), indent=4, sort_keys=True, default=str
162
+ )
@@ -0,0 +1,198 @@
1
+ import os
2
+ from logging import getLogger
3
+ from pathlib import Path
4
+ from random import Random
5
+ from typing import List, Union, Callable
6
+
7
+ from investing_algorithm_framework.domain.exceptions import \
8
+ OperationalException
9
+ from investing_algorithm_framework.domain.utils.custom_tqdm import tqdm
10
+
11
+ from .backtest import Backtest
12
+
13
+ logger = getLogger("investing_algorithm_framework")
14
+
15
+
16
+ def save_backtests_to_directory(
17
+ backtests: List[Backtest],
18
+ directory_path: Union[str, Path],
19
+ backtest_date_range=None,
20
+ dir_name_generation_function: Callable[[Backtest], str] = None,
21
+ number_of_backtests_to_save: int = None,
22
+ filter_function: Callable[[Backtest], bool] = None,
23
+ show_progress: bool = False
24
+ ) -> None:
25
+ """
26
+ Saves a list of Backtest objects to the specified directory.
27
+
28
+ Args:
29
+ backtests (List[Backtest]): List of Backtest objects to save.
30
+ directory_path (str): Path to the directory where backtests
31
+ will be saved.
32
+ backtest_date_range (BacktestDateRange, optional): Date range
33
+ to filter backtests before saving. If provided, only backtest runs
34
+ with this date range will be saved. Defaults to None.
35
+ dir_name_generation_function (Callable[[Backtest], str], optional):
36
+ A function that takes a Backtest object as input and returns
37
+ a string to be used as the directory name for that backtest.
38
+ If not provided, the backtest's algorithm_id will be used.
39
+ Defaults to None.
40
+ number_of_backtests_to_save (int, optional): Maximum number of
41
+ backtests to save. If None, all backtests will be saved.
42
+ filter_function (Callable[[Backtest], bool], optional): A function
43
+ that takes a Backtest object as input and returns True if the
44
+ backtest should be saved. Defaults to None.
45
+ show_progress (bool, optional): Whether to display a progress bar
46
+ while saving backtests. Defaults to False.
47
+
48
+ Returns:
49
+ None
50
+ """
51
+
52
+ if not os.path.exists(directory_path):
53
+ os.makedirs(directory_path)
54
+
55
+ if show_progress:
56
+ backtests = tqdm(backtests, desc="Saving backtests")
57
+
58
+ for backtest in backtests:
59
+
60
+ # Check if we have reached the limit of backtests to save
61
+ if number_of_backtests_to_save is not None:
62
+ if number_of_backtests_to_save <= 0:
63
+ break
64
+
65
+ number_of_backtests_to_save -= 1
66
+
67
+ if filter_function is not None:
68
+ if not filter_function(backtest):
69
+ continue
70
+
71
+ if dir_name_generation_function is not None:
72
+ dir_name = dir_name_generation_function(backtest)
73
+ else:
74
+
75
+ if not hasattr(backtest, "algorithm_id"):
76
+ raise OperationalException(
77
+ "algorithm_id is not set in backtest instance,"
78
+ "cannot generate directory name automatically, "
79
+ "please make sure to set the algorithm_id field "
80
+ "in your strategies or provide "
81
+ "a dir_name_generation_function."
82
+ )
83
+
84
+ # Check if there is an ID in the backtest metadata
85
+ dir_name = backtest.algorithm_id
86
+
87
+ if dir_name is None:
88
+ logger.warning(
89
+ "Backtest algorithm_id is None. "
90
+ "Generating a random directory name."
91
+ )
92
+ dir_name = str(Random().randint(100000, 999999))
93
+
94
+ backtest.save(
95
+ os.path.join(directory_path, dir_name),
96
+ backtest_date_ranges=[backtest_date_range]
97
+ if backtest_date_range else None
98
+ )
99
+
100
+
101
+ def load_backtests_from_directory(
102
+ directory_path: Union[str, Path],
103
+ filter_function: Callable[[Backtest], bool] = None,
104
+ number_of_backtests_to_load: int = None,
105
+ show_progress: bool = False
106
+ ) -> List[Backtest]:
107
+ """
108
+ Loads Backtest objects from the specified directory.
109
+
110
+ Args:
111
+ directory_path (str): Path to the directory from which backtests
112
+ will be loaded.
113
+ filter_function (Callable[[Backtest], bool], optional): A function
114
+ that takes a Backtest object as input and returns True if the
115
+ backtest should be included in the result. Defaults to None.
116
+ number_of_backtests_to_load (int, optional): Maximum number of
117
+ backtests to load. If None, all backtests will be loaded.
118
+ show_progress (bool, optional): Whether to display a progress bar
119
+ while loading backtests. Defaults to False.
120
+
121
+ Returns:
122
+ List[Backtest]: List of loaded Backtest objects.
123
+ """
124
+
125
+ backtests = []
126
+
127
+ if not os.path.exists(directory_path):
128
+ logger.warning(
129
+ f"Directory {directory_path} does not exist. "
130
+ "No backtests loaded."
131
+ )
132
+ return backtests
133
+
134
+ dirs = os.listdir(directory_path)
135
+
136
+ if show_progress:
137
+ dirs = tqdm(dirs, desc="Loading backtests")
138
+
139
+ for file_name in dirs:
140
+
141
+ # Check if the filename is not the checkpoints.json file or
142
+ # a python file
143
+ if file_name == "checkpoints.json" or file_name.endswith(".py"):
144
+ continue
145
+
146
+ # Check if we have reached the limit of backtests to load
147
+ if number_of_backtests_to_load is not None:
148
+ if number_of_backtests_to_load <= 0:
149
+ break
150
+ number_of_backtests_to_load -= 1
151
+
152
+ file_path = os.path.join(directory_path, file_name)
153
+
154
+ try:
155
+
156
+ # Add step-by-step debugging
157
+ try:
158
+ backtest = Backtest.open(file_path)
159
+ except KeyError as ke:
160
+ logger.error(
161
+ f"KeyError during Backtest.open for {file_path}: {ke}"
162
+ )
163
+ import traceback
164
+ logger.error(
165
+ f"Backtest.open KeyError "
166
+ f"traceback: {traceback.format_exc()}"
167
+ )
168
+ continue # Skip this backtest and continue with the next one
169
+ except Exception as be:
170
+ logger.error(
171
+ f"Other error during Backtest.open for {file_path}: {be}"
172
+ )
173
+ import traceback
174
+ logger.error(
175
+ f"Backtest.open error traceback: {traceback.format_exc()}"
176
+ )
177
+ continue # Skip this backtest and continue with the next one
178
+
179
+ if filter_function is not None:
180
+ try:
181
+ if not filter_function(backtest):
182
+ continue
183
+ except Exception as fe:
184
+ logger.error(
185
+ f"Error in filter_function for {file_path}: {fe}"
186
+ )
187
+ continue
188
+
189
+ backtests.append(backtest)
190
+
191
+ except Exception as e:
192
+ logger.error(
193
+ f"Unexpected top-level error loading "
194
+ f"backtest from {file_path}: {e}"
195
+ )
196
+ import traceback
197
+
198
+ return backtests