investing-algorithm-framework 3.7.0__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 (256) hide show
  1. investing_algorithm_framework/__init__.py +168 -45
  2. investing_algorithm_framework/app/__init__.py +32 -1
  3. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  4. investing_algorithm_framework/app/algorithm/algorithm.py +239 -0
  5. investing_algorithm_framework/app/algorithm/algorithm_factory.py +114 -0
  6. investing_algorithm_framework/app/analysis/__init__.py +15 -0
  7. investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
  8. investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
  9. investing_algorithm_framework/app/analysis/permutation.py +116 -0
  10. investing_algorithm_framework/app/analysis/ranking.py +297 -0
  11. investing_algorithm_framework/app/app.py +1933 -589
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1725 -0
  14. investing_algorithm_framework/app/eventloop.py +590 -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 +4 -2
  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 +1 -1
  39. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  40. investing_algorithm_framework/app/strategy.py +664 -84
  41. investing_algorithm_framework/app/task.py +5 -3
  42. investing_algorithm_framework/app/web/__init__.py +2 -1
  43. investing_algorithm_framework/app/web/create_app.py +4 -2
  44. investing_algorithm_framework/cli/__init__.py +0 -0
  45. investing_algorithm_framework/cli/cli.py +226 -0
  46. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  47. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  48. investing_algorithm_framework/cli/initialize_app.py +603 -0
  49. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  50. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  51. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  52. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  53. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  54. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  55. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  56. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  57. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  58. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  59. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  60. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  61. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  62. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  63. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  64. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  65. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  66. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  67. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  68. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  69. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  70. investing_algorithm_framework/create_app.py +40 -6
  71. investing_algorithm_framework/dependency_container.py +72 -56
  72. investing_algorithm_framework/domain/__init__.py +71 -47
  73. investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
  74. investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
  75. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
  76. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
  77. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
  78. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  79. investing_algorithm_framework/domain/backtesting/backtest_run.py +605 -0
  80. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  81. investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
  82. investing_algorithm_framework/domain/config.py +59 -91
  83. investing_algorithm_framework/domain/constants.py +13 -38
  84. investing_algorithm_framework/domain/data_provider.py +334 -0
  85. investing_algorithm_framework/domain/data_structures.py +3 -2
  86. investing_algorithm_framework/domain/exceptions.py +51 -1
  87. investing_algorithm_framework/domain/models/__init__.py +17 -12
  88. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  89. investing_algorithm_framework/domain/models/data/data_source.py +214 -0
  90. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  91. investing_algorithm_framework/domain/models/event.py +35 -0
  92. investing_algorithm_framework/domain/models/market/market_credential.py +55 -1
  93. investing_algorithm_framework/domain/models/order/order.py +77 -83
  94. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  95. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  96. investing_algorithm_framework/domain/models/portfolio/portfolio.py +81 -3
  97. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +26 -3
  98. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
  99. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  100. investing_algorithm_framework/domain/models/position/position.py +12 -0
  101. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  102. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  103. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  104. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  105. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  106. investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
  107. investing_algorithm_framework/domain/models/time_frame.py +37 -0
  108. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  109. investing_algorithm_framework/domain/models/time_unit.py +66 -2
  110. investing_algorithm_framework/domain/models/trade/__init__.py +8 -1
  111. investing_algorithm_framework/domain/models/trade/trade.py +295 -171
  112. investing_algorithm_framework/domain/models/trade/trade_status.py +9 -2
  113. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  114. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  115. investing_algorithm_framework/domain/order_executor.py +112 -0
  116. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  117. investing_algorithm_framework/domain/services/__init__.py +2 -9
  118. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +0 -6
  119. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  120. investing_algorithm_framework/domain/strategy.py +1 -29
  121. investing_algorithm_framework/domain/utils/__init__.py +12 -7
  122. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  123. investing_algorithm_framework/domain/utils/dates.py +57 -0
  124. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  125. investing_algorithm_framework/domain/utils/polars.py +53 -0
  126. investing_algorithm_framework/domain/utils/random.py +29 -0
  127. investing_algorithm_framework/download_data.py +108 -0
  128. investing_algorithm_framework/infrastructure/__init__.py +31 -18
  129. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  130. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -0
  131. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  132. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  133. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  134. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
  135. investing_algorithm_framework/infrastructure/models/__init__.py +6 -11
  136. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -1
  137. investing_algorithm_framework/infrastructure/models/order/order.py +35 -49
  138. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  139. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  140. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +1 -1
  141. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -0
  142. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -5
  143. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  144. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  145. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  146. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  147. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  148. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  149. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  150. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  151. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  152. investing_algorithm_framework/infrastructure/repositories/__init__.py +8 -0
  153. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  154. investing_algorithm_framework/infrastructure/repositories/order_repository.py +5 -0
  155. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +1 -1
  156. investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
  157. investing_algorithm_framework/infrastructure/repositories/repository.py +81 -27
  158. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  159. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  160. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  161. investing_algorithm_framework/infrastructure/services/__init__.py +4 -4
  162. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  163. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -0
  164. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  165. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  166. investing_algorithm_framework/services/__init__.py +113 -16
  167. investing_algorithm_framework/services/backtesting/__init__.py +0 -7
  168. investing_algorithm_framework/services/backtesting/backtest_service.py +566 -359
  169. investing_algorithm_framework/services/configuration_service.py +77 -11
  170. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  171. investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
  172. investing_algorithm_framework/services/market_credential_service.py +16 -1
  173. investing_algorithm_framework/services/metrics/__init__.py +114 -0
  174. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  175. investing_algorithm_framework/services/metrics/beta.py +0 -0
  176. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  177. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  178. investing_algorithm_framework/services/metrics/drawdown.py +181 -0
  179. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  180. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  181. investing_algorithm_framework/services/metrics/generate.py +358 -0
  182. investing_algorithm_framework/services/metrics/mean_daily_return.py +83 -0
  183. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  184. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  185. investing_algorithm_framework/services/metrics/returns.py +452 -0
  186. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  187. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  188. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  189. investing_algorithm_framework/services/metrics/standard_deviation.py +157 -0
  190. investing_algorithm_framework/services/metrics/trades.py +500 -0
  191. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  192. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  193. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  194. investing_algorithm_framework/services/metrics/volatility.py +97 -0
  195. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  196. investing_algorithm_framework/services/order_service/__init__.py +3 -1
  197. investing_algorithm_framework/services/order_service/order_backtest_service.py +76 -89
  198. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  199. investing_algorithm_framework/services/order_service/order_service.py +407 -326
  200. investing_algorithm_framework/services/portfolios/__init__.py +3 -1
  201. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +37 -3
  202. investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +22 -8
  203. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  204. investing_algorithm_framework/services/portfolios/portfolio_service.py +96 -28
  205. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +97 -28
  206. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +116 -313
  207. investing_algorithm_framework/services/positions/__init__.py +7 -0
  208. investing_algorithm_framework/services/positions/position_service.py +210 -0
  209. investing_algorithm_framework/services/repository_service.py +8 -2
  210. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  211. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -0
  212. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  213. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  214. investing_algorithm_framework/services/trade_service/__init__.py +7 -1
  215. investing_algorithm_framework/services/trade_service/trade_service.py +1013 -315
  216. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  217. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  218. investing_algorithm_framework-7.19.15.dist-info/METADATA +537 -0
  219. investing_algorithm_framework-7.19.15.dist-info/RECORD +263 -0
  220. investing_algorithm_framework-7.19.15.dist-info/entry_points.txt +3 -0
  221. investing_algorithm_framework/app/algorithm.py +0 -1105
  222. investing_algorithm_framework/domain/graphs.py +0 -382
  223. investing_algorithm_framework/domain/metrics/__init__.py +0 -6
  224. investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -11
  225. investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -43
  226. investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
  227. investing_algorithm_framework/domain/models/backtesting/backtest_report.py +0 -580
  228. investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -243
  229. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  230. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  231. investing_algorithm_framework/domain/services/market_data_sources.py +0 -344
  232. investing_algorithm_framework/domain/services/market_service.py +0 -153
  233. investing_algorithm_framework/domain/singleton.py +0 -9
  234. investing_algorithm_framework/domain/utils/backtesting.py +0 -472
  235. investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -12
  236. investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -559
  237. investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -254
  238. investing_algorithm_framework/infrastructure/models/market_data_sources/us_treasury_yield.py +0 -47
  239. investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
  240. investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -455
  241. investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
  242. investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
  243. investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -350
  244. investing_algorithm_framework/services/backtesting/backtest_report_writer_service.py +0 -53
  245. investing_algorithm_framework/services/backtesting/graphs.py +0 -61
  246. investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -8
  247. investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -150
  248. investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -189
  249. investing_algorithm_framework/services/position_service.py +0 -31
  250. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -264
  251. investing_algorithm_framework-3.7.0.dist-info/METADATA +0 -339
  252. investing_algorithm_framework-3.7.0.dist-info/RECORD +0 -147
  253. /investing_algorithm_framework/{domain → services}/metrics/price_efficiency.py +0 -0
  254. /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
  255. {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
  256. {investing_algorithm_framework-3.7.0.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
@@ -4,7 +4,14 @@ from investing_algorithm_framework.domain import MarketCredential
4
4
 
5
5
 
6
6
  class MarketCredentialService:
7
- _market_credentials = {}
7
+ """
8
+ Service to manage market credentials.
9
+
10
+ This service is responsible for adding, retrieving, and
11
+ initializing market credentials.
12
+ """
13
+ def __init__(self):
14
+ self._market_credentials = {}
8
15
 
9
16
  def add(self, market_data_credential: MarketCredential):
10
17
  self._market_credentials[market_data_credential.market.upper()] \
@@ -23,3 +30,11 @@ class MarketCredentialService:
23
30
 
24
31
  def get_all(self) -> List[MarketCredential]:
25
32
  return list(self._market_credentials.values())
33
+
34
+ def initialize(self):
35
+ """
36
+ Initialize all market credentials.
37
+ """
38
+
39
+ for market_credential in self.get_all():
40
+ market_credential.initialize()
@@ -0,0 +1,114 @@
1
+ from .volatility import get_annual_volatility
2
+ from .sortino_ratio import get_sortino_ratio
3
+ from .drawdown import get_drawdown_series, get_max_drawdown
4
+ from .equity_curve import get_equity_curve
5
+ from .price_efficiency import get_price_efficiency_ratio
6
+ from .profit_factor import get_profit_factor, \
7
+ get_cumulative_profit_factor_series, get_rolling_profit_factor_series
8
+ from .sharpe_ratio import get_sharpe_ratio, get_rolling_sharpe_ratio
9
+ from .price_efficiency import get_price_efficiency_ratio
10
+ from .equity_curve import get_equity_curve
11
+ from .drawdown import get_drawdown_series, get_max_drawdown, \
12
+ get_max_drawdown_absolute, get_max_drawdown_duration, \
13
+ get_max_daily_drawdown
14
+ from .cagr import get_cagr
15
+ from .standard_deviation import get_standard_deviation_downside_returns, \
16
+ get_standard_deviation_returns
17
+ from .returns import get_yearly_returns, get_monthly_returns, \
18
+ get_best_year, get_best_month, get_worst_month, get_total_return, \
19
+ get_average_yearly_return, get_average_monthly_return, \
20
+ get_percentage_winning_months, get_average_monthly_return_losing_months, \
21
+ get_average_monthly_return_winning_months, get_total_growth, \
22
+ get_percentage_winning_years, get_worst_year, get_cumulative_return, \
23
+ get_total_loss, get_cumulative_return_series
24
+ from .exposure import get_average_trade_duration, \
25
+ get_trade_frequency, get_trades_per_day, get_trades_per_year, \
26
+ get_cumulative_exposure, get_exposure_ratio
27
+ from .win_rate import get_win_rate, get_win_loss_ratio, get_current_win_rate, \
28
+ get_current_win_loss_ratio
29
+ from .calmar_ratio import get_calmar_ratio
30
+ from .generate import create_backtest_metrics, \
31
+ create_backtest_metrics_for_backtest
32
+ from .risk_free_rate import get_risk_free_rate_us
33
+ from .trades import get_negative_trades, get_positive_trades, \
34
+ get_number_of_trades, get_number_of_closed_trades, \
35
+ get_average_trade_size, get_average_trade_return, get_best_trade, \
36
+ get_worst_trade, get_average_trade_gain, get_median_trade_return, \
37
+ get_average_trade_loss, get_current_average_trade_loss, \
38
+ get_current_average_trade_duration, get_current_average_trade_gain, \
39
+ get_current_average_trade_return, get_number_of_open_trades, \
40
+ get_average_trade_duration
41
+
42
+ __all__ = [
43
+ "get_annual_volatility",
44
+ "get_sortino_ratio",
45
+ "get_drawdown_series",
46
+ "get_max_drawdown",
47
+ "get_equity_curve",
48
+ "get_price_efficiency_ratio",
49
+ "get_sharpe_ratio",
50
+ "get_profit_factor",
51
+ "get_cumulative_profit_factor_series",
52
+ "get_rolling_profit_factor_series",
53
+ "get_sharpe_ratio",
54
+ "get_cagr",
55
+ "get_standard_deviation_returns",
56
+ "get_standard_deviation_downside_returns",
57
+ "get_max_drawdown_absolute",
58
+ "get_total_return",
59
+ "get_total_loss",
60
+ "get_total_growth",
61
+ "get_cumulative_exposure",
62
+ "get_exposure_ratio",
63
+ "get_win_rate",
64
+ "get_win_loss_ratio",
65
+ "get_calmar_ratio",
66
+ "get_trade_frequency",
67
+ "get_yearly_returns",
68
+ "get_monthly_returns",
69
+ "get_best_year",
70
+ "get_best_month",
71
+ "get_worst_year",
72
+ "get_worst_month",
73
+ "get_best_trade",
74
+ "get_worst_trade",
75
+ "get_average_yearly_return",
76
+ "get_average_monthly_return",
77
+ "get_percentage_winning_months",
78
+ "get_average_trade_duration",
79
+ "get_trade_frequency",
80
+ "get_win_rate",
81
+ "get_win_loss_ratio",
82
+ "get_calmar_ratio",
83
+ "get_max_drawdown_absolute",
84
+ "get_max_drawdown_duration",
85
+ "get_max_daily_drawdown",
86
+ "get_trades_per_day",
87
+ "get_trades_per_year",
88
+ "get_average_monthly_return_losing_months",
89
+ "get_average_monthly_return_winning_months",
90
+ "get_percentage_winning_years",
91
+ "get_rolling_sharpe_ratio",
92
+ "create_backtest_metrics",
93
+ "get_risk_free_rate_us",
94
+ "get_median_trade_return",
95
+ "get_average_trade_gain",
96
+ "get_average_trade_loss",
97
+ "get_average_trade_size",
98
+ "get_average_trade_return",
99
+ "get_number_of_trades",
100
+ "get_number_of_closed_trades",
101
+ "get_negative_trades",
102
+ "get_positive_trades",
103
+ "get_cumulative_return",
104
+ "get_cumulative_return_series",
105
+ "get_current_win_rate",
106
+ "get_current_win_loss_ratio",
107
+ "get_current_average_trade_loss",
108
+ "get_current_average_trade_duration",
109
+ "get_current_average_trade_gain",
110
+ "get_current_average_trade_return",
111
+ "get_number_of_open_trades",
112
+ "get_average_trade_duration",
113
+ "create_backtest_metrics_for_backtest"
114
+ ]
File without changes
@@ -0,0 +1,60 @@
1
+ """
2
+ The Compound Annual Growth Rate (CAGR) normalizes returns over a one-year
3
+ period, allowing you to compare performance across different timeframes.
4
+ It assumes the investment grows at a steady rate and compounds over time.
5
+
6
+ This formula is suitable whether your data spans:
7
+
8
+ * Less than a year (e.g. 30 days)
9
+ * Exactly a year (365 days)
10
+ * More than a year (e.g. 500 days)
11
+ """
12
+
13
+ import pandas as pd
14
+ from typing import List
15
+ from investing_algorithm_framework.domain import PortfolioSnapshot
16
+
17
+
18
+ def get_cagr(snapshots: List[PortfolioSnapshot]) -> float:
19
+ """
20
+ Calculate the Compound Annual Growth Rate (CAGR) of a backtest report.
21
+ CAGR is a useful metric to evaluate the performance of an investment
22
+ over a period of time, normalizing the return to a one-year basis.
23
+
24
+ The formula for CAGR is:
25
+ CAGR = (End Value / Start Value) ^ (1 / Number of Years) - 1
26
+
27
+ Where:
28
+ - End Value is the final value of the investment
29
+ - Start Value is the initial value of the investment
30
+ - Number of Years is the total time period in years
31
+ This function assumes that the snapshots in the report are ordered by
32
+ creation date and that the net size represents the value of the investment.
33
+
34
+ Args:
35
+ snapshots (list[Snapshot]): A list of snapshots
36
+
37
+ Returns:
38
+ Float: The CAGR as a decimal. Returns 0.0 if not enough
39
+ data is available.
40
+ """
41
+
42
+ if len(snapshots) < 2:
43
+ return 0.0 # Not enough data
44
+
45
+ # Convert snapshots to DataFrame
46
+ data = [(s.total_value, s.created_at) for s in snapshots]
47
+ df = pd.DataFrame(data, columns=["total_value", "created_at"])
48
+ df['created_at'] = pd.to_datetime(df['created_at'])
49
+ df = df.sort_values('created_at')
50
+ start_value = df.iloc[0]['total_value']
51
+ end_value = df.iloc[-1]['total_value']
52
+ start_date = df.iloc[0]['created_at']
53
+ end_date = df.iloc[-1]['created_at']
54
+ num_days = (end_date - start_date).days
55
+
56
+ if num_days == 0 or start_value == 0:
57
+ return 0.0
58
+
59
+ # Apply CAGR formula
60
+ return (end_value / start_value) ** (365 / num_days) - 1
@@ -0,0 +1,40 @@
1
+ """
2
+ | **Calmar Ratio** | **Interpretation** |
3
+ | ---------------- | ----------------------------------------------------------- |
4
+ | **> 3.0** | **Excellent** – strong return vs. drawdown |
5
+ | **2.0 – 3.0** | **Very Good** – solid risk-adjusted performance |
6
+ | **1.0 – 2.0** | **Acceptable** – decent, especially for volatile strategies |
7
+ | **< 1.0** | **Poor** – high drawdowns relative to return |
8
+ """
9
+
10
+ from typing import List
11
+ from investing_algorithm_framework.domain import PortfolioSnapshot
12
+ from .cagr import get_cagr
13
+ from .drawdown import get_max_drawdown
14
+
15
+
16
+ def get_calmar_ratio(snapshots: List[PortfolioSnapshot]):
17
+ """
18
+ Calculate the Calmar Ratio, which is the ratio of the annualized
19
+ return to the maximum drawdown.
20
+
21
+ Formula:
22
+ Calmar Ratio = CAGR / |Maximum Drawdown|
23
+
24
+ The Calmar Ratio is a measure of risk-adjusted return,
25
+ where a higher ratio indicates a more favorable risk-return profile.
26
+
27
+ Args:
28
+ snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
29
+ from the backtest report.
30
+
31
+ Returns:
32
+ float: The Calmar Ratio.
33
+ """
34
+ cagr = get_cagr(snapshots)
35
+ max_drawdown = get_max_drawdown(snapshots)
36
+
37
+ if max_drawdown == 0 or max_drawdown is None:
38
+ return 0.0
39
+
40
+ return cagr / max_drawdown
@@ -0,0 +1,181 @@
1
+ """"
2
+ Max Drawdown (MDD) — a key risk metric that shows the worst
3
+ peak-to-trough decline of a portfolio:
4
+
5
+ | **Max Drawdown (%)** | **Interpretation** |
6
+ |-----------------------|----------------------------------------------------------------------|
7
+ | **0% to -5%** | 🟢 Excellent — Very low risk, typical for conservative strategies |
8
+ | **-5% to -10%** | ✅ Good — Moderate volatility, acceptable for balanced portfolios |
9
+ | **-10% to -20%** | ⚠️ Elevated Risk — Common in growth or actively managed strategies |
10
+ | **-20% to -40%** | 🔻 High Risk — Significant drawdown, typical of aggressive strategies |
11
+ | **> -40%** | 🚨 Very High Risk — Risk of capital loss or strategy failure |
12
+ """
13
+ from typing import List, Tuple
14
+ import pandas as pd
15
+ from datetime import datetime
16
+ from investing_algorithm_framework.domain import PortfolioSnapshot, Trade
17
+ from .equity_curve import get_equity_curve
18
+
19
+
20
+ def get_drawdown_series(snapshots: List[PortfolioSnapshot]) -> List[Tuple[float, datetime]]:
21
+ """
22
+ Calculate the drawdown series of a backtest report.
23
+
24
+ The drawdown is calculated as the percentage difference
25
+ between the current equity value and the maximum equity value
26
+ observed up to that point in time.
27
+
28
+ Args:
29
+ snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
30
+
31
+ Returns:
32
+ List[Tuple[datetime, float]]: A list of tuples with datetime
33
+ and drawdown percentage. The drawdown is expressed as a
34
+ negative percentage, where 0% means no drawdown and -100%
35
+ means the portfolio has lost all its value.
36
+ """
37
+ equity_curve = get_equity_curve(snapshots)
38
+
39
+ drawdown_series = []
40
+ max_value = None
41
+
42
+ for value, timestamp in equity_curve:
43
+ if max_value is None:
44
+ max_value = value
45
+ max_value = max(max_value, value)
46
+ drawdown = (value - max_value) / max_value # This will be <= 0
47
+ drawdown_series.append((drawdown, timestamp))
48
+
49
+ return drawdown_series
50
+
51
+
52
+ def get_max_drawdown(snapshots: List[PortfolioSnapshot]) -> float:
53
+ """
54
+ Calculate the maximum drawdown of the portfolio as a percentage from the peak.
55
+
56
+ Max Drawdown is the maximum observed loss from a peak to a
57
+ trough before a new peak is achieved.
58
+
59
+ It is expressed here as a negative percentage.
60
+
61
+ Args:
62
+ snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
63
+
64
+ Returns:
65
+ float: The maximum drawdown as a negative percentage (e.g., -12.5 for a 12.5% drawdown).
66
+ """
67
+ equity_curve = get_equity_curve(snapshots)
68
+
69
+ if not equity_curve:
70
+ return 0.0
71
+
72
+ peak = equity_curve[0][0]
73
+ max_drawdown_pct = 0.0
74
+
75
+ for equity, _ in equity_curve:
76
+ if equity > peak:
77
+ peak = equity
78
+
79
+ drawdown_pct = (equity - peak) / peak # Will be 0 or negative
80
+ max_drawdown_pct = min(max_drawdown_pct, drawdown_pct)
81
+
82
+ return abs(max_drawdown_pct)
83
+
84
+
85
+ def get_max_daily_drawdown(snapshots: List[PortfolioSnapshot]) -> float:
86
+ """
87
+ Calculate the maximum daily drawdown of the portfolio as a percentage from the peak.
88
+
89
+ This is the largest drop in equity (in percentage) from a peak to a trough
90
+ during the backtest period, calculated on a daily basis.
91
+
92
+ Args:
93
+ snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
94
+
95
+ Returns:
96
+ float: The maximum daily drawdown as a negative percentage (e.g., -5.0 for a 5% drawdown).
97
+ """
98
+ # Create DataFrame from snapshots
99
+ data = [(s.created_at, s.total_value) for s in snapshots]
100
+ df = pd.DataFrame(data, columns=["created_at", "total_value"])
101
+ df['created_at'] = pd.to_datetime(df['created_at'])
102
+ df = df.sort_values('created_at').drop_duplicates('created_at')\
103
+ .set_index('created_at')
104
+
105
+ # Resample to daily frequency using last value of the day
106
+ daily_df = df.resample('1D').last().dropna()
107
+
108
+ if daily_df.empty:
109
+ return 0.0
110
+
111
+ peak = daily_df['total_value'].iloc[0]
112
+ max_daily_drawdown_pct = 0.0
113
+ for equity in daily_df['total_value']:
114
+ if equity > peak:
115
+ peak = equity
116
+
117
+ drawdown_pct = (equity - peak) / peak
118
+ max_daily_drawdown_pct = min(max_daily_drawdown_pct, drawdown_pct)
119
+ return abs(max_daily_drawdown_pct) # Return as positive percentage (e.g., 5.0 for a 5% drawdown)
120
+
121
+ def get_max_drawdown_duration(snapshots: List[PortfolioSnapshot]) -> int:
122
+ """
123
+ Calculate the maximum duration of drawdown in days.
124
+
125
+ This is the longest period where the portfolio equity was below its peak.
126
+
127
+ Args:
128
+ snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
129
+
130
+ Returns:
131
+ int: The maximum drawdown duration in days.
132
+ """
133
+ equity_curve = get_equity_curve(snapshots)
134
+ if not equity_curve:
135
+ return 0
136
+
137
+ peak = equity_curve[0][0]
138
+ max_duration = 0
139
+ current_duration = 0
140
+
141
+ for equity, _ in equity_curve:
142
+ if equity < peak:
143
+ current_duration += 1
144
+ else:
145
+ max_duration = max(max_duration, current_duration)
146
+ current_duration = 0
147
+ peak = equity # Reset peak to current equity
148
+
149
+ max_duration = max(max_duration, current_duration) # Final check
150
+
151
+ return max_duration
152
+
153
+
154
+ def get_max_drawdown_absolute(snapshots: List[PortfolioSnapshot]) -> float:
155
+ """
156
+ Calculate the maximum absolute drawdown of the portfolio.
157
+
158
+ This is the largest drop in equity (in currency units) from a peak to a trough
159
+ during the backtest period.
160
+
161
+ Args:
162
+ snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
163
+
164
+ Returns:
165
+ float: The maximum absolute drawdown as a positive number (e.g., €10,000).
166
+ """
167
+ equity_curve = get_equity_curve(snapshots)
168
+ if not equity_curve:
169
+ return 0.0
170
+
171
+ peak = equity_curve[0][0]
172
+ max_drawdown = 0.0
173
+
174
+ for equity, _ in equity_curve:
175
+ if equity > peak:
176
+ peak = equity
177
+
178
+ drawdown = peak - equity # Drop from peak
179
+ max_drawdown = max(max_drawdown, drawdown)
180
+
181
+ return abs(max_drawdown) # Return as positive number (e.g., €10,000)
@@ -0,0 +1,24 @@
1
+ from datetime import datetime
2
+ from typing import List
3
+ from investing_algorithm_framework.domain import PortfolioSnapshot
4
+
5
+
6
+ def get_equity_curve(
7
+ snapshots: List[PortfolioSnapshot]
8
+ ) -> list[tuple[float, datetime]]:
9
+ """
10
+ Calculate the total size of the portfolio at each snapshot timestamp.
11
+
12
+ Args:
13
+ snapshots (List[PortfolioSnapshot]): List of portfolio snapshots.
14
+ Returns:
15
+ list[tuple[datetime, float]]: A list of tuples with
16
+ timestamps and total sizes.
17
+ """
18
+ series = []
19
+ for snapshot in snapshots:
20
+ timestamp = snapshot.created_at
21
+ total_size = snapshot.total_value
22
+ series.append((total_size, timestamp))
23
+
24
+ return series
@@ -0,0 +1,210 @@
1
+ """
2
+ High exposure (>1) means you’re deploying capital aggressively, possibly with many simultaneous positions.
3
+
4
+ Exposure around 1 means capital is nearly fully invested most of the time, but not overlapping.
5
+
6
+ Low exposure (<1) means capital is mostly idle or only partially invested.
7
+ """
8
+
9
+ from datetime import datetime, timedelta
10
+ from typing import List
11
+
12
+ from investing_algorithm_framework.domain import Trade
13
+
14
+
15
+ def get_exposure_ratio(
16
+ trades: List["Trade"], start_date: datetime, end_date: datetime
17
+ ) -> float:
18
+ """
19
+ Calculates the exposure ratio (time in market) as the fraction of the total
20
+ backtest duration where at least one position was open.
21
+
22
+ Unlike cumulative exposure, overlapping trades are not double-counted.
23
+ The result is always between 0 and 1.
24
+
25
+ Args:
26
+ trades (List[Trade]): List of trades executed during the backtest.
27
+ start_date (datetime): The start date of the backtest.
28
+ end_date (datetime): The end date of the backtest.
29
+
30
+ Returns:
31
+ A float between 0 and 1 representing the exposure ratio.
32
+ """
33
+ if not trades:
34
+ return 0.0
35
+
36
+ # Collect trade intervals
37
+ intervals = []
38
+ for trade in trades:
39
+ entry = max(trade.opened_at, start_date)
40
+ exit = min(trade.closed_at or end_date, end_date)
41
+ if exit > entry:
42
+ intervals.append((entry, exit))
43
+
44
+ if not intervals:
45
+ return 0.0
46
+
47
+ # Sort intervals by start time
48
+ intervals.sort(key=lambda x: x[0])
49
+
50
+ # Merge overlapping intervals
51
+ merged = []
52
+ current_start, current_end = intervals[0]
53
+ for start, end in intervals[1:]:
54
+ if start <= current_end: # overlap
55
+ current_end = max(current_end, end)
56
+ else:
57
+ merged.append((current_start, current_end))
58
+ current_start, current_end = start, end
59
+ merged.append((current_start, current_end))
60
+
61
+ # Total time with at least one open trade
62
+ total_exposed_time = sum((end - start for start, end in merged), timedelta(0))
63
+
64
+ backtest_duration = end_date - start_date
65
+ if backtest_duration.total_seconds() == 0:
66
+ return 0.0
67
+
68
+ return total_exposed_time.total_seconds() \
69
+ / backtest_duration.total_seconds()
70
+
71
+
72
+ def get_cumulative_exposure(
73
+ trades: List[Trade], start_date: datetime, end_date: datetime
74
+ ) -> float:
75
+ """
76
+ Calculates the exposure time as a fraction of the total backtest duration
77
+ that the strategy had capital deployed (i.e., at least one open position).
78
+
79
+ This value can be greater than 1 if the strategy had overlapping trades.
80
+ For example, if the strategy had two trades open at the same time,
81
+ the exposure factor would be 2.0, indicating that capital was deployed
82
+ for twice the duration of the backtest period.
83
+
84
+ Args:
85
+ trades (List[Trade]): List of trades executed during the backtest.
86
+ start_date (datetime): The start date.
87
+ end_date (datetime): The end date.
88
+
89
+ Returns:
90
+ A float representing the exposure factor, which is the fraction of time
91
+ the strategy had capital deployed during the backtest period.
92
+
93
+ """
94
+ if not trades:
95
+ return 0.0
96
+
97
+ total_trade_duration = timedelta(0)
98
+ for trade in trades:
99
+ entry = trade.opened_at
100
+ exit = trade.closed_at or end_date # open trades counted up to end
101
+
102
+ if exit > entry:
103
+ total_trade_duration += exit - entry
104
+
105
+ backtest_duration = end_date - start_date
106
+
107
+ if backtest_duration.total_seconds() == 0:
108
+ return 0.0
109
+
110
+ return (total_trade_duration.total_seconds()
111
+ / backtest_duration.total_seconds())
112
+
113
+
114
+ def get_average_trade_duration(trades: List[Trade]):
115
+ """
116
+ Calculates the average duration of trades in the backtest report.
117
+
118
+ Args:
119
+ trades (List[Trade]): List of trades executed during the backtest.
120
+
121
+ Returns:
122
+ A float representing the average trade duration in hours.
123
+ """
124
+ if not trades:
125
+ return 0.0
126
+
127
+ total_duration = 0
128
+
129
+ for trade in trades:
130
+ trade_duration = trade.duration
131
+
132
+ if trade_duration is not None:
133
+ total_duration += trade_duration
134
+
135
+ average_trade_duration = total_duration / len(trades)
136
+ return average_trade_duration
137
+
138
+
139
+ def get_trade_frequency(
140
+ trades: List[Trade], start_date: datetime, end_date: datetime
141
+ ) -> float:
142
+ """
143
+ Calculates the trade frequency as the number of trades per day
144
+ during the backtest period.
145
+
146
+ Args:
147
+ trades (List[Trade]): List of trades executed during the backtest.
148
+ start_date (datetime): The start date of the backtest.
149
+ end_date (datetime): The end date of the backtest.
150
+
151
+ Returns:
152
+ A float representing the average number of trades per day.
153
+ """
154
+
155
+ if not trades:
156
+ return 0.0
157
+
158
+ total_days = (end_date - start_date).days + 1
159
+ if total_days <= 0:
160
+ return 0.0
161
+
162
+ return len(trades) / total_days
163
+
164
+
165
+ def get_trades_per_day(
166
+ trades: List[Trade], start_date: datetime, end_date: datetime
167
+ ) -> float:
168
+ """
169
+ Calculates the average number of trades per day during the backtest period.
170
+
171
+ Args:
172
+ trades (List[Trade]): List of trades executed during the backtest.
173
+ start_date (datetime): The start date of the backtest.
174
+ end_date (datetime): The end date of the backtest.
175
+
176
+ Returns:
177
+ A float representing the average number of trades per day.
178
+ """
179
+ if not trades:
180
+ return 0.0
181
+
182
+ total_days = (end_date - start_date).days + 1
183
+ if total_days <= 0:
184
+ return 0.0
185
+
186
+ return len(trades) / total_days
187
+
188
+
189
+ def get_trades_per_year(
190
+ trades: List[Trade], start_date: datetime, end_date: datetime
191
+ ) -> float:
192
+ """
193
+ Calculates the average number of trades per year during the backtest period.
194
+
195
+ Args:
196
+ trades (List[Trade]): List of trades executed during the backtest.
197
+ start_date (datetime): The start date of the backtest.
198
+ end_date (datetime): The end date of the backtest.
199
+
200
+ Returns:
201
+ A float representing the average number of trades per year.
202
+ """
203
+ if not trades:
204
+ return 0.0
205
+
206
+ total_years = (end_date - start_date).days / 365.25
207
+ if total_years <= 0:
208
+ return 0.0
209
+
210
+ return len(trades) / total_years