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
@@ -1,350 +0,0 @@
1
- import logging
2
-
3
- from investing_algorithm_framework.domain import OrderStatus, OrderSide
4
-
5
- logger = logging.getLogger(__name__)
6
-
7
-
8
- class PerformanceService:
9
- """
10
- Service to calculate the performance of a portfolio.
11
- """
12
-
13
- def __init__(
14
- self,
15
- order_repository,
16
- position_repository,
17
- portfolio_repository,
18
- ):
19
- self.order_repository = order_repository
20
- self.position_repository = position_repository
21
- self.portfolio_repository = portfolio_repository
22
-
23
- def get_total_net_gain(self, portfolio_id):
24
- pass
25
-
26
- def get_total_size(self, portfolio_id):
27
- pass
28
-
29
- def get_percentage_change(self, portfolio_id, time_frame):
30
- pass
31
-
32
- def get_total_cost(self, portfolio_id):
33
- pass
34
-
35
- def get_number_of_trades_closed(self, portfolio_id):
36
- """"
37
- Get the number of trades closed. This function will
38
- return the number of trades that are already closed.
39
-
40
- param portfolio_id: The id of the portfolio
41
- type portfolio_id: str
42
-
43
- return: The number of trades closed
44
- """
45
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
46
- number_of_trades_closed = 0
47
- orders = self.order_repository.get_all(
48
- {
49
- "portfolio_id": portfolio.id,
50
- "order_side": OrderSide.BUY.value,
51
- }
52
- )
53
-
54
- for order in orders:
55
- if order.get_trade_closed_at() is not None:
56
- number_of_trades_closed += 1
57
-
58
- return number_of_trades_closed
59
-
60
- def get_number_of_trades_open(self, portfolio_id):
61
- """
62
- Get the number of trades open. This function will
63
- return the number of trades that are still open.
64
-
65
- param portfolio_id: The id of the portfolio
66
- type portfolio_id: str
67
-
68
- return: The number of trades open
69
- """
70
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
71
- number_of_trades_open = 0
72
- orders = self.order_repository.get_all(
73
- {
74
- "portfolio_id": portfolio.id,
75
- "order_side": OrderSide.BUY.value,
76
- }
77
- )
78
-
79
- for order in orders:
80
- if order.get_trade_closed_at() is None:
81
- number_of_trades_open += 1
82
-
83
- return number_of_trades_open
84
-
85
- def get_percentage_positive_trades(self, portfolio_id):
86
- """
87
- Get the percentage of positive trades. This function will
88
- calculate the percentage of positive trades by dividing the
89
- total number of positive trades by the total number of trades
90
- and then multiplying it by 100.
91
-
92
- param portfolio_id: The id of the portfolio
93
- type portfolio_id: str
94
-
95
- return: The percentage of positive trades
96
- """
97
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
98
- orders = self.order_repository.get_all(
99
- {"portfolio_id": portfolio.id, "status": OrderStatus.CLOSED.value}
100
- )
101
- total_number_of_orders = len(orders)
102
-
103
- if total_number_of_orders == 0:
104
- return 0.0
105
-
106
- positive_orders = [
107
- order for order in orders if order.get_net_gain() > 0
108
- ]
109
- total_number_of_positive_orders = len(positive_orders)
110
- return total_number_of_positive_orders / total_number_of_orders * 100
111
-
112
- def get_percentage_negative_trades(self, portfolio_id):
113
- """
114
- Get the percentage of negative trades. This function will
115
- calculate the percentage of negative trades by dividing the
116
- total number of negative trades by the total number of trades
117
- and then multiplying it by 100.
118
-
119
- param portfolio_id: The id of the portfolio
120
- type portfolio_id: str
121
-
122
- return: The percentage of negative trades
123
- """
124
-
125
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
126
- orders = self.order_repository.get_all(
127
- {"portfolio_id": portfolio.id, "status": OrderStatus.CLOSED.value}
128
- )
129
- total_number_of_orders = len(orders)
130
-
131
- if total_number_of_orders == 0:
132
- return 0.0
133
-
134
- negative_orders = [
135
- order for order in orders if order.get_net_gain() < 0
136
- ]
137
- total_number_of_negative_orders = len(negative_orders)
138
- return total_number_of_negative_orders / total_number_of_orders * 100
139
-
140
- def get_growth_rate_of_backtest(
141
- self, portfolio_id, tickers, backtest_profile
142
- ):
143
- """
144
- Get the growth rate of the backtest. This function will
145
- calculate the total value of the portfolio and then
146
- calculate the growth rate of the portfolio.
147
-
148
- param portfolio_id: The id of the portfolio
149
- type portfolio_id: str
150
- param tickers: list of tickers of all the used symbols
151
- type tickers: dict
152
-
153
- return: The growth rate of the backtest
154
- """
155
- total_value = self.get_total_value(
156
- portfolio_id, tickers, backtest_profile
157
- )
158
- gain = total_value - backtest_profile.initial_unallocated
159
- return gain / backtest_profile.initial_unallocated * 100
160
-
161
- def get_growth_of_backtest(self, portfolio_id, tickers, backtest_profile):
162
- """
163
- Get the growth of the backtest. This function will
164
- calculate the total value of the portfolio and then
165
- calculate the growth of the portfolio.
166
-
167
- param portfolio_id: The id of the portfolio
168
- type portfolio_id: str
169
- param tickers: The tickers of the market
170
- type tickers: dict
171
-
172
- return: The growth of the backtest
173
- """
174
- total_value = self.get_total_value(
175
- portfolio_id, tickers, backtest_profile
176
- )
177
- return total_value - backtest_profile.initial_unallocated
178
-
179
- def get_total_net_gain_percentage_of_backtest(
180
- self, portfolio_id, backtest_profile
181
- ):
182
- """
183
- Get the total net gain percentage of the backtest. This function
184
- will calculate the total net gain percentage of the portfolio
185
- by dividing the total net gain by the initial unallocated value
186
- of the portfolio and then multiplying it by 100.
187
-
188
- param portfolio_id: The id of the portfolio
189
- type portfolio_id: str
190
- param backtest_profile: The backtest profile
191
- type backtest_profile: BacktestProfile
192
-
193
- return: The total net gain percentage of the backtest
194
- """
195
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
196
- return portfolio.total_net_gain \
197
- / backtest_profile.initial_unallocated * 100
198
-
199
- def get_total_value(self, portfolio_id, tickers, backtest_profile):
200
- """
201
- Get the total value of the portfolio. This functions
202
- will calculate the allocated value, pending buy value,
203
- pending sell value and unallocated value.
204
-
205
- At the end, it will sum all these values and return the
206
- total value of the portfolio.
207
-
208
- param portfolio_id: The id of the portfolio
209
- type portfolio_id: str
210
- param tickers: The tickers of the market
211
- type tickers: dict
212
- param backtest_profile: The backtest profile
213
- type backtest_profile: BacktestProfile
214
-
215
- return: The total value of the portfolio
216
- rtype: float
217
- """
218
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
219
- positions = self.position_repository.get_all(
220
- {"portfolio_id": portfolio.id}
221
- )
222
- allocated = 0
223
-
224
- for position in positions:
225
-
226
- if position.symbol == portfolio.trading_symbol:
227
- continue
228
-
229
- ticker_symbol = f"{position.symbol.upper()}" \
230
- f"/{portfolio.trading_symbol.upper()}"
231
- if ticker_symbol not in tickers:
232
- logger.warning(
233
- f"Symbol {position.symbol} not found in tickers, "
234
- f"cannot calculate the total value of the position"
235
- )
236
- continue
237
-
238
- allocated += position.amount * tickers[ticker_symbol]["bid"]
239
-
240
- # Calculate the pending sell value
241
- pending_sell_orders = self.order_repository.get_all(
242
- {
243
- "portfolio_id": portfolio.id,
244
- "status": OrderStatus.OPEN.value,
245
- "order_side": OrderSide.SELL.value,
246
- }
247
- )
248
-
249
- for order in pending_sell_orders:
250
-
251
- if order.get_symbol() in tickers:
252
- allocated += order.get_amount() \
253
- * tickers[order.get_symbol()]["bid"]
254
- else:
255
- logger.warning(
256
- f"Symbol {order.get_symbol()} not found in tickers, "
257
- f"cannot calculate the total value of sell orders"
258
- )
259
-
260
- # Calculate the unallocated value by summing the unallocated and
261
- # pending buy value
262
- unallocated = portfolio.unallocated
263
- pending_buy_orders = self.order_repository.get_all(
264
- {
265
- "portfolio_id": portfolio.id,
266
- "status": OrderStatus.OPEN.value,
267
- "order_side": OrderSide.BUY.value,
268
- }
269
- )
270
-
271
- for order in pending_buy_orders:
272
- if order.get_symbol() in tickers:
273
- unallocated += order.get_amount() \
274
- * tickers[order.get_symbol()]["ask"]
275
- else:
276
- logger.warning(
277
- f"Symbol {order.get_symbol()} not found in tickers, "
278
- f"cannot calculate the total value of buy orders"
279
- )
280
-
281
- # Add everything together
282
- return allocated + unallocated
283
-
284
- def get_average_trade_duration(self, portfolio_id):
285
- """
286
- Get the average trade duration. This function will
287
- calculate the average trade duration by summing the
288
- duration of all the trades and then dividing it by the
289
- total number of trades.
290
-
291
- param portfolio_id: The id of the portfolio
292
- type portfolio_id: str
293
-
294
- return: The average trade duration
295
- """
296
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
297
- buy_orders = self.order_repository.get_all(
298
- {
299
- "portfolio_id": portfolio.id,
300
- "order_side": OrderSide.BUY.value,
301
- }
302
- )
303
- buy_orders_with_trade_closed = [
304
- order for order in buy_orders
305
- if order.get_trade_closed_at() is not None
306
- ]
307
-
308
- if len(buy_orders_with_trade_closed) == 0:
309
- return 0
310
-
311
- total_duration = 0
312
-
313
- for order in buy_orders_with_trade_closed:
314
- duration = order.get_trade_closed_at() - order.get_created_at()
315
- total_duration += duration.total_seconds() / 3600
316
-
317
- return total_duration / len(buy_orders_with_trade_closed)
318
-
319
- def get_average_trade_size(self, portfolio_id):
320
- """
321
- Get the average trade size. This function will calculate
322
- the average trade size by summing the size of all the trades
323
- and then dividing it by the total number of trades.
324
-
325
- param portfolio_id: The id of the portfolio
326
- type portfolio_id: str
327
-
328
- return: The average trade size
329
- """
330
- portfolio = self.portfolio_repository.find({"id": portfolio_id})
331
- buy_orders = self.order_repository.get_all(
332
- {
333
- "portfolio_id": portfolio.id,
334
- "order_side": OrderSide.BUY.value,
335
- }
336
- )
337
- closed_buy_orders = [
338
- order for order in buy_orders
339
- if order.get_trade_closed_at() is not None
340
- ]
341
-
342
- if len(closed_buy_orders) == 0:
343
- return 0
344
-
345
- total_size = 0
346
-
347
- for order in closed_buy_orders:
348
- total_size += order.get_amount() * order.get_price()
349
-
350
- return total_size / len(closed_buy_orders)
@@ -1,53 +0,0 @@
1
- import json
2
- import os
3
-
4
- from investing_algorithm_framework.domain import BacktestReport, \
5
- DATETIME_FORMAT_BACKTESTING
6
-
7
-
8
- class BacktestReportWriterService:
9
- """
10
- Service to write backtest reports to a file.
11
-
12
- Service supports writing backtest reports to the following formats:
13
- - JSON
14
- """
15
-
16
- def write_report_to_json(
17
- self, report: BacktestReport, output_directory: str
18
- ):
19
- if not os.path.exists(output_directory):
20
- os.makedirs(output_directory)
21
- backtest_start_date = report.backtest_date_range.start_date\
22
- .strftime(DATETIME_FORMAT_BACKTESTING)
23
- backtest_end_date = report.backtest_date_range.end_date\
24
- .strftime(DATETIME_FORMAT_BACKTESTING)
25
- created_at = report.created_at.strftime(DATETIME_FORMAT_BACKTESTING)
26
- json_file_path = os.path.join(
27
- output_directory,
28
- f"report_{report.name}_backtest_start_date_"
29
- f"{backtest_start_date}_backtest_end_date_"
30
- f"{backtest_end_date}_created_at_{created_at}.json"
31
- )
32
- report_dict = report.to_dict()
33
- # Convert dictionary to JSON
34
- json_data = json.dumps(report_dict, indent=4)
35
-
36
- # Write JSON data to a .json file
37
- with open(json_file_path, "w") as json_file:
38
- json_file.write(json_data)
39
-
40
- @staticmethod
41
- def create_report_name(report, output_directory, extension=".json"):
42
- backtest_start_date = report.backtest_start_date \
43
- .strftime(DATETIME_FORMAT_BACKTESTING)
44
- backtest_end_date = report.backtest_end_date \
45
- .strftime(DATETIME_FORMAT_BACKTESTING)
46
- created_at = report.created_at.strftime(DATETIME_FORMAT_BACKTESTING)
47
- file_path = os.path.join(
48
- output_directory,
49
- f"report_{report.name}_backtest_start_date_"
50
- f"{backtest_start_date}_backtest_end_date_"
51
- f"{backtest_end_date}_created_at_{created_at}{extension}"
52
- )
53
- return file_path
@@ -1,61 +0,0 @@
1
- from typing import List
2
-
3
- import pandas as pd
4
- from plotly import graph_objects as go
5
-
6
- from investing_algorithm_framework.domain import Trade
7
-
8
-
9
- def create_prices_chart(df, column="Close"):
10
- """
11
- Function to create a prices chart.
12
- """
13
- return go.Scatter(
14
- x=df.index,
15
- y=df[column],
16
- mode='lines',
17
- line=dict(color="blue", width=1),
18
- name="Close"
19
- )
20
-
21
-
22
- def create_trade_entry_markers_chart(df, trades: List[Trade]):
23
- df['entry_prices'] = None
24
-
25
- for trade in trades:
26
- opened_index = df.index.get_indexer(
27
- [pd.to_datetime(trade.opened_at)], method='nearest'
28
- )
29
- df.at[df.index[opened_index[0]], 'entry_prices'] = df.at[
30
- df.index[opened_index[0]], 'Close']
31
-
32
- return go.Scatter(
33
- x=df.index,
34
- y=df["entry_prices"],
35
- marker_symbol="arrow-up",
36
- marker=dict(color='green'),
37
- mode='markers',
38
- name='Buy'
39
- )
40
-
41
-
42
- def create_trade_exit_markers_chart(df, trades: List[Trade]):
43
- df['exit_prices'] = None
44
-
45
- for trade in trades:
46
-
47
- if trade.closed_at is not None:
48
- closed_index = df.index.get_indexer(
49
- [pd.to_datetime(trade.closed_at)], method='nearest'
50
- )
51
- df.at[df.index[closed_index[0]], 'exit_prices'] = df.at[
52
- df.index[closed_index[0]], 'Close']
53
-
54
- return go.Scatter(
55
- x=df.index,
56
- y=df["exit_prices"],
57
- marker_symbol="arrow-down",
58
- marker=dict(color='red'),
59
- mode='markers',
60
- name='Sell'
61
- )
@@ -1,8 +0,0 @@
1
- from .backtest_market_data_source_service import \
2
- BacktestMarketDataSourceService
3
- from .market_data_source_service import MarketDataSourceService
4
-
5
- __all__ = [
6
- "MarketDataSourceService",
7
- "BacktestMarketDataSourceService"
8
- ]
@@ -1,150 +0,0 @@
1
- from typing import List
2
-
3
- from tqdm import tqdm
4
-
5
- from investing_algorithm_framework.domain import MarketService, \
6
- BacktestMarketDataSource, BACKTESTING_END_DATE, BACKTESTING_START_DATE, \
7
- BACKTESTING_INDEX_DATETIME, OperationalException
8
- from investing_algorithm_framework.services.configuration_service import \
9
- ConfigurationService
10
- from investing_algorithm_framework.services.market_credential_service \
11
- import MarketCredentialService
12
- from .market_data_source_service import MarketDataSourceService
13
-
14
-
15
- class BacktestMarketDataSourceService(MarketDataSourceService):
16
- """
17
- BacktestMarketDataSourceService is a subclass of MarketDataSourceService.
18
- It is used to create market data sources for backtesting.
19
-
20
- In the constructor, it takes a list of BacktestMarketDataSource objects.
21
- These objects are used to prepare the data for backtesting.
22
-
23
- The prepare_data method of BacktestMarketDataSource is called in the
24
- constructor.
25
- """
26
- def __init__(
27
- self,
28
- market_data_sources: List[BacktestMarketDataSource],
29
- market_service: MarketService,
30
- market_credential_service: MarketCredentialService,
31
- configuration_service: ConfigurationService,
32
- ):
33
- super().__init__(
34
- market_service=market_service,
35
- market_data_sources=None,
36
- market_credential_service=market_credential_service,
37
- )
38
- self.market_data_sources = []
39
-
40
- # Add all market data sources to the list
41
- if market_data_sources is not None:
42
- for market_data_source in market_data_sources:
43
- self.add(market_data_source)
44
-
45
- self._configuration_service: ConfigurationService = \
46
- configuration_service
47
-
48
- for backtest_market_data_source in tqdm(
49
- market_data_sources,
50
- total=len(self._market_data_sources),
51
- desc="Preparing backtest market data",
52
- colour="GREEN"
53
- ):
54
-
55
- if backtest_market_data_source is not None:
56
- backtest_market_data_source.market_credentials_service = \
57
- self._market_credential_service
58
- backtest_market_data_source.prepare_data(
59
- config=configuration_service.get_config(),
60
- backtest_start_date=configuration_service
61
- .get_config()[BACKTESTING_START_DATE],
62
- backtest_end_date=configuration_service
63
- .get_config()[BACKTESTING_END_DATE],
64
- market_credential_service=self._market_credential_service
65
- )
66
-
67
- def get_data(self, identifier):
68
- """
69
- This method is used to get the data for backtesting. It loops
70
- over all the backtest market data sources and returns the data
71
- for the given identifier (If there is a match).
72
- """
73
- for backtest_market_data_source in self._market_data_sources:
74
- if backtest_market_data_source.identifier == identifier:
75
- backtest_market_data_source.market_credentials_service = \
76
- self._market_credential_service
77
- return backtest_market_data_source.get_data(
78
- backtest_index_date=self._configuration_service
79
- .config[BACKTESTING_INDEX_DATETIME],
80
- )
81
-
82
- raise OperationalException(
83
- f"Backtest market data source not found for {identifier}"
84
- )
85
-
86
- def get_ticker(self, symbol, market):
87
- market_data_source = self.get_ticker_market_data_source(
88
- symbol=symbol, market=market
89
- )
90
-
91
- if market_data_source is None:
92
- raise OperationalException(
93
- f"Backtest ticker data source "
94
- f"not found for {symbol} and market {market}"
95
- )
96
-
97
- market_data_source.market_credentials_service = \
98
- self._market_credential_service
99
- return market_data_source.get_data(
100
- backtest_index_date=self._configuration_service
101
- .config[BACKTESTING_INDEX_DATETIME],
102
- )
103
-
104
- def get_order_book(self, symbol, market):
105
- market_data_source = self.get_order_book_market_data_source(
106
- symbol=symbol, market=market
107
- )
108
- market_data_source.market_credential_service = \
109
- self._market_credential_service
110
- return market_data_source.get_data(
111
- backtest_index_date=self._configuration_service
112
- .config[BACKTESTING_INDEX_DATETIME],
113
- )
114
-
115
- def get_ohlcv(
116
- self,
117
- symbol,
118
- from_timestamp,
119
- time_frame=None,
120
- market=None,
121
- to_timestamp=None
122
- ):
123
- market_data_source = self.get_ohlcv_market_data_source(
124
- symbol=symbol, market=market, time_frame=time_frame
125
- )
126
- market_data_source.market_credential_service = \
127
- self._market_credential_service
128
- return market_data_source.get_data(
129
- from_timestamp=from_timestamp,
130
- to_timestamp=to_timestamp,
131
- backtest_index_date=self._configuration_service
132
- .config[BACKTESTING_INDEX_DATETIME],
133
- )
134
-
135
- def is_ohlcv_data_source_present(self, symbol, time_frame, market):
136
- market_data_source = self.get_ohlcv_market_data_source(
137
- symbol=symbol, market=market, time_frame=time_frame
138
- )
139
- return market_data_source is not None
140
-
141
- def add(self, market_data_source):
142
-
143
- # Check if there is already a market data source with the same
144
- # identifier
145
- for existing_market_data_source in self._market_data_sources:
146
- if existing_market_data_source.get_identifier() == \
147
- market_data_source.get_identifier():
148
- return
149
-
150
- self._market_data_sources.append(market_data_source)