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
@@ -1,243 +0,0 @@
1
- from typing import List, Dict
2
-
3
- from investing_algorithm_framework.domain.exceptions import \
4
- OperationalException
5
- from investing_algorithm_framework.domain.models.backtesting\
6
- .backtest_date_range import BacktestDateRange
7
- from .backtest_report import BacktestReport
8
-
9
-
10
- class BacktestReportsEvaluation:
11
- """
12
- A class to evaluate backtest reports.
13
-
14
- This class is used to evaluate backtest reports and order them based on
15
- different metrics. It also groups the reports by backtest date range.
16
-
17
- The class has methods to get the best algorithm based on profit and growth.
18
- """
19
- report_groupings: Dict[BacktestDateRange, List[BacktestReport]]
20
-
21
- def __init__(self, backtest_reports: List[BacktestReport]):
22
-
23
- if backtest_reports is None:
24
- raise OperationalException("No backtest reports to evaluate")
25
-
26
- self.backtest_reports = backtest_reports
27
- self.group_reports(self.backtest_reports)
28
- self.profit_order = {}
29
- self.profit_order_grouped_by_date = {}
30
- self.growth_order = {}
31
- self.growth_order_grouped_by_date = {}
32
- self.percentage_positive_trades_order = {}
33
- self.percentage_positive_trades_order_grouped_by_date = {}
34
-
35
- # Calculate all metrics and group reports
36
- for key in self.report_groupings:
37
- self.profit_order_grouped_by_date[key] = self.order_on_profit(
38
- self.report_groupings[key].copy()
39
- )
40
- self.growth_order_grouped_by_date[key] = self.order_on_growth(
41
- self.report_groupings[key].copy()
42
- )
43
- self.percentage_positive_trades_order_grouped_by_date[key] = \
44
- self.order_on_positive_trades(
45
- self.report_groupings[key].copy()
46
- )
47
-
48
- # Overall ordered reports
49
- self.percentage_positive_trades_order = \
50
- self.order_on_positive_trades(self.backtest_reports)
51
- self.growth_order = self.order_on_growth(self.backtest_reports)
52
- self.profit_order = self.order_on_profit(self.backtest_reports)
53
-
54
- def order_on_profit(self, reports: List[BacktestReport]):
55
- return sorted(reports, key=lambda x: x.total_net_gain, reverse=True)
56
-
57
- def order_on_growth(self, reports: List[BacktestReport]):
58
- return sorted(reports, key=lambda x: x.growth_rate, reverse=True)
59
-
60
- def order_on_positive_trades(self, reports: List[BacktestReport]):
61
- return sorted(
62
- reports, key=lambda x: x.percentage_positive_trades, reverse=True
63
- )
64
-
65
- def group_reports(self, reports: List[BacktestReport]):
66
- """
67
- Group reports by backtest start and end date.
68
- """
69
- self.report_groupings = {}
70
-
71
- for report in reports:
72
- key = report.backtest_date_range
73
- if key not in self.report_groupings:
74
- self.report_groupings[key] = []
75
- self.report_groupings[key].append(report)
76
-
77
- def get_date_ranges(self):
78
- """
79
- Get the date ranges of the backtest reports.
80
- """
81
- return list(self.report_groupings.keys())
82
-
83
- def get_profit_order(
84
- self, backtest_date_range: BacktestDateRange = None
85
- ) -> List[BacktestReport]:
86
- """
87
- Function to get profit order of all the backtest reports
88
- in an ordered list.
89
-
90
- :param backtest_date_range: Tuple with two datetime objects
91
- :return: List of backtest reports
92
- """
93
-
94
- if backtest_date_range is None:
95
- return self.profit_order
96
- else:
97
-
98
- if backtest_date_range in self.profit_order_grouped_by_date:
99
- return self.profit_order_grouped_by_date[backtest_date_range]
100
-
101
- raise OperationalException("No matches for given date range")
102
-
103
- def get_growth_order(
104
- self, backtest_date_range: BacktestDateRange = None
105
- ) -> List[BacktestReport]:
106
- """
107
- Function to get growth order of all the backtest reports
108
- in an ordered list.
109
-
110
- :param backtest_date_range: Tuple with two datetime objects
111
- :return: List of backtest reports
112
- """
113
-
114
- if backtest_date_range is None:
115
- return self.growth_order
116
- else:
117
-
118
- if backtest_date_range in self.growth_order_grouped_by_date:
119
- return self.growth_order_grouped_by_date[backtest_date_range]
120
-
121
- raise OperationalException("No matches for given date range")
122
-
123
- def get_percentage_positive_trades_order(
124
- self, backtest_date_range: BacktestDateRange = None
125
- ) -> List[BacktestReport]:
126
- """
127
- Function to get growth order of all the backtest reports
128
- in an ordered list.
129
-
130
- :param backtest_date_range: Tuple with two datetime objects
131
- :return: List of backtest reports
132
- """
133
-
134
- if backtest_date_range is None:
135
- return self.percentage_positive_trades_order
136
- else:
137
-
138
- if backtest_date_range in \
139
- self.percentage_positive_trades_order_grouped_by_date:
140
- return self.percentage_positive_trades_order_grouped_by_date[
141
- backtest_date_range
142
- ]
143
-
144
- raise OperationalException("No matches for given date range")
145
-
146
- def rank(
147
- self,
148
- weight_profit=0.7,
149
- weight_growth=0.3,
150
- backtest_date_range: BacktestDateRange = None
151
- ) -> str:
152
- """
153
- Function to get the best overall algorithm based on the
154
- weighted sum of profit and growth.
155
-
156
- :param weight_profit: Weight for profit
157
- :param weight_growth: Weight for growth
158
- :return: Name of the best algorithm
159
- """
160
-
161
- # Create a dictionary with the algorithm name as key and group
162
- # the reports by name
163
- ordered_reports = {}
164
-
165
- if backtest_date_range is not None:
166
- reports = self.report_groupings[backtest_date_range]
167
- else:
168
- reports = self.backtest_reports
169
-
170
- for report in reports:
171
- if report.name not in ordered_reports:
172
- ordered_reports[report.name] = []
173
- ordered_reports[report.name].append(report)
174
-
175
- best_algorithm = None
176
- best_score = 0
177
- profit_score = 0
178
- growth_score = 0
179
-
180
- for algorithm in ordered_reports:
181
- profit_score += sum(
182
- [report.total_net_gain for
183
- report in ordered_reports[algorithm]]
184
- )
185
- growth_score += sum(
186
- [report.growth for report in
187
- ordered_reports[algorithm]]
188
- )
189
- score = weight_profit * profit_score + weight_growth * growth_score
190
-
191
- if score > best_score:
192
- best_score = score
193
- best_algorithm = algorithm
194
-
195
- profit_score = 0
196
- growth_score = 0
197
-
198
- return best_algorithm
199
-
200
- def get_reports(
201
- self, name: str = None, backtest_date_range: BacktestDateRange = None
202
- ) -> List[BacktestReport]:
203
- """
204
- Function to get all the reports for a given algorithm name.
205
-
206
- :param name: Name of the algorithm
207
- :param backtest_date_range: Tuple with two datetime objects
208
- :return: List of backtest reports
209
- """
210
-
211
- if name is not None and backtest_date_range is not None:
212
- return [
213
- report for report in self.report_groupings[backtest_date_range]
214
- if report.name == name
215
- ]
216
-
217
- if name is not None:
218
- return [
219
- report for report in self.backtest_reports
220
- if report.name == name
221
- ]
222
-
223
- if backtest_date_range is not None:
224
- return self.report_groupings[backtest_date_range]
225
-
226
- def get_report(
227
- self, name: str, backtest_date_range: BacktestDateRange = None
228
- ):
229
- """
230
- Function to get the report for a given algorithm name and date range.
231
-
232
- :param name: Name of the algorithm
233
- :param backtest_date_range: Tuple with two datetime objects
234
- :return: Backtest report
235
- """
236
- reports = self.get_reports(name, backtest_date_range)
237
-
238
- if len(reports) == 0:
239
- raise OperationalException(
240
- "No matches for given name and date range"
241
- )
242
-
243
- return reports[0]
@@ -1,8 +0,0 @@
1
- import yfinance as yf
2
-
3
-
4
- def get_risk_free_rate_us():
5
- ten_year = yf.Ticker("^TNX")
6
- hist = ten_year.history(period="5d")
7
- latest_yield = hist["Close"].iloc[-1] / 100
8
- return latest_yield
@@ -1,69 +0,0 @@
1
- """
2
- Volatility is a statistical measure of the dispersion of returns for a
3
- given portfolio. In finance, it is commonly used as a proxy for risk.
4
- This function calculates the standard deviation of daily log returns and
5
- annualizes it, giving an estimate of how much the portfolio's value
6
- fluctuates on a yearly basis.
7
-
8
- | **Annual Volatility** | **Risk Level** | **Typical for...** | **Comments** |
9
- | --------------------- | -------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
10
- | **< 5%** | Very Low Risk | Cash equivalents, short-term bonds | Low return expectations; often used for capital preservation |
11
- | **5% – 10%** | Low Risk | Diversified bond portfolios, conservative allocation strategies | Suitable for risk-averse investors |
12
- | **10% – 15%** | Moderate Risk | Balanced portfolios, large-cap equity indexes (e.g., S\&P 500 ≈ \~15%) | Standard for traditional diversified portfolios |
13
- | **15% – 25%** | High Risk | Growth stocks, hedge funds, active equity strategies | Higher return potential, but more drawdowns |
14
- | **> 25%** | Very High Risk | Crypto, leveraged ETFs, speculative strategies | High potential returns, but prone to large losses; often not suitable long-term |
15
-
16
- """
17
-
18
- from typing import List
19
-
20
- import pandas as pd
21
- import numpy as np
22
-
23
- from investing_algorithm_framework.domain import PortfolioSnapshot
24
-
25
-
26
- def get_annual_volatility(snapshots: List[PortfolioSnapshot]) -> float:
27
- """
28
- Calculate the annualized volatility of portfolio net values.
29
-
30
- Args:
31
- snapshots (List[PortfolioSnapshot]): List of portfolio snapshots
32
- from the backtest report.
33
-
34
- Returns:
35
- Float: Annualized volatility as a float
36
- """
37
-
38
- if len(snapshots) < 2:
39
- return 0.0 # Not enough data to calculate volatility
40
-
41
- # Build DataFrame from snapshots
42
- records = [
43
- (snapshot.total_value, snapshot.created_at) for snapshot in snapshots
44
- ]
45
- df = pd.DataFrame(records, columns=['total_value', 'created_at'])
46
- df['created_at'] = pd.to_datetime(df['created_at'])
47
- df = df.sort_values('created_at').drop_duplicates('created_at').copy()
48
-
49
- # Calculate log returns
50
- df['log_return'] = np.log(df['total_value'] / df['total_value'].shift(1))
51
- df = df.dropna()
52
-
53
- if df.empty:
54
- return 0.0
55
-
56
- daily_volatility = df['log_return'].std()
57
-
58
- start_date = snapshots[0].created_at
59
- end_date = snapshots[-1].created_at
60
- # Estimate trading days per year based on snapshot frequency
61
- total_days = (end_date - start_date).days
62
- num_observations = len(df)
63
-
64
- if total_days > 0:
65
- trading_days_per_year = (num_observations / total_days) * 365
66
- else:
67
- trading_days_per_year = 365 # Default fallback
68
-
69
- return daily_volatility * np.sqrt(trading_days_per_year)
@@ -1,3 +0,0 @@
1
- investing-algorithm-framework>=6.8.2
2
- azure-functions==1.17.0
3
- pyindicators>=0.5.4
@@ -1,9 +0,0 @@
1
- from .backtest_position import BacktestPosition
2
- from .backtest_results import BacktestResult
3
- from .backtest_date_range import BacktestDateRange
4
-
5
- __all__ = [
6
- "BacktestResult",
7
- "BacktestPosition",
8
- "BacktestDateRange"
9
- ]
@@ -1,47 +0,0 @@
1
- from datetime import datetime
2
- from dateutil.parser import parse
3
-
4
-
5
- class BacktestDateRange:
6
- """
7
- Represents a date range for a backtest
8
- """
9
- def __init__(self, start_date, end_date=None, name=None):
10
-
11
- if isinstance(start_date, str):
12
- start_date = parse(start_date)
13
-
14
- if end_date is not None and isinstance(end_date, str):
15
- end_date = parse(end_date)
16
-
17
- self._start_date = start_date
18
- self._end_date = end_date
19
- self._name = name
20
-
21
- if end_date is None:
22
- self._end_date = datetime.now()
23
-
24
- if end_date < start_date:
25
- raise ValueError(
26
- "End date cannot be before start date for a backtest "
27
- "date range. " +
28
- f"(start_date: {start_date}, end_date: {end_date})"
29
- )
30
-
31
- @property
32
- def start_date(self):
33
- return self._start_date
34
-
35
- @property
36
- def end_date(self):
37
- return self._end_date
38
-
39
- @property
40
- def name(self):
41
- return self._name
42
-
43
- def __repr__(self):
44
- return f"{self.name}: {self._start_date} - {self._end_date}"
45
-
46
- def __str__(self):
47
- return self.__repr__()
@@ -1,120 +0,0 @@
1
- from investing_algorithm_framework.domain.models.base_model import BaseModel
2
-
3
-
4
- class BacktestPosition(BaseModel):
5
-
6
- def __init__(
7
- self,
8
- position,
9
- trading_symbol=False,
10
- amount_pending_buy=0.0,
11
- amount_pending_sell=0.0,
12
- total_value_portfolio=0.0
13
- ):
14
- self._symbol = position.symbol
15
- self._amount = position.amount + amount_pending_sell
16
- self._cost = position.cost
17
- self._price = 0.0
18
- self._trading_symbol = trading_symbol
19
- self._amount_pending_buy = amount_pending_buy
20
- self._amount_pending_sell = amount_pending_sell
21
- self._total_value_portfolio = total_value_portfolio
22
-
23
- @property
24
- def price(self):
25
- return self._price
26
-
27
- @price.setter
28
- def price(self, value):
29
- self._price = value
30
-
31
- @property
32
- def cost(self):
33
-
34
- if self._trading_symbol:
35
- return self.amount
36
-
37
- return self._cost
38
-
39
- @property
40
- def value(self):
41
-
42
- if self._trading_symbol:
43
- return self.amount
44
-
45
- return self._price * self.amount
46
-
47
- @property
48
- def growth(self):
49
-
50
- if self._amount == 0:
51
- return 0.0
52
-
53
- if self._trading_symbol:
54
- return 0.0
55
-
56
- return self.value - self.cost
57
-
58
- @property
59
- def growth_rate(self):
60
-
61
- if self._trading_symbol:
62
- return 0.0
63
-
64
- if self.cost == 0:
65
- return 0.0
66
-
67
- return self.growth / self.cost * 100
68
-
69
- @property
70
- def symbol(self):
71
- return self._symbol
72
-
73
- @property
74
- def amount(self):
75
- return self._amount
76
-
77
- @property
78
- def amount_pending_sell(self):
79
- return self._amount_pending_sell
80
-
81
- @amount_pending_sell.setter
82
- def amount_pending_sell(self, value):
83
- self._amount_pending_sell = value
84
-
85
- @property
86
- def amount_pending_buy(self):
87
- return self._amount_pending_buy
88
-
89
- @amount_pending_buy.setter
90
- def amount_pending_buy(self, value):
91
- self._amount_pending_buy = value
92
-
93
- @property
94
- def percentage_of_portfolio(self):
95
-
96
- if self._total_value_portfolio == 0:
97
- return 0.0
98
-
99
- return (self.value / self._total_value_portfolio) * 100
100
-
101
- def get_percentage_of_portfolio(self):
102
-
103
- if self._total_value_portfolio == 0:
104
- return 0.0
105
-
106
- return self.value / self._total_value_portfolio * 100
107
-
108
- def to_dict(self):
109
- return {
110
- "symbol": self.symbol,
111
- "amount": self.amount,
112
- "cost": self.cost,
113
- "price": self.price,
114
- "value": self.value,
115
- "growth": self.growth,
116
- "growth_rate": self.growth_rate,
117
- "amount_pending_buy": self.amount_pending_buy,
118
- "amount_pending_sell": self.amount_pending_sell,
119
- "percentage_of_portfolio": self.percentage_of_portfolio
120
- }