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.
- investing_algorithm_framework/__init__.py +192 -16
- investing_algorithm_framework/analysis/__init__.py +16 -0
- investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
- investing_algorithm_framework/analysis/data.py +170 -0
- investing_algorithm_framework/analysis/markdown.py +91 -0
- investing_algorithm_framework/analysis/ranking.py +298 -0
- investing_algorithm_framework/app/__init__.py +29 -4
- investing_algorithm_framework/app/algorithm/__init__.py +7 -0
- investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
- investing_algorithm_framework/app/app.py +2220 -379
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1724 -0
- investing_algorithm_framework/app/eventloop.py +620 -0
- investing_algorithm_framework/app/reporting/__init__.py +27 -0
- investing_algorithm_framework/app/reporting/ascii.py +921 -0
- investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
- investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
- investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
- investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
- investing_algorithm_framework/app/reporting/generate.py +185 -0
- investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +80 -0
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +147 -0
- investing_algorithm_framework/app/reporting/tables/trades_table.py +75 -0
- investing_algorithm_framework/app/reporting/tables/utils.py +29 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +154 -0
- investing_algorithm_framework/app/stateless/action_handlers/__init__.py +6 -3
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
- investing_algorithm_framework/app/strategy.py +867 -60
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/app/web/__init__.py +2 -1
- investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
- investing_algorithm_framework/app/web/controllers/orders.py +3 -2
- investing_algorithm_framework/app/web/controllers/positions.py +2 -2
- investing_algorithm_framework/app/web/create_app.py +4 -2
- investing_algorithm_framework/app/web/schemas/position.py +1 -0
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +231 -0
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
- investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
- investing_algorithm_framework/cli/initialize_app.py +603 -0
- investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
- investing_algorithm_framework/cli/templates/app.py.template +18 -0
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
- investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
- investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
- investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
- investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
- investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
- investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
- investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
- investing_algorithm_framework/cli/templates/env.example.template +2 -0
- investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
- investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
- investing_algorithm_framework/cli/templates/readme.md.template +135 -0
- investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
- investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
- investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
- investing_algorithm_framework/create_app.py +40 -7
- investing_algorithm_framework/dependency_container.py +100 -47
- investing_algorithm_framework/domain/__init__.py +97 -30
- investing_algorithm_framework/domain/algorithm_id.py +69 -0
- investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
- investing_algorithm_framework/domain/config.py +59 -136
- investing_algorithm_framework/domain/constants.py +18 -37
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +42 -0
- investing_algorithm_framework/domain/exceptions.py +51 -1
- investing_algorithm_framework/domain/models/__init__.py +26 -19
- investing_algorithm_framework/domain/models/app_mode.py +34 -0
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +222 -0
- investing_algorithm_framework/domain/models/data/data_type.py +46 -0
- investing_algorithm_framework/domain/models/event.py +35 -0
- investing_algorithm_framework/domain/models/market/__init__.py +5 -0
- investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
- investing_algorithm_framework/domain/models/order/__init__.py +3 -4
- investing_algorithm_framework/domain/models/order/order.py +198 -65
- investing_algorithm_framework/domain/models/order/order_status.py +2 -2
- investing_algorithm_framework/domain/models/order/order_type.py +1 -3
- investing_algorithm_framework/domain/models/portfolio/__init__.py +6 -2
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +98 -3
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -43
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +108 -11
- investing_algorithm_framework/domain/models/position/__init__.py +2 -1
- investing_algorithm_framework/domain/models/position/position.py +20 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- investing_algorithm_framework/domain/models/position/position_snapshot.py +0 -2
- investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
- investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
- investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
- investing_algorithm_framework/domain/models/strategy_profile.py +19 -141
- investing_algorithm_framework/domain/models/time_frame.py +94 -98
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +66 -2
- investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
- investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
- investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
- investing_algorithm_framework/domain/models/trade/trade.py +389 -0
- investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
- investing_algorithm_framework/domain/order_executor.py +112 -0
- investing_algorithm_framework/domain/portfolio_provider.py +118 -0
- investing_algorithm_framework/domain/services/__init__.py +11 -0
- investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
- investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
- investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
- investing_algorithm_framework/domain/services/rounding_service.py +27 -0
- investing_algorithm_framework/domain/services/state_handler.py +38 -0
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +15 -5
- investing_algorithm_framework/domain/utils/csv.py +22 -0
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/dates.py +57 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +53 -0
- investing_algorithm_framework/domain/utils/random.py +29 -0
- investing_algorithm_framework/download_data.py +244 -0
- investing_algorithm_framework/infrastructure/__init__.py +37 -11
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
- investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
- investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
- investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +86 -12
- investing_algorithm_framework/infrastructure/models/__init__.py +7 -3
- investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
- investing_algorithm_framework/infrastructure/models/order/order.py +53 -53
- investing_algorithm_framework/infrastructure/models/order/order_metadata.py +44 -0
- investing_algorithm_framework/infrastructure/models/order_trade_association.py +10 -0
- investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +1 -1
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +8 -2
- investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +17 -6
- investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +3 -1
- investing_algorithm_framework/infrastructure/models/trades/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/models/trades/trade.py +130 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +59 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
- investing_algorithm_framework/infrastructure/repositories/__init__.py +10 -4
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +16 -5
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +11 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +84 -30
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +9 -4
- investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
- investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
- investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
- investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
- investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
- investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
- investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
- investing_algorithm_framework/services/__init__.py +123 -15
- investing_algorithm_framework/services/configuration_service.py +77 -11
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
- investing_algorithm_framework/services/market_credential_service.py +40 -0
- investing_algorithm_framework/services/metrics/__init__.py +119 -0
- investing_algorithm_framework/services/metrics/alpha.py +0 -0
- investing_algorithm_framework/services/metrics/beta.py +0 -0
- investing_algorithm_framework/services/metrics/cagr.py +60 -0
- investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
- investing_algorithm_framework/services/metrics/drawdown.py +218 -0
- investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
- investing_algorithm_framework/services/metrics/exposure.py +210 -0
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
- investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
- investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
- investing_algorithm_framework/services/metrics/recovery.py +113 -0
- investing_algorithm_framework/services/metrics/returns.py +452 -0
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
- investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
- investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
- investing_algorithm_framework/services/metrics/trades.py +473 -0
- investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
- investing_algorithm_framework/services/metrics/ulcer.py +0 -0
- investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
- investing_algorithm_framework/services/metrics/volatility.py +118 -0
- investing_algorithm_framework/services/metrics/win_rate.py +177 -0
- investing_algorithm_framework/services/order_service/__init__.py +9 -0
- investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
- investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
- investing_algorithm_framework/services/order_service/order_service.py +826 -0
- investing_algorithm_framework/services/portfolios/__init__.py +16 -0
- investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
- investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
- investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
- investing_algorithm_framework/services/positions/__init__.py +7 -0
- investing_algorithm_framework/services/positions/position_service.py +210 -0
- investing_algorithm_framework/services/repository_service.py +8 -2
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
- investing_algorithm_framework/services/trade_service/__init__.py +9 -0
- investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
- investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
- investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
- investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
- investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
- {investing_algorithm_framework-1.5.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
- investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
- investing_algorithm_framework/app/algorithm.py +0 -630
- investing_algorithm_framework/domain/models/backtest_profile.py +0 -414
- investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
- investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
- investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -105
- investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
- investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
- investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
- investing_algorithm_framework/domain/models/trade.py +0 -78
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
- investing_algorithm_framework/domain/singleton.py +0 -9
- investing_algorithm_framework/domain/utils/backtesting.py +0 -82
- investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
- investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
- investing_algorithm_framework/infrastructure/services/market_backtest_service.py +0 -360
- investing_algorithm_framework/infrastructure/services/market_service.py +0 -410
- investing_algorithm_framework/infrastructure/services/performance_service.py +0 -192
- investing_algorithm_framework/services/backtest_service.py +0 -268
- investing_algorithm_framework/services/market_data_service.py +0 -77
- investing_algorithm_framework/services/order_backtest_service.py +0 -122
- investing_algorithm_framework/services/order_service.py +0 -752
- investing_algorithm_framework/services/portfolio_service.py +0 -164
- investing_algorithm_framework/services/portfolio_snapshot_service.py +0 -68
- investing_algorithm_framework/services/position_cost_service.py +0 -5
- investing_algorithm_framework/services/position_service.py +0 -63
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -225
- investing_algorithm_framework-1.5.dist-info/AUTHORS.md +0 -8
- investing_algorithm_framework-1.5.dist-info/METADATA +0 -230
- investing_algorithm_framework-1.5.dist-info/RECORD +0 -119
- investing_algorithm_framework-1.5.dist-info/top_level.txt +0 -1
- /investing_algorithm_framework/{infrastructure/services/performance_backtest_service.py → app/reporting/tables/stop_loss_table.py} +0 -0
- /investing_algorithm_framework/services/{position_snapshot_service.py → positions/position_snapshot_service.py} +0 -0
- {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
|
+
)
|