investing-algorithm-framework 7.19.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of investing-algorithm-framework might be problematic. Click here for more details.
- investing_algorithm_framework/__init__.py +197 -0
- investing_algorithm_framework/app/__init__.py +47 -0
- investing_algorithm_framework/app/algorithm/__init__.py +7 -0
- investing_algorithm_framework/app/algorithm/algorithm.py +239 -0
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +114 -0
- investing_algorithm_framework/app/analysis/__init__.py +15 -0
- investing_algorithm_framework/app/analysis/backtest_data_ranges.py +121 -0
- investing_algorithm_framework/app/analysis/backtest_utils.py +107 -0
- investing_algorithm_framework/app/analysis/permutation.py +116 -0
- investing_algorithm_framework/app/analysis/ranking.py +297 -0
- investing_algorithm_framework/app/app.py +2204 -0
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1667 -0
- investing_algorithm_framework/app/eventloop.py +590 -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/stop_loss_table.py +0 -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/__init__.py +35 -0
- investing_algorithm_framework/app/stateless/action_handlers/__init__.py +84 -0
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +8 -0
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +15 -0
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +40 -0
- investing_algorithm_framework/app/stateless/exception_handler.py +40 -0
- investing_algorithm_framework/app/strategy.py +675 -0
- investing_algorithm_framework/app/task.py +41 -0
- investing_algorithm_framework/app/web/__init__.py +5 -0
- investing_algorithm_framework/app/web/controllers/__init__.py +13 -0
- investing_algorithm_framework/app/web/controllers/orders.py +20 -0
- investing_algorithm_framework/app/web/controllers/portfolio.py +20 -0
- investing_algorithm_framework/app/web/controllers/positions.py +18 -0
- investing_algorithm_framework/app/web/create_app.py +20 -0
- investing_algorithm_framework/app/web/error_handler.py +59 -0
- investing_algorithm_framework/app/web/responses.py +20 -0
- investing_algorithm_framework/app/web/run_strategies.py +4 -0
- investing_algorithm_framework/app/web/schemas/__init__.py +12 -0
- investing_algorithm_framework/app/web/schemas/order.py +12 -0
- investing_algorithm_framework/app/web/schemas/portfolio.py +22 -0
- investing_algorithm_framework/app/web/schemas/position.py +15 -0
- investing_algorithm_framework/app/web/setup_cors.py +6 -0
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +207 -0
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +499 -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/create_app.py +54 -0
- investing_algorithm_framework/dependency_container.py +155 -0
- investing_algorithm_framework/domain/__init__.py +148 -0
- investing_algorithm_framework/domain/backtesting/__init__.py +21 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +503 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +96 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +242 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +459 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +435 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +280 -0
- investing_algorithm_framework/domain/config.py +111 -0
- investing_algorithm_framework/domain/constants.py +83 -0
- investing_algorithm_framework/domain/data_provider.py +334 -0
- investing_algorithm_framework/domain/data_structures.py +42 -0
- investing_algorithm_framework/domain/decimal_parsing.py +40 -0
- investing_algorithm_framework/domain/exceptions.py +112 -0
- investing_algorithm_framework/domain/models/__init__.py +43 -0
- investing_algorithm_framework/domain/models/app_mode.py +34 -0
- investing_algorithm_framework/domain/models/base_model.py +25 -0
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +214 -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 +6 -0
- investing_algorithm_framework/domain/models/order/order.py +384 -0
- investing_algorithm_framework/domain/models/order/order_side.py +36 -0
- investing_algorithm_framework/domain/models/order/order_status.py +37 -0
- investing_algorithm_framework/domain/models/order/order_type.py +30 -0
- investing_algorithm_framework/domain/models/portfolio/__init__.py +9 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +169 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +93 -0
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
- investing_algorithm_framework/domain/models/position/__init__.py +4 -0
- investing_algorithm_framework/domain/models/position/position.py +68 -0
- investing_algorithm_framework/domain/models/position/position_snapshot.py +47 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
- investing_algorithm_framework/domain/models/strategy_profile.py +33 -0
- investing_algorithm_framework/domain/models/time_frame.py +153 -0
- investing_algorithm_framework/domain/models/time_interval.py +124 -0
- investing_algorithm_framework/domain/models/time_unit.py +149 -0
- 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 +13 -0
- investing_algorithm_framework/domain/models/trade/trade.py +388 -0
- investing_algorithm_framework/domain/models/trade/trade_risk_type.py +34 -0
- investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +267 -0
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +303 -0
- investing_algorithm_framework/domain/order_executor.py +112 -0
- investing_algorithm_framework/domain/portfolio_provider.py +118 -0
- investing_algorithm_framework/domain/positions/__init__.py +4 -0
- investing_algorithm_framework/domain/positions/position_size.py +41 -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/stateless_actions.py +7 -0
- investing_algorithm_framework/domain/strategy.py +44 -0
- investing_algorithm_framework/domain/utils/__init__.py +27 -0
- investing_algorithm_framework/domain/utils/csv.py +104 -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 +41 -0
- investing_algorithm_framework/domain/utils/signatures.py +17 -0
- investing_algorithm_framework/domain/utils/stoppable_thread.py +26 -0
- investing_algorithm_framework/domain/utils/synchronized.py +12 -0
- investing_algorithm_framework/download_data.py +108 -0
- investing_algorithm_framework/infrastructure/__init__.py +50 -0
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1143 -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 +10 -0
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +120 -0
- investing_algorithm_framework/infrastructure/models/__init__.py +16 -0
- investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
- investing_algorithm_framework/infrastructure/models/model_extension.py +6 -0
- investing_algorithm_framework/infrastructure/models/order/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/order/order.py +124 -0
- 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 +4 -0
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
- investing_algorithm_framework/infrastructure/models/portfolio/sql_portfolio.py +114 -0
- investing_algorithm_framework/infrastructure/models/position/__init__.py +4 -0
- investing_algorithm_framework/infrastructure/models/position/position.py +63 -0
- investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +23 -0
- 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 +40 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +41 -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 +21 -0
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +96 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +30 -0
- investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +66 -0
- investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +299 -0
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +23 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +23 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +7 -0
- investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
- investing_algorithm_framework/infrastructure/services/aws/state_handler.py +113 -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/services/__init__.py +132 -0
- investing_algorithm_framework/services/backtesting/__init__.py +5 -0
- investing_algorithm_framework/services/backtesting/backtest_service.py +651 -0
- investing_algorithm_framework/services/configuration_service.py +96 -0
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +850 -0
- investing_algorithm_framework/services/market_credential_service.py +40 -0
- investing_algorithm_framework/services/metrics/__init__.py +114 -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 +181 -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 +83 -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 +157 -0
- investing_algorithm_framework/services/metrics/trades.py +500 -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 +97 -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/portfolios/portfolio_configuration_service.py +75 -0
- 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/positions/position_snapshot_service.py +18 -0
- investing_algorithm_framework/services/repository_service.py +40 -0
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +132 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +66 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +41 -0
- investing_algorithm_framework/services/trade_service/__init__.py +3 -0
- investing_algorithm_framework/services/trade_service/trade_service.py +1083 -0
- investing_algorithm_framework-7.19.14.dist-info/LICENSE +201 -0
- investing_algorithm_framework-7.19.14.dist-info/METADATA +459 -0
- investing_algorithm_framework-7.19.14.dist-info/RECORD +260 -0
- investing_algorithm_framework-7.19.14.dist-info/WHEEL +4 -0
- investing_algorithm_framework-7.19.14.dist-info/entry_points.txt +3 -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>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from investing_algorithm_framework.app.stateless.action_handlers \
|
|
2
|
+
import ActionHandler
|
|
3
|
+
from investing_algorithm_framework.app.stateless.action_handlers import \
|
|
4
|
+
StatelessAction
|
|
5
|
+
from investing_algorithm_framework.app.stateless.exception_handler import \
|
|
6
|
+
handle_exception
|
|
7
|
+
from investing_algorithm_framework.domain.exceptions import \
|
|
8
|
+
OperationalException
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StatelessHandler:
|
|
12
|
+
|
|
13
|
+
def handler(self, payload, algorithm):
|
|
14
|
+
action = StatelessHandler.get_action_type(payload)
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
# Handle the action
|
|
18
|
+
action_handler = ActionHandler.of(StatelessAction
|
|
19
|
+
.from_string(action))
|
|
20
|
+
return action_handler.handle(payload)
|
|
21
|
+
except Exception as e:
|
|
22
|
+
return handle_exception(e)
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def get_action_type(payload):
|
|
26
|
+
|
|
27
|
+
if "action" in payload:
|
|
28
|
+
action = payload["action"]
|
|
29
|
+
else:
|
|
30
|
+
action = payload["ACTION"]
|
|
31
|
+
|
|
32
|
+
if action is None:
|
|
33
|
+
raise OperationalException("Action type not supported")
|
|
34
|
+
|
|
35
|
+
return action
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from investing_algorithm_framework.app.stateless.action_handlers \
|
|
4
|
+
.check_online_handler import CheckOnlineHandler
|
|
5
|
+
from investing_algorithm_framework.app.stateless.action_handlers \
|
|
6
|
+
.run_strategy_handler import RunStrategyHandler
|
|
7
|
+
from investing_algorithm_framework.domain.exceptions import \
|
|
8
|
+
OperationalException
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StatelessAction(Enum):
|
|
12
|
+
RUN_STRATEGY = 'RUN_STRATEGY'
|
|
13
|
+
PING = "PING"
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def from_value(value: str):
|
|
17
|
+
if isinstance(value, StatelessAction):
|
|
18
|
+
for action in StatelessAction:
|
|
19
|
+
|
|
20
|
+
if value == action:
|
|
21
|
+
return action
|
|
22
|
+
|
|
23
|
+
if isinstance(value, str):
|
|
24
|
+
return StatelessAction.from_string(value)
|
|
25
|
+
|
|
26
|
+
raise ValueError("Could not convert value to stateless action")
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def from_string(value: str):
|
|
30
|
+
|
|
31
|
+
if isinstance(value, str):
|
|
32
|
+
for action in StatelessAction:
|
|
33
|
+
|
|
34
|
+
if value.upper() == action.value:
|
|
35
|
+
return action
|
|
36
|
+
|
|
37
|
+
raise ValueError("Could not convert value to stateless action")
|
|
38
|
+
|
|
39
|
+
def equals(self, other):
|
|
40
|
+
|
|
41
|
+
if isinstance(other, Enum):
|
|
42
|
+
return self.value == other.value
|
|
43
|
+
|
|
44
|
+
else:
|
|
45
|
+
return StatelessAction.from_string(other) == self
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ActionHandler:
|
|
49
|
+
strategy = None
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def of(payload: dict):
|
|
53
|
+
action_handler = ActionHandler()
|
|
54
|
+
action_handler.set_strategy(payload)
|
|
55
|
+
return action_handler
|
|
56
|
+
|
|
57
|
+
def handle(self, payload, context, strategy_orchestrator_service):
|
|
58
|
+
return self.strategy.handle_event(
|
|
59
|
+
payload, context, strategy_orchestrator_service
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def set_strategy(self, payload):
|
|
63
|
+
action = ActionHandler.get_action_type(payload)
|
|
64
|
+
|
|
65
|
+
if StatelessAction.RUN_STRATEGY.equals(action):
|
|
66
|
+
self.strategy = RunStrategyHandler()
|
|
67
|
+
elif StatelessAction.PING.equals(action):
|
|
68
|
+
self.strategy = CheckOnlineHandler()
|
|
69
|
+
else:
|
|
70
|
+
raise OperationalException("Action not supported")
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def get_action_type(payload):
|
|
74
|
+
|
|
75
|
+
if payload is None or \
|
|
76
|
+
("ACTION" not in payload and "action" not in payload):
|
|
77
|
+
raise OperationalException("Action type is not defined")
|
|
78
|
+
|
|
79
|
+
if "action" in payload:
|
|
80
|
+
action = payload["action"]
|
|
81
|
+
else:
|
|
82
|
+
action = payload["ACTION"]
|
|
83
|
+
|
|
84
|
+
return action
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from investing_algorithm_framework.app.stateless.action_handlers \
|
|
4
|
+
.action_handler_strategy import ActionHandlerStrategy
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CheckOnlineHandler(ActionHandlerStrategy):
|
|
8
|
+
MESSAGE = {"message": "online"}
|
|
9
|
+
|
|
10
|
+
def handle_event(self, payload, context, strategy_orchestrator_service):
|
|
11
|
+
return {
|
|
12
|
+
"statusCode": 200,
|
|
13
|
+
"headers": {"Content-Type": "application/json"},
|
|
14
|
+
"body": json.dumps(CheckOnlineHandler.MESSAGE)
|
|
15
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from investing_algorithm_framework.app.stateless.action_handlers \
|
|
4
|
+
.action_handler_strategy import ActionHandlerStrategy
|
|
5
|
+
|
|
6
|
+
|
|
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
|
+
"""
|
|
15
|
+
MESSAGE = {"message": "Ok"}
|
|
16
|
+
|
|
17
|
+
def handle_event(self, payload, context, strategy_orchestrator_service):
|
|
18
|
+
strategies = strategy_orchestrator_service\
|
|
19
|
+
.get_strategies(payload.get("strategies", None))
|
|
20
|
+
tasks = strategy_orchestrator_service.get_tasks()
|
|
21
|
+
|
|
22
|
+
for strategy in strategies:
|
|
23
|
+
strategy_orchestrator_service.run_strategy(
|
|
24
|
+
strategy=strategy,
|
|
25
|
+
context=context,
|
|
26
|
+
sync=True
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
for task in tasks:
|
|
30
|
+
strategy_orchestrator_service.run_task(
|
|
31
|
+
task=task,
|
|
32
|
+
context=context,
|
|
33
|
+
sync=True
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
"statusCode": 200,
|
|
38
|
+
"headers": {"Content-Type": "application/json"},
|
|
39
|
+
"body": json.dumps({"message": RunStrategyHandler.MESSAGE})
|
|
40
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Dict, List
|
|
4
|
+
|
|
5
|
+
from investing_algorithm_framework.domain import OperationalException
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger("investing_algorithm_framework")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_error_response(error_message, status_code: int = 400):
|
|
11
|
+
response = json.dumps({"error_message": error_message})
|
|
12
|
+
return response, status_code
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def format_marshmallow_validation_error(errors):
|
|
16
|
+
errors_message = {}
|
|
17
|
+
|
|
18
|
+
for key in errors:
|
|
19
|
+
|
|
20
|
+
if isinstance(errors[key], Dict):
|
|
21
|
+
errors_message[key] = \
|
|
22
|
+
format_marshmallow_validation_error(errors[key])
|
|
23
|
+
|
|
24
|
+
if isinstance(errors[key], List):
|
|
25
|
+
errors_message[key] = errors[key][0].lower()
|
|
26
|
+
return errors_message
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def handle_exception(error):
|
|
30
|
+
logger.error("exception of type {} occurred".format(type(error)))
|
|
31
|
+
logger.exception(error)
|
|
32
|
+
|
|
33
|
+
if isinstance(error, OperationalException):
|
|
34
|
+
return error.to_response()
|
|
35
|
+
else:
|
|
36
|
+
# Internal error happened that was unknown
|
|
37
|
+
return {
|
|
38
|
+
"status": "error",
|
|
39
|
+
"message": str(error)
|
|
40
|
+
}
|