investing-algorithm-framework 1.5__py3-none-any.whl → 7.25.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. investing_algorithm_framework/__init__.py +192 -16
  2. investing_algorithm_framework/analysis/__init__.py +16 -0
  3. investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
  4. investing_algorithm_framework/analysis/data.py +170 -0
  5. investing_algorithm_framework/analysis/markdown.py +91 -0
  6. investing_algorithm_framework/analysis/ranking.py +298 -0
  7. investing_algorithm_framework/app/__init__.py +29 -4
  8. investing_algorithm_framework/app/algorithm/__init__.py +7 -0
  9. investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
  10. investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
  11. investing_algorithm_framework/app/app.py +2220 -379
  12. investing_algorithm_framework/app/app_hook.py +28 -0
  13. investing_algorithm_framework/app/context.py +1724 -0
  14. investing_algorithm_framework/app/eventloop.py +620 -0
  15. investing_algorithm_framework/app/reporting/__init__.py +27 -0
  16. investing_algorithm_framework/app/reporting/ascii.py +921 -0
  17. investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
  18. investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
  19. investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
  20. investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
  21. investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
  22. investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
  23. investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
  24. investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
  25. investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
  26. investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
  27. investing_algorithm_framework/app/reporting/generate.py +185 -0
  28. investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
  29. investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
  30. investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
  31. investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
  32. investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
  33. investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
  34. investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
  35. investing_algorithm_framework/app/stateless/action_handlers/__init__.py +6 -3
  36. investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
  37. investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
  38. investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
  39. investing_algorithm_framework/app/strategy.py +867 -60
  40. investing_algorithm_framework/app/task.py +5 -3
  41. investing_algorithm_framework/app/web/__init__.py +2 -1
  42. investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
  43. investing_algorithm_framework/app/web/controllers/orders.py +3 -2
  44. investing_algorithm_framework/app/web/controllers/positions.py +2 -2
  45. investing_algorithm_framework/app/web/create_app.py +4 -2
  46. investing_algorithm_framework/app/web/schemas/position.py +1 -0
  47. investing_algorithm_framework/cli/__init__.py +0 -0
  48. investing_algorithm_framework/cli/cli.py +231 -0
  49. investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
  50. investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
  51. investing_algorithm_framework/cli/initialize_app.py +603 -0
  52. investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
  53. investing_algorithm_framework/cli/templates/app.py.template +18 -0
  54. investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
  55. investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
  56. investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
  57. investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
  58. investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
  59. investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
  60. investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
  61. investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
  62. investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
  63. investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
  64. investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
  65. investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
  66. investing_algorithm_framework/cli/templates/env.example.template +2 -0
  67. investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
  68. investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
  69. investing_algorithm_framework/cli/templates/readme.md.template +135 -0
  70. investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
  71. investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
  72. investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
  73. investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
  74. investing_algorithm_framework/create_app.py +40 -7
  75. investing_algorithm_framework/dependency_container.py +100 -47
  76. investing_algorithm_framework/domain/__init__.py +97 -30
  77. investing_algorithm_framework/domain/algorithm_id.py +69 -0
  78. investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
  79. investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
  80. investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
  81. investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
  82. investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
  83. investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
  84. investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
  85. investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
  86. investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
  87. investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
  88. investing_algorithm_framework/domain/config.py +59 -136
  89. investing_algorithm_framework/domain/constants.py +18 -37
  90. investing_algorithm_framework/domain/data_provider.py +334 -0
  91. investing_algorithm_framework/domain/data_structures.py +42 -0
  92. investing_algorithm_framework/domain/exceptions.py +51 -1
  93. investing_algorithm_framework/domain/models/__init__.py +26 -19
  94. investing_algorithm_framework/domain/models/app_mode.py +34 -0
  95. investing_algorithm_framework/domain/models/data/__init__.py +7 -0
  96. investing_algorithm_framework/domain/models/data/data_source.py +222 -0
  97. investing_algorithm_framework/domain/models/data/data_type.py +46 -0
  98. investing_algorithm_framework/domain/models/event.py +35 -0
  99. investing_algorithm_framework/domain/models/market/__init__.py +5 -0
  100. investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
  101. investing_algorithm_framework/domain/models/order/__init__.py +3 -4
  102. investing_algorithm_framework/domain/models/order/order.py +198 -65
  103. investing_algorithm_framework/domain/models/order/order_status.py +2 -2
  104. investing_algorithm_framework/domain/models/order/order_type.py +1 -3
  105. investing_algorithm_framework/domain/models/portfolio/__init__.py +6 -2
  106. investing_algorithm_framework/domain/models/portfolio/portfolio.py +98 -3
  107. investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -43
  108. investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
  109. investing_algorithm_framework/domain/models/position/__init__.py +2 -1
  110. investing_algorithm_framework/domain/models/position/position.py +20 -0
  111. investing_algorithm_framework/domain/models/position/position_size.py +41 -0
  112. investing_algorithm_framework/domain/models/position/position_snapshot.py +0 -2
  113. investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
  114. investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
  115. investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
  116. investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
  117. investing_algorithm_framework/domain/models/strategy_profile.py +19 -141
  118. investing_algorithm_framework/domain/models/time_frame.py +94 -98
  119. investing_algorithm_framework/domain/models/time_interval.py +33 -0
  120. investing_algorithm_framework/domain/models/time_unit.py +66 -2
  121. investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
  122. investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
  123. investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
  124. investing_algorithm_framework/domain/models/trade/trade.py +389 -0
  125. investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
  126. investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
  127. investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
  128. investing_algorithm_framework/domain/order_executor.py +112 -0
  129. investing_algorithm_framework/domain/portfolio_provider.py +118 -0
  130. investing_algorithm_framework/domain/services/__init__.py +11 -0
  131. investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
  132. investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
  133. investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
  134. investing_algorithm_framework/domain/services/rounding_service.py +27 -0
  135. investing_algorithm_framework/domain/services/state_handler.py +38 -0
  136. investing_algorithm_framework/domain/strategy.py +1 -29
  137. investing_algorithm_framework/domain/utils/__init__.py +15 -5
  138. investing_algorithm_framework/domain/utils/csv.py +22 -0
  139. investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
  140. investing_algorithm_framework/domain/utils/dates.py +57 -0
  141. investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
  142. investing_algorithm_framework/domain/utils/polars.py +53 -0
  143. investing_algorithm_framework/domain/utils/random.py +29 -0
  144. investing_algorithm_framework/download_data.py +244 -0
  145. investing_algorithm_framework/infrastructure/__init__.py +37 -11
  146. investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
  147. investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
  148. investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
  149. investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
  150. investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
  151. investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
  152. investing_algorithm_framework/infrastructure/models/__init__.py +7 -3
  153. investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
  154. investing_algorithm_framework/infrastructure/models/order/order.py +53 -53
  155. investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
  156. investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
  157. investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +1 -1
  158. investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -2
  159. investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -6
  160. investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +3 -1
  161. investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
  162. investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
  163. investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
  164. investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
  165. investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
  166. investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
  167. investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
  168. investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
  169. investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
  170. investing_algorithm_framework/infrastructure/repositories/__init__.py +10 -4
  171. investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
  172. investing_algorithm_framework/infrastructure/repositories/order_repository.py +16 -5
  173. investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
  174. investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
  175. investing_algorithm_framework/infrastructure/repositories/repository.py +84 -30
  176. investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
  177. investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
  178. investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
  179. investing_algorithm_framework/infrastructure/services/__init__.py +9 -4
  180. investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
  181. investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
  182. investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
  183. investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
  184. investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
  185. investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
  186. investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
  187. investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
  188. investing_algorithm_framework/services/__init__.py +123 -15
  189. investing_algorithm_framework/services/configuration_service.py +77 -11
  190. investing_algorithm_framework/services/data_providers/__init__.py +5 -0
  191. investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
  192. investing_algorithm_framework/services/market_credential_service.py +40 -0
  193. investing_algorithm_framework/services/metrics/__init__.py +119 -0
  194. investing_algorithm_framework/services/metrics/alpha.py +0 -0
  195. investing_algorithm_framework/services/metrics/beta.py +0 -0
  196. investing_algorithm_framework/services/metrics/cagr.py +60 -0
  197. investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
  198. investing_algorithm_framework/services/metrics/drawdown.py +218 -0
  199. investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
  200. investing_algorithm_framework/services/metrics/exposure.py +210 -0
  201. investing_algorithm_framework/services/metrics/generate.py +358 -0
  202. investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
  203. investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
  204. investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
  205. investing_algorithm_framework/services/metrics/recovery.py +113 -0
  206. investing_algorithm_framework/services/metrics/returns.py +452 -0
  207. investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
  208. investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
  209. investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
  210. investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
  211. investing_algorithm_framework/services/metrics/trades.py +473 -0
  212. investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  213. investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  214. investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  215. investing_algorithm_framework/services/metrics/volatility.py +118 -0
  216. investing_algorithm_framework/services/metrics/win_rate.py +177 -0
  217. investing_algorithm_framework/services/order_service/__init__.py +9 -0
  218. investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
  219. investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
  220. investing_algorithm_framework/services/order_service/order_service.py +826 -0
  221. investing_algorithm_framework/services/portfolios/__init__.py +16 -0
  222. investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
  223. investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
  224. investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
  225. investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
  226. investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
  227. investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
  228. investing_algorithm_framework/services/positions/__init__.py +7 -0
  229. investing_algorithm_framework/services/positions/position_service.py +210 -0
  230. investing_algorithm_framework/services/repository_service.py +8 -2
  231. investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
  232. investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
  233. investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
  234. investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
  235. investing_algorithm_framework/services/trade_service/__init__.py +9 -0
  236. investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
  237. investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
  238. investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
  239. investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
  240. investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
  241. {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
  242. investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
  243. investing_algorithm_framework/app/algorithm.py +0 -630
  244. investing_algorithm_framework/domain/models/backtest_profile.py +0 -414
  245. investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
  246. investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
  247. investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -105
  248. investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
  249. investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
  250. investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
  251. investing_algorithm_framework/domain/models/trade.py +0 -78
  252. investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
  253. investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
  254. investing_algorithm_framework/domain/singleton.py +0 -9
  255. investing_algorithm_framework/domain/utils/backtesting.py +0 -82
  256. investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
  257. investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
  258. investing_algorithm_framework/infrastructure/services/market_backtest_service.py +0 -360
  259. investing_algorithm_framework/infrastructure/services/market_service.py +0 -410
  260. investing_algorithm_framework/infrastructure/services/performance_service.py +0 -192
  261. investing_algorithm_framework/services/backtest_service.py +0 -268
  262. investing_algorithm_framework/services/market_data_service.py +0 -77
  263. investing_algorithm_framework/services/order_backtest_service.py +0 -122
  264. investing_algorithm_framework/services/order_service.py +0 -752
  265. investing_algorithm_framework/services/portfolio_service.py +0 -164
  266. investing_algorithm_framework/services/portfolio_snapshot_service.py +0 -68
  267. investing_algorithm_framework/services/position_cost_service.py +0 -5
  268. investing_algorithm_framework/services/position_service.py +0 -63
  269. investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -225
  270. investing_algorithm_framework-1.5.dist-info/AUTHORS.md +0 -8
  271. investing_algorithm_framework-1.5.dist-info/METADATA +0 -230
  272. investing_algorithm_framework-1.5.dist-info/RECORD +0 -119
  273. investing_algorithm_framework-1.5.dist-info/top_level.txt +0 -1
  274. /investing_algorithm_framework/{infrastructure/services/performance_backtest_service.py → app/reporting/tables/stop_loss_table.py} +0 -0
  275. /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
  276. {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
@@ -0,0 +1,285 @@
1
+ import logging
2
+ from datetime import datetime, timezone, timedelta
3
+ from collections import defaultdict
4
+ from typing import Dict, List
5
+
6
+ from investing_algorithm_framework.domain import BacktestDateRange, \
7
+ BacktestRun, TimeUnit, generate_backtest_summary_metrics, Backtest
8
+ from investing_algorithm_framework.services import DataProviderService, \
9
+ create_backtest_metrics
10
+
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class EventBacktestService:
16
+ """
17
+ Service that handles event-driven backtesting.
18
+
19
+ This service encapsulates the logic for running event-driven backtests,
20
+ where the strategy's `on_run` method is called at each scheduled time
21
+ step. This is different from vectorized backtesting where buy/sell
22
+ signals are generated in a vectorized manner.
23
+
24
+ The event-driven backtest simulates the trading bot running in real-time,
25
+ executing strategies at their scheduled intervals and processing orders,
26
+ trades, stop losses, and take profits at each iteration.
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ data_provider_service: DataProviderService,
32
+ order_service,
33
+ portfolio_service,
34
+ portfolio_snapshot_service,
35
+ position_repository,
36
+ trade_service,
37
+ configuration_service,
38
+ portfolio_configuration_service,
39
+ ):
40
+ """
41
+ Initialize the EventBacktestService.
42
+
43
+ Args:
44
+ data_provider_service: Service for fetching market data.
45
+ order_service: Service for managing orders.
46
+ portfolio_service: Service for managing portfolios.
47
+ portfolio_snapshot_service: Service for creating
48
+ portfolio snapshots.
49
+ position_repository: Repository for positions.
50
+ trade_service: Service for managing trades.
51
+ configuration_service: Service for configuration management.
52
+ portfolio_configuration_service: Service for
53
+ portfolio configuration.
54
+ """
55
+ self._data_provider_service = data_provider_service
56
+ self._order_service = order_service
57
+ self._portfolio_service = portfolio_service
58
+ self._portfolio_snapshot_service = portfolio_snapshot_service
59
+ self._position_repository = position_repository
60
+ self._trade_service = trade_service
61
+ self._configuration_service = configuration_service
62
+ self._portfolio_configuration_service = portfolio_configuration_service
63
+
64
+ def run(
65
+ self,
66
+ algorithm,
67
+ backtest_date_range: BacktestDateRange,
68
+ risk_free_rate: float,
69
+ event_loop_service,
70
+ trade_order_evaluator,
71
+ show_progress: bool = True,
72
+ ) -> BacktestRun:
73
+ """
74
+ Run an event-driven backtest for an algorithm.
75
+
76
+ This method executes the algorithm's strategies according to their
77
+ scheduled intervals, simulating real-time trading behavior.
78
+
79
+ Args:
80
+ algorithm: The algorithm containing strategies and tasks to run.
81
+ backtest_date_range: The date range for the backtest.
82
+ risk_free_rate: The risk-free rate for calculating metrics.
83
+ event_loop_service: The event loop service instance
84
+ (pre-configured).
85
+ trade_order_evaluator: The trade order evaluator for handling
86
+ pending orders, stop losses, and take profits.
87
+ show_progress: Whether to show progress bars.
88
+
89
+ Returns:
90
+ BacktestRun: The backtest run containing results and metrics.
91
+ """
92
+ # Generate schedule
93
+ schedule = self.generate_schedule(
94
+ algorithm.strategies,
95
+ algorithm.tasks,
96
+ backtest_date_range.start_date,
97
+ backtest_date_range.end_date
98
+ )
99
+
100
+ # Initialize and run the event loop
101
+ event_loop_service.initialize(
102
+ algorithm=algorithm,
103
+ trade_order_evaluator=trade_order_evaluator
104
+ )
105
+ event_loop_service.start(
106
+ schedule=schedule, show_progress=show_progress
107
+ )
108
+
109
+ # Create backtest run from results
110
+ return self._create_backtest_run(
111
+ algorithm=algorithm,
112
+ backtest_date_range=backtest_date_range,
113
+ number_of_runs=event_loop_service.total_number_of_runs,
114
+ risk_free_rate=risk_free_rate,
115
+ )
116
+
117
+ def generate_schedule(
118
+ self,
119
+ strategies,
120
+ tasks,
121
+ start_date: datetime,
122
+ end_date: datetime
123
+ ) -> Dict[datetime, Dict[str, List[str]]]:
124
+ """
125
+ Generates a dict-based schedule: datetime => {strategy_ids, task_ids}
126
+
127
+ This schedule determines when each strategy should be executed during
128
+ the backtest based on their defined time units and intervals.
129
+
130
+ Args:
131
+ strategies: List of strategies to schedule.
132
+ tasks: List of tasks to schedule.
133
+ start_date: Start date of the backtest.
134
+ end_date: End date of the backtest.
135
+
136
+ Returns:
137
+ Dict mapping datetime to strategy_ids and task_ids to run.
138
+ """
139
+ schedule = defaultdict(
140
+ lambda: {"strategy_ids": set(), "task_ids": set(tasks)}
141
+ )
142
+
143
+ for strategy in strategies:
144
+ strategy_id = strategy.strategy_profile.strategy_id
145
+ interval = strategy.strategy_profile.interval
146
+ time_unit = strategy.strategy_profile.time_unit
147
+
148
+ if time_unit == TimeUnit.SECOND:
149
+ step = timedelta(seconds=interval)
150
+ elif time_unit == TimeUnit.MINUTE:
151
+ step = timedelta(minutes=interval)
152
+ elif time_unit == TimeUnit.HOUR:
153
+ step = timedelta(hours=interval)
154
+ elif time_unit == TimeUnit.DAY:
155
+ step = timedelta(days=interval)
156
+ else:
157
+ raise ValueError(f"Unsupported time unit: {time_unit}")
158
+
159
+ t = start_date
160
+ while t <= end_date:
161
+ schedule[t]["strategy_ids"].add(strategy_id)
162
+ t += step
163
+
164
+ return {
165
+ ts: {
166
+ "strategy_ids": sorted(data["strategy_ids"]),
167
+ "task_ids": sorted(data["task_ids"])
168
+ }
169
+ for ts, data in schedule.items()
170
+ }
171
+
172
+ def _create_backtest_run(
173
+ self,
174
+ algorithm,
175
+ backtest_date_range: BacktestDateRange,
176
+ number_of_runs: int,
177
+ risk_free_rate: float,
178
+ ) -> BacktestRun:
179
+ """
180
+ Create a BacktestRun from the current state after event loop execution.
181
+
182
+ Args:
183
+ algorithm: The algorithm that was backtested.
184
+ backtest_date_range: The date range of the backtest.
185
+ number_of_runs: Total number of strategy executions.
186
+ risk_free_rate: Risk-free rate for metrics calculation.
187
+
188
+ Returns:
189
+ BacktestRun: The completed backtest run with metrics.
190
+ """
191
+ # Get the portfolio
192
+ portfolio = self._portfolio_service.get_all()[0]
193
+
194
+ # Get initial unallocated amount
195
+ initial_unallocated = self._get_initial_unallocated()
196
+
197
+ # Create the backtest run
198
+ run = BacktestRun(
199
+ backtest_start_date=backtest_date_range.start_date,
200
+ backtest_end_date=backtest_date_range.end_date,
201
+ backtest_date_range_name=backtest_date_range.name,
202
+ initial_unallocated=initial_unallocated,
203
+ trading_symbol=portfolio.trading_symbol,
204
+ created_at=datetime.now(tz=timezone.utc),
205
+ portfolio_snapshots=self._portfolio_snapshot_service.get_all(
206
+ {"portfolio_id": portfolio.id}
207
+ ),
208
+ number_of_runs=number_of_runs,
209
+ trades=self._trade_service.get_all(
210
+ {"portfolio": portfolio.id}
211
+ ),
212
+ orders=self._order_service.get_all(
213
+ {"portfolio": portfolio.id}
214
+ ),
215
+ positions=self._position_repository.get_all(
216
+ {"portfolio": portfolio.id}
217
+ ),
218
+ )
219
+
220
+ # Calculate and add metrics
221
+ backtest_metrics = create_backtest_metrics(
222
+ run, risk_free_rate=risk_free_rate
223
+ )
224
+ run.backtest_metrics = backtest_metrics
225
+
226
+ return run
227
+
228
+ def _get_initial_unallocated(self) -> float:
229
+ """
230
+ Get the initial unallocated amount for the backtest.
231
+
232
+ Returns:
233
+ float: The initial unallocated amount.
234
+ """
235
+ portfolios = self._portfolio_service.get_all()
236
+ initial_unallocated = 0.0
237
+
238
+ for portfolio in portfolios:
239
+ initial_unallocated += portfolio.initial_balance
240
+
241
+ return initial_unallocated
242
+
243
+ def create_backtest(
244
+ self,
245
+ algorithm,
246
+ backtest_date_range: BacktestDateRange,
247
+ number_of_runs: int,
248
+ risk_free_rate: float,
249
+ ) -> Backtest:
250
+ """
251
+ Create a Backtest object from the current state.
252
+
253
+ This method creates a full Backtest object containing the backtest
254
+ run, metrics, and summary.
255
+
256
+ Args:
257
+ algorithm: The algorithm that was backtested.
258
+ backtest_date_range: The date range of the backtest.
259
+ number_of_runs: Total number of strategy executions.
260
+ risk_free_rate: Risk-free rate for metrics calculation.
261
+
262
+ Returns:
263
+ Backtest: The completed backtest with run and summary.
264
+ """
265
+ run = self._create_backtest_run(
266
+ algorithm=algorithm,
267
+ backtest_date_range=backtest_date_range,
268
+ number_of_runs=number_of_runs,
269
+ risk_free_rate=risk_free_rate,
270
+ )
271
+
272
+ algorithm_id = (
273
+ algorithm.algorithm_id
274
+ if hasattr(algorithm, 'algorithm_id')
275
+ else algorithm.id
276
+ )
277
+
278
+ return Backtest(
279
+ algorithm_id=algorithm_id,
280
+ backtest_runs=[run],
281
+ backtest_summary=generate_backtest_summary_metrics(
282
+ [run.backtest_metrics]
283
+ ),
284
+ risk_free_rate=risk_free_rate,
285
+ )