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,147 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from investing_algorithm_framework.domain import DEFAULT_DATETIME_FORMAT
|
|
4
|
+
from .utils import safe_format, safe_format_date, safe_format_percentage
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def highlight_win_rate(row):
|
|
8
|
+
"""
|
|
9
|
+
| **Winning Percentage** | **Interpretation** |
|
|
10
|
+
|-------------------------|----------------------------------------------------------------------|
|
|
11
|
+
| **< 40%** | 🟥 Poor — Strategy may not be viable |
|
|
12
|
+
| **40% to 50%** | 🟧 Weak — Needs improvement, often breakeven or slightly negative |
|
|
13
|
+
| **50% to 60%** | 🟨 Average — Acceptable for many strategies, especially with high PF |
|
|
14
|
+
| **60% to 70%** | 🟩 Good — Solid performance, often with good risk/reward |
|
|
15
|
+
| **> 70%** | 🟩 Excellent — High win rate, typically indicates a strong edge |
|
|
16
|
+
"""
|
|
17
|
+
metric = row['Metric']
|
|
18
|
+
try:
|
|
19
|
+
value = float(row['Value'].strip('%'))
|
|
20
|
+
except Exception:
|
|
21
|
+
value = None
|
|
22
|
+
styles = pd.Series('', index=row.index)
|
|
23
|
+
if metric == 'Win Rate' and value is not None:
|
|
24
|
+
if value < 40:
|
|
25
|
+
styles['Value'] = 'color: #B22222; font-weight: bold;' # red
|
|
26
|
+
elif 40 <= value < 50:
|
|
27
|
+
styles['Value'] = 'color: #FFA500; font-weight: bold;' # orange
|
|
28
|
+
elif 50 <= value < 60:
|
|
29
|
+
styles['Value'] = 'color: #FFD700; font-weight: bold;' # gold
|
|
30
|
+
elif 60 <= value < 70:
|
|
31
|
+
styles['Value'] = 'color: #32CD32; font-weight: bold;' # lime green
|
|
32
|
+
elif value >= 70:
|
|
33
|
+
styles['Value'] = 'color: #228B22; font-weight: bold;' # dark green
|
|
34
|
+
return styles
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def highlight_win_loss_ratio(row):
|
|
38
|
+
"""
|
|
39
|
+
| **Win/Loss Ratio** | **Interpretation** |
|
|
40
|
+
|---------------------|----------------------------------------------------------------------|
|
|
41
|
+
| **< 0.5** | 🟥 Poor — Strategy may not be viable |
|
|
42
|
+
| **0.5 to 1.0** | 🟧 Weak — Needs improvement, often breakeven or slightly negative |
|
|
43
|
+
| **1.0 to 1.5** | 🟨 Average — Acceptable for many strategies, especially with high PF |
|
|
44
|
+
| **1.5 to 2.0** | 🟩 Good — Solid performance, often with good risk/reward |
|
|
45
|
+
| **> 2.0** | 🟩 Excellent — High win/loss ratio, typically indicates a strong edge |
|
|
46
|
+
"""
|
|
47
|
+
metric = row['Metric']
|
|
48
|
+
try:
|
|
49
|
+
value = float(row['Value'])
|
|
50
|
+
except Exception:
|
|
51
|
+
value = None
|
|
52
|
+
styles = pd.Series('', index=row.index)
|
|
53
|
+
if metric == 'Win/Loss Ratio' and value is not None:
|
|
54
|
+
if value < 0.5:
|
|
55
|
+
styles['Value'] = 'color: #B22222; font-weight: bold;' # red
|
|
56
|
+
elif 0.5 <= value < 1.0:
|
|
57
|
+
styles['Value'] = 'color: #FFA500; font-weight: bold;' # orange
|
|
58
|
+
elif 1.0 <= value < 1.5:
|
|
59
|
+
styles['Value'] = 'color: #FFD700; font-weight: bold;' # gold
|
|
60
|
+
elif 1.5 <= value < 2.0:
|
|
61
|
+
styles['Value'] = 'color: #32CD32; font-weight: bold;' # lime green
|
|
62
|
+
elif value >= 2.0:
|
|
63
|
+
styles['Value'] = 'color: #228B22; font-weight: bold;' # dark green
|
|
64
|
+
return styles
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def create_html_trade_metrics_table(results, report):
|
|
68
|
+
copy_results = results.to_dict().copy()
|
|
69
|
+
string_format = "{:.2f}"
|
|
70
|
+
copy_results['Trades per Year'] = safe_format(copy_results['trades_per_year'], string_format)
|
|
71
|
+
copy_results['Trades per Day'] = safe_format(copy_results['trade_per_day'], string_format)
|
|
72
|
+
copy_results['Exposure Ratio'] = safe_format(copy_results['exposure_ratio'],string_format)
|
|
73
|
+
copy_results["Cumulative Exposure"] = safe_format(copy_results['cumulative_exposure'],string_format)
|
|
74
|
+
best_trade = copy_results['best_trade']
|
|
75
|
+
|
|
76
|
+
if best_trade is None:
|
|
77
|
+
copy_results["Best Trade"] = "N/A"
|
|
78
|
+
copy_results['Best Trade Date'] = "N/A"
|
|
79
|
+
else:
|
|
80
|
+
copy_results['Best Trade'] = f"{best_trade['net_gain']:.2f} {report.trading_symbol}"
|
|
81
|
+
copy_results['Best Trade Date'] = safe_format_date(best_trade["opened_at"], format_str=DEFAULT_DATETIME_FORMAT)
|
|
82
|
+
worst_trade = copy_results['worst_trade']
|
|
83
|
+
|
|
84
|
+
if worst_trade is None:
|
|
85
|
+
copy_results["Worst Trade"] = "N/A"
|
|
86
|
+
copy_results['Worst Trade Date'] = "N/A"
|
|
87
|
+
else:
|
|
88
|
+
copy_results['Worst Trade'] = f"{worst_trade['net_gain']:.2f} {report.trading_symbol}"
|
|
89
|
+
copy_results['Worst Trade Date'] = safe_format_date(worst_trade['opened_at'], format_str=DEFAULT_DATETIME_FORMAT)
|
|
90
|
+
|
|
91
|
+
copy_results['Trades Average Gain'] = f"{safe_format(copy_results['average_trade_gain'], string_format)} {report.trading_symbol} {copy_results['trades_average_gain_percentage']:.2f}%"
|
|
92
|
+
copy_results['Trades Average Loss'] = f"{safe_format(copy_results['average_trade_loss'], string_format)} {report.trading_symbol} {copy_results['trades_average_loss_percentage']:.2f}%"
|
|
93
|
+
copy_results['Average Trade Duration'] = f"{copy_results['average_trade_duration']:.2f} hours"
|
|
94
|
+
copy_results['Number of Trades'] = f"{copy_results['number_of_trades']}"
|
|
95
|
+
copy_results['Win Rate'] = f"{copy_results['win_rate']:.2f}%"
|
|
96
|
+
copy_results['Win/Loss Ratio'] = f"{copy_results['win_loss_ratio']:.2f}"
|
|
97
|
+
|
|
98
|
+
stats = {
|
|
99
|
+
"Metric": [
|
|
100
|
+
"Trades per Year",
|
|
101
|
+
"Trade per Day",
|
|
102
|
+
"Exposure Ratio",
|
|
103
|
+
"Cumulative Exposure",
|
|
104
|
+
"Trades Average Gain",
|
|
105
|
+
"Trades Average Loss",
|
|
106
|
+
"Best Trade",
|
|
107
|
+
"Best Trade Date",
|
|
108
|
+
"Worst Trade",
|
|
109
|
+
"Worst Trade Date",
|
|
110
|
+
"Average Trade Duration",
|
|
111
|
+
"Number of Trades",
|
|
112
|
+
"Win Rate",
|
|
113
|
+
"Win/Loss Ratio"
|
|
114
|
+
],
|
|
115
|
+
"Value": [
|
|
116
|
+
copy_results['Trades per Year'],
|
|
117
|
+
copy_results['Trades per Day'],
|
|
118
|
+
copy_results['Exposure Ratio'],
|
|
119
|
+
copy_results['Cumulative Exposure'],
|
|
120
|
+
copy_results['Trades Average Gain'],
|
|
121
|
+
copy_results['Trades Average Loss'],
|
|
122
|
+
copy_results['Best Trade'],
|
|
123
|
+
copy_results['Best Trade Date'],
|
|
124
|
+
copy_results['Worst Trade'],
|
|
125
|
+
copy_results['Worst Trade Date'],
|
|
126
|
+
copy_results['Average Trade Duration'],
|
|
127
|
+
copy_results['Number of Trades'],
|
|
128
|
+
copy_results['Win Rate'],
|
|
129
|
+
copy_results['Win/Loss Ratio']
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
df_stats = pd.DataFrame(stats)
|
|
134
|
+
|
|
135
|
+
table_html = (
|
|
136
|
+
df_stats.style
|
|
137
|
+
.apply(highlight_win_rate, axis=1)
|
|
138
|
+
.apply(highlight_win_loss_ratio, axis=1)
|
|
139
|
+
.set_table_styles([
|
|
140
|
+
{'selector': 'th', 'props': [('background-color', '#f2f2f2'), ('font-weight', 'bold')]},
|
|
141
|
+
{'selector': 'td', 'props': [('font-size', '14px')]},
|
|
142
|
+
{'selector': 'tr:nth-child(even)', 'props': [('background-color', '#fafafa')]}
|
|
143
|
+
])
|
|
144
|
+
.hide(axis='index')
|
|
145
|
+
.to_html()
|
|
146
|
+
)
|
|
147
|
+
return table_html
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def highlight_net_gain(row):
|
|
5
|
+
"""
|
|
6
|
+
Apply conditional formatting to the 'Net Gain' column based on numeric value.
|
|
7
|
+
"""
|
|
8
|
+
try:
|
|
9
|
+
# Extract numeric value before the first space (assumes format like "123.45 USDT (10.23%)")
|
|
10
|
+
value_str = row['Net Gain'].split()[2]
|
|
11
|
+
value_str = value_str.split('(')[1]
|
|
12
|
+
value_str = value_str.replace(')', '').strip()
|
|
13
|
+
if '%' in value_str:
|
|
14
|
+
value_str = value_str.replace('%', '')
|
|
15
|
+
value = float(value_str)
|
|
16
|
+
|
|
17
|
+
except Exception:
|
|
18
|
+
value = None
|
|
19
|
+
|
|
20
|
+
styles = pd.Series('', index=row.index)
|
|
21
|
+
|
|
22
|
+
if value is not None:
|
|
23
|
+
if value < -5:
|
|
24
|
+
styles['Net Gain'] = 'color: #B22222; font-weight: bold;' # red
|
|
25
|
+
elif -5 <= value < -2:
|
|
26
|
+
styles['Net Gain'] = 'color: #FFA500; font-weight: bold;' # orange
|
|
27
|
+
elif -2 <= value < 0:
|
|
28
|
+
styles['Net Gain'] = 'color: #FFD700; font-weight: bold;' # gold
|
|
29
|
+
elif 0 <= value < 5:
|
|
30
|
+
styles['Net Gain'] = 'color: #32CD32; font-weight: bold;' # lime green
|
|
31
|
+
elif value >= 5:
|
|
32
|
+
styles['Net Gain'] = 'color: #228B22; font-weight: bold;' # dark green
|
|
33
|
+
|
|
34
|
+
return styles
|
|
35
|
+
|
|
36
|
+
def get_exit(trade):
|
|
37
|
+
"""
|
|
38
|
+
Returns the exit price of a trade if it is closed, otherwise returns "open".
|
|
39
|
+
This is used to display the exit price in the trades table.
|
|
40
|
+
|
|
41
|
+
A trade can have multiple exits and close prices.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
if trade.closed_at:
|
|
45
|
+
return f"{trade.closed_at.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
46
|
+
else:
|
|
47
|
+
return "open"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def create_html_trades_table(report):
|
|
51
|
+
trades = report.get_trades()
|
|
52
|
+
|
|
53
|
+
# Create a DataFrame from the trades, with the attributes id, net_gain, net_gain_percentage, entry_time, exit_time, duration, and symbol
|
|
54
|
+
selection = {
|
|
55
|
+
'Trade': [f"{trade.id} {trade.target_symbol}/{trade.trading_symbol}" for trade in trades],
|
|
56
|
+
'Net Gain': [f"{trade.change:.2f} {report.trading_symbol} ({trade.percentage_change:.2f}%)" for trade in trades],
|
|
57
|
+
'Entry (Price, Date)': [f"{trade.open_price} {trade.opened_at.strftime('%Y-%m-%d %H:%M:%S')}" for trade in trades],
|
|
58
|
+
'Exit (Price, Date)': [get_exit(trade) for trade in trades],
|
|
59
|
+
'Duration': [f"{trade.duration:.2f} hours" for trade in trades],
|
|
60
|
+
}
|
|
61
|
+
trades_df = pd.DataFrame(selection)
|
|
62
|
+
|
|
63
|
+
table_html = (
|
|
64
|
+
trades_df.style
|
|
65
|
+
.apply(highlight_net_gain, axis=1)
|
|
66
|
+
# .apply(highlight_win_loss_ratio, axis=1)
|
|
67
|
+
.set_table_styles([
|
|
68
|
+
{'selector': 'th', 'props': [('background-color', '#f2f2f2'), ('font-weight', 'bold')]},
|
|
69
|
+
{'selector': 'td', 'props': [('font-size', '14px')]},
|
|
70
|
+
{'selector': 'tr:nth-child(even)', 'props': [('background-color', '#fafafa')]}
|
|
71
|
+
])
|
|
72
|
+
.hide(axis='index')
|
|
73
|
+
.to_html(classes='display', index=False, table_id='tradesTable')
|
|
74
|
+
)
|
|
75
|
+
return table_html
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def safe_format(value, format_str, default_value='N/A'):
|
|
5
|
+
if value is None:
|
|
6
|
+
return default_value
|
|
7
|
+
|
|
8
|
+
if isinstance(value, (int, float)):
|
|
9
|
+
return format_str.format(value)
|
|
10
|
+
return value
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def safe_format_percentage(value, format_str, default_value='N/A'):
|
|
14
|
+
if value is None:
|
|
15
|
+
return default_value
|
|
16
|
+
|
|
17
|
+
if isinstance(value, (int, float)):
|
|
18
|
+
return format_str.format(value * 100)
|
|
19
|
+
|
|
20
|
+
return value
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def safe_format_date(value, format_str, default_value='N/A'):
|
|
24
|
+
if value is None:
|
|
25
|
+
return default_value
|
|
26
|
+
|
|
27
|
+
if isinstance(value, pd.Timestamp):
|
|
28
|
+
return value.strftime(format_str)
|
|
29
|
+
return value
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Aluminium Smelting Strategy Report</title>
|
|
5
|
+
<style>
|
|
6
|
+
body {{ '{' }}
|
|
7
|
+
font-family: Arial, sans-serif;
|
|
8
|
+
margin: 40px;
|
|
9
|
+
{{ '}' }}
|
|
10
|
+
h1 {{ '{' }}
|
|
11
|
+
color: #333;
|
|
12
|
+
text-align: center;
|
|
13
|
+
{{ '}' }}
|
|
14
|
+
.tab {{ '{' }}
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
border-bottom: 1px solid #ccc;
|
|
17
|
+
margin-bottom: 20px;
|
|
18
|
+
{{ '}' }}
|
|
19
|
+
.tab button {{ '{' }}
|
|
20
|
+
background-color: #f1f1f1;
|
|
21
|
+
float: left;
|
|
22
|
+
border: none;
|
|
23
|
+
outline: none;
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
padding: 10px 20px;
|
|
26
|
+
transition: 0.3s;
|
|
27
|
+
font-size: 17px;
|
|
28
|
+
{{ '}' }}
|
|
29
|
+
.tab button:hover {{ '{' }}
|
|
30
|
+
background-color: #ddd;
|
|
31
|
+
{{ '}' }}
|
|
32
|
+
.tab button.active {{ '{' }}
|
|
33
|
+
background-color: #ccc;
|
|
34
|
+
{{ '}' }}
|
|
35
|
+
.tabcontent {{ '{' }}
|
|
36
|
+
display: none;
|
|
37
|
+
{{ '}' }}
|
|
38
|
+
.tabcontent.active {{ '{' }}
|
|
39
|
+
display: block;
|
|
40
|
+
{{ '}' }}
|
|
41
|
+
.stats-table, .metrics-table {{ '{' }}
|
|
42
|
+
border-collapse: collapse;
|
|
43
|
+
width: 50%;
|
|
44
|
+
margin-top: 20px;
|
|
45
|
+
{{ '}' }}
|
|
46
|
+
.stats-table th, .stats-table td {{ '{' }}
|
|
47
|
+
border: 1px solid #ddd;
|
|
48
|
+
padding: 8px;
|
|
49
|
+
text-align: left;
|
|
50
|
+
{{ '}' }}
|
|
51
|
+
.stats-table th {{ '{' }}
|
|
52
|
+
background-color: #f2f2f2;
|
|
53
|
+
{{ '}' }}
|
|
54
|
+
.grid-container {{ '{' }}
|
|
55
|
+
display: flex;
|
|
56
|
+
gap: 10px;
|
|
57
|
+
align-items: flex-start;
|
|
58
|
+
margin-top: 20px;
|
|
59
|
+
{{ '}' }}
|
|
60
|
+
.heatmap-cell {{ '{' }}
|
|
61
|
+
padding: 5px;
|
|
62
|
+
width: 60%;
|
|
63
|
+
{{ '}' }}
|
|
64
|
+
.yearly-returns-cell {{ '{' }}
|
|
65
|
+
padding: 5px;
|
|
66
|
+
width: 30%;
|
|
67
|
+
{{ '}' }}
|
|
68
|
+
.metrics-table {{ '{' }}
|
|
69
|
+
padding: 5px;
|
|
70
|
+
{{ '}' }}
|
|
71
|
+
</style>
|
|
72
|
+
<script>
|
|
73
|
+
function openTab(evt, tabName) {{ '{' }}
|
|
74
|
+
var i, tabcontent, tablinks;
|
|
75
|
+
tabcontent = document.getElementsByClassName("tabcontent");
|
|
76
|
+
for (i = 0; i < tabcontent.length; i++) {{ '{' }}
|
|
77
|
+
tabcontent[i].classList.remove("active");
|
|
78
|
+
{{ '}' }}
|
|
79
|
+
tablinks = document.getElementsByClassName("tablink");
|
|
80
|
+
for (i = 0; i < tablinks.length; i++) {{ '{' }}
|
|
81
|
+
tablinks[i].classList.remove("active");
|
|
82
|
+
{{ '}' }}
|
|
83
|
+
document.getElementById(tabName).classList.add("active");
|
|
84
|
+
evt.currentTarget.classList.add("active");
|
|
85
|
+
{{ '}' }}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<!-- DataTables CSS -->
|
|
89
|
+
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css">
|
|
90
|
+
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css"/>
|
|
91
|
+
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
|
|
92
|
+
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
|
93
|
+
</head>
|
|
94
|
+
<body>
|
|
95
|
+
<h1>Backtest report: {{ report.name }}</h1>
|
|
96
|
+
|
|
97
|
+
<div class="tab">
|
|
98
|
+
<button class="tablink active" onclick="openTab(event, 'Overview')">Overview</button>
|
|
99
|
+
<button class="tablink" onclick="openTab(event, 'Trades')">Trades</button>
|
|
100
|
+
<button class="tablink" onclick="openTab(event, 'Positions and orders')">Positions and Orders</button>
|
|
101
|
+
<button class="tablink" onclick="openTab(event, 'DataAnalysis')">Data Analysis</button>
|
|
102
|
+
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div id="Overview" class="tabcontent active">
|
|
106
|
+
<div>
|
|
107
|
+
{{ equity_with_drawdown_plot_html | safe }}
|
|
108
|
+
</div>
|
|
109
|
+
<div>
|
|
110
|
+
{{ rolling_sharpe_ratio_plot_html | safe }}
|
|
111
|
+
</div>
|
|
112
|
+
<div class="grid-container">
|
|
113
|
+
<div class="heatmap-cell">
|
|
114
|
+
{{ monthly_returns_heatmap_html | safe }}
|
|
115
|
+
</div>
|
|
116
|
+
<div class="yearly-returns-cell">
|
|
117
|
+
{{ yearly_returns_histogram_html | safe }}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
<div class="grid-container">
|
|
121
|
+
<div class="metrics-table">
|
|
122
|
+
<h2>Time Metrics</h2>
|
|
123
|
+
{{ time_metrics_table_html | safe }}
|
|
124
|
+
</div>
|
|
125
|
+
<div class="metrics-table">
|
|
126
|
+
<h2>Performance Metrics</h2>
|
|
127
|
+
{{ key_metrics_table_html | safe }}
|
|
128
|
+
</div>
|
|
129
|
+
<div class="metrics-table">
|
|
130
|
+
<h2>Trade Metrics</h2>
|
|
131
|
+
{{ trades_metrics_table_html | safe }}
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
<div id="Trades" class="tabcontent">
|
|
136
|
+
{{ trades_table_html | safe }}
|
|
137
|
+
{% raw %}
|
|
138
|
+
<script>
|
|
139
|
+
$(document).ready(function() {
|
|
140
|
+
$('.stats-table').DataTable({
|
|
141
|
+
paging: true,
|
|
142
|
+
pageLength: 10,
|
|
143
|
+
searching: true
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
</script>
|
|
147
|
+
{% endraw %}
|
|
148
|
+
</div>
|
|
149
|
+
<div id="DataAnalysis" class="tabcontent">
|
|
150
|
+
<h2>OHLCV Data Completeness Charts</h2>
|
|
151
|
+
{{ data_completeness_charts_html | safe }}
|
|
152
|
+
</div>
|
|
153
|
+
</body>
|
|
154
|
+
</html>
|
|
@@ -54,8 +54,10 @@ class ActionHandler:
|
|
|
54
54
|
action_handler.set_strategy(payload)
|
|
55
55
|
return action_handler
|
|
56
56
|
|
|
57
|
-
def handle(self, payload,
|
|
58
|
-
return self.strategy.handle_event(
|
|
57
|
+
def handle(self, payload, context, strategy_orchestrator_service):
|
|
58
|
+
return self.strategy.handle_event(
|
|
59
|
+
payload, context, strategy_orchestrator_service
|
|
60
|
+
)
|
|
59
61
|
|
|
60
62
|
def set_strategy(self, payload):
|
|
61
63
|
action = ActionHandler.get_action_type(payload)
|
|
@@ -70,7 +72,8 @@ class ActionHandler:
|
|
|
70
72
|
@staticmethod
|
|
71
73
|
def get_action_type(payload):
|
|
72
74
|
|
|
73
|
-
if payload is None or
|
|
75
|
+
if payload is None or \
|
|
76
|
+
("ACTION" not in payload and "action" not in payload):
|
|
74
77
|
raise OperationalException("Action type is not defined")
|
|
75
78
|
|
|
76
79
|
if "action" in payload:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
|
|
2
3
|
from investing_algorithm_framework.app.stateless.action_handlers \
|
|
3
4
|
.action_handler_strategy import ActionHandlerStrategy
|
|
4
5
|
|
|
@@ -6,7 +7,7 @@ from investing_algorithm_framework.app.stateless.action_handlers \
|
|
|
6
7
|
class CheckOnlineHandler(ActionHandlerStrategy):
|
|
7
8
|
MESSAGE = {"message": "online"}
|
|
8
9
|
|
|
9
|
-
def handle_event(self, payload,
|
|
10
|
+
def handle_event(self, payload, context, strategy_orchestrator_service):
|
|
10
11
|
return {
|
|
11
12
|
"statusCode": 200,
|
|
12
13
|
"headers": {"Content-Type": "application/json"},
|
|
@@ -5,24 +5,31 @@ from investing_algorithm_framework.app.stateless.action_handlers \
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class RunStrategyHandler(ActionHandlerStrategy):
|
|
8
|
+
"""
|
|
9
|
+
RunStrategyHandler is an action handler that runs a strategy and its tasks
|
|
10
|
+
synchronously.
|
|
11
|
+
|
|
12
|
+
If the run was successful, it returns a 200 OK response with a message
|
|
13
|
+
"OK".
|
|
14
|
+
"""
|
|
8
15
|
MESSAGE = {"message": "Ok"}
|
|
9
16
|
|
|
10
|
-
def handle_event(self, payload,
|
|
11
|
-
strategies =
|
|
17
|
+
def handle_event(self, payload, context, strategy_orchestrator_service):
|
|
18
|
+
strategies = strategy_orchestrator_service\
|
|
12
19
|
.get_strategies(payload.get("strategies", None))
|
|
13
|
-
tasks =
|
|
20
|
+
tasks = strategy_orchestrator_service.get_tasks()
|
|
14
21
|
|
|
15
22
|
for strategy in strategies:
|
|
16
|
-
|
|
23
|
+
strategy_orchestrator_service.run_strategy(
|
|
17
24
|
strategy=strategy,
|
|
18
|
-
|
|
25
|
+
context=context,
|
|
19
26
|
sync=True
|
|
20
27
|
)
|
|
21
28
|
|
|
22
29
|
for task in tasks:
|
|
23
|
-
|
|
30
|
+
strategy_orchestrator_service.run_task(
|
|
24
31
|
task=task,
|
|
25
|
-
|
|
32
|
+
context=context,
|
|
26
33
|
sync=True
|
|
27
34
|
)
|
|
28
35
|
|