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,921 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from tabulate import tabulate
|
|
6
|
+
|
|
7
|
+
from investing_algorithm_framework.domain import DATETIME_FORMAT, \
|
|
8
|
+
BacktestDateRange, TradeStatus, OrderSide, TradeRiskType, Backtest
|
|
9
|
+
from investing_algorithm_framework.domain.constants import \
|
|
10
|
+
DATETIME_FORMAT_BACKTESTING
|
|
11
|
+
|
|
12
|
+
COLOR_RED = '\033[91m'
|
|
13
|
+
COLOR_PURPLE = '\033[95m'
|
|
14
|
+
COLOR_RESET = '\033[0m'
|
|
15
|
+
COLOR_GREEN = '\033[92m'
|
|
16
|
+
COLOR_YELLOW = '\033[93m'
|
|
17
|
+
BACKTEST_REPORT_FILE_NAME_PATTERN = (
|
|
18
|
+
r"^report_\w+_backtest-start-date_\d{4}-\d{2}-\d{2}:\d{2}:\d{2}_"
|
|
19
|
+
r"backtest-end-date_\d{4}-\d{2}-\d{2}:\d{2}:\d{2}_"
|
|
20
|
+
r"created-at_\d{4}-\d{2}-\d{2}:\d{2}:\d{2}\.json$"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def format_date(date) -> str:
|
|
25
|
+
"""
|
|
26
|
+
Format the date to the format YYYY-MM-DD HH:MM:SS
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
if date is None:
|
|
30
|
+
return ""
|
|
31
|
+
|
|
32
|
+
if isinstance(date, datetime):
|
|
33
|
+
return date.strftime("%Y-%m-%d %H:%M")
|
|
34
|
+
else:
|
|
35
|
+
date = datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
|
|
36
|
+
return date.strftime("%Y-%m-%d %H:%M")
|
|
37
|
+
|
|
38
|
+
def is_positive(number) -> bool:
|
|
39
|
+
"""
|
|
40
|
+
Check if a number is positive.
|
|
41
|
+
|
|
42
|
+
param number: The number
|
|
43
|
+
:return: True if the number is positive, False otherwise
|
|
44
|
+
"""
|
|
45
|
+
number = float(number)
|
|
46
|
+
return number > 0
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def pretty_print_profit_evaluation(
|
|
50
|
+
reports, price_precision=2, percentage_precision=4
|
|
51
|
+
):
|
|
52
|
+
profit_table = {}
|
|
53
|
+
profit_table["Algorithm name"] = [
|
|
54
|
+
report.name for report in reports
|
|
55
|
+
]
|
|
56
|
+
profit_table["Profit"] = [
|
|
57
|
+
f"{float(report.total_net_gain):.{price_precision}f} {report.trading_symbol}"
|
|
58
|
+
for report in reports
|
|
59
|
+
]
|
|
60
|
+
profit_table["Profit percentage"] = [
|
|
61
|
+
f"{float(report.total_net_gain_percentage):.{percentage_precision}f}%" for report in reports
|
|
62
|
+
]
|
|
63
|
+
profit_table["Percentage positive trades"] = [
|
|
64
|
+
f"{float(report.percentage_positive_trades):.{0}f}%"
|
|
65
|
+
for report in reports
|
|
66
|
+
]
|
|
67
|
+
profit_table["Date range"] = [
|
|
68
|
+
f"{report.backtest_date_range.name} {report.backtest_date_range.start_date} - {report.backtest_date_range.end_date}"
|
|
69
|
+
for report in reports
|
|
70
|
+
]
|
|
71
|
+
profit_table["Total value"] = [
|
|
72
|
+
f"{float(report.total_value):.{price_precision}f}" for report in reports
|
|
73
|
+
]
|
|
74
|
+
print(tabulate(profit_table, headers="keys", tablefmt="rounded_grid"))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def pretty_print_growth_evaluation(
|
|
78
|
+
reports,
|
|
79
|
+
price_precision=2,
|
|
80
|
+
percentage_precision=4
|
|
81
|
+
):
|
|
82
|
+
growth_table = {}
|
|
83
|
+
growth_table["Algorithm name"] = [
|
|
84
|
+
report.name for report in reports
|
|
85
|
+
]
|
|
86
|
+
growth_table["Growth"] = [
|
|
87
|
+
f"{float(report.growth):.{price_precision}f} {report.trading_symbol}" for report in reports
|
|
88
|
+
]
|
|
89
|
+
growth_table["Growth percentage"] = [
|
|
90
|
+
f"{float(report.growth_percentage):.{percentage_precision}f}%" for report in reports
|
|
91
|
+
]
|
|
92
|
+
growth_table["Percentage positive trades"] = [
|
|
93
|
+
f"{float(report.percentage_positive_trades):.{0}f}%"
|
|
94
|
+
for report in reports
|
|
95
|
+
]
|
|
96
|
+
growth_table["Date range"] = [
|
|
97
|
+
f"{report.backtest_date_range.name} {report.backtest_date_range.start_date} - {report.backtest_date_range.end_date}"
|
|
98
|
+
for report in reports
|
|
99
|
+
]
|
|
100
|
+
growth_table["Total value"] = [
|
|
101
|
+
f"{float(report.total_value):.{price_precision}f}" for report in reports
|
|
102
|
+
]
|
|
103
|
+
print(
|
|
104
|
+
tabulate(growth_table, headers="keys", tablefmt="rounded_grid")
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def pretty_print_stop_losses(
|
|
108
|
+
backtest,
|
|
109
|
+
backtest_date_range: BacktestDateRange = None,
|
|
110
|
+
triggered_only=False,
|
|
111
|
+
amount_precision=4,
|
|
112
|
+
price_precision=2,
|
|
113
|
+
time_precision=1,
|
|
114
|
+
percentage_precision=0
|
|
115
|
+
):
|
|
116
|
+
print(f"{COLOR_YELLOW}Stop losses overview{COLOR_RESET}")
|
|
117
|
+
stop_loss_table = {}
|
|
118
|
+
results = backtest.get_backtest_run(backtest_date_range)
|
|
119
|
+
trades = results.get_trades()
|
|
120
|
+
selection = []
|
|
121
|
+
|
|
122
|
+
def get_sold_amount(stop_loss):
|
|
123
|
+
if stop_loss["sold_amount"] > 0:
|
|
124
|
+
return f"{float(stop_loss['sold_amount']):.{amount_precision}f} {stop_loss['target_symbol']}"
|
|
125
|
+
|
|
126
|
+
return ""
|
|
127
|
+
|
|
128
|
+
def get_status(stop_loss):
|
|
129
|
+
|
|
130
|
+
if stop_loss.sold_amount == 0:
|
|
131
|
+
return "NOT TRIGGERED"
|
|
132
|
+
|
|
133
|
+
if stop_loss.sold_amount == stop_loss.sell_amount:
|
|
134
|
+
return "TRIGGERED"
|
|
135
|
+
|
|
136
|
+
if stop_loss.sold_amount < stop_loss.sell_amount:
|
|
137
|
+
return "PARTIALLY TRIGGERED"
|
|
138
|
+
|
|
139
|
+
def get_high_water_mark(stop_loss):
|
|
140
|
+
if stop_loss["high_water_mark"] is not None:
|
|
141
|
+
return f"{float(stop_loss['high_water_mark']):.{price_precision}f} {stop_loss['trading_symbol']} {format_date(stop_loss['high_water_mark_date'])}"
|
|
142
|
+
|
|
143
|
+
return ""
|
|
144
|
+
|
|
145
|
+
def get_stop_loss_price(take_profit):
|
|
146
|
+
|
|
147
|
+
if TradeRiskType.TRAILING.equals(take_profit["trade_risk_type"]):
|
|
148
|
+
initial_price = take_profit["open_price"]
|
|
149
|
+
percentage = take_profit["percentage"]
|
|
150
|
+
initial_stop_loss_price = \
|
|
151
|
+
initial_price * (1 - (percentage / 100))
|
|
152
|
+
return f"{float(take_profit['stop_loss_price']):.{price_precision}f} ({(initial_stop_loss_price):.{price_precision}f}) ({take_profit['percentage']})% {take_profit['trading_symbol']}"
|
|
153
|
+
else:
|
|
154
|
+
return f"{float(take_profit['stop_loss_price']):.{price_precision}f}({take_profit['percentage']})% {take_profit['trading_symbol']}"
|
|
155
|
+
|
|
156
|
+
if triggered_only:
|
|
157
|
+
for trade in trades:
|
|
158
|
+
|
|
159
|
+
if trade.stop_losses is not None:
|
|
160
|
+
selection += [
|
|
161
|
+
{
|
|
162
|
+
"symbol": trade.symbol,
|
|
163
|
+
"target_symbol": trade.target_symbol,
|
|
164
|
+
"trading_symbol": trade.trading_symbol,
|
|
165
|
+
"status": get_status(stop_loss),
|
|
166
|
+
"trade_id": stop_loss.trade_id,
|
|
167
|
+
"trade_risk_type": stop_loss.trade_risk_type,
|
|
168
|
+
"percentage": stop_loss.percentage,
|
|
169
|
+
"open_price": stop_loss.open_price,
|
|
170
|
+
"sell_percentage": stop_loss.sell_percentage,
|
|
171
|
+
"high_water_mark": stop_loss.high_water_mark,
|
|
172
|
+
"high_water_mark_date": stop_loss.high_water_mark_date,
|
|
173
|
+
"stop_loss_price": stop_loss.stop_loss_price,
|
|
174
|
+
"sell_amount": stop_loss.sell_amount,
|
|
175
|
+
"sold_amount": stop_loss.sold_amount,
|
|
176
|
+
"active": stop_loss.active,
|
|
177
|
+
"sell_prices": stop_loss.sell_prices
|
|
178
|
+
} for stop_loss in trade.stop_losses if stop_loss.sold_amount > 0
|
|
179
|
+
]
|
|
180
|
+
else:
|
|
181
|
+
for trade in trades:
|
|
182
|
+
|
|
183
|
+
if trade.stop_losses is not None:
|
|
184
|
+
for stop_loss in trade.stop_losses:
|
|
185
|
+
data = {
|
|
186
|
+
"symbol": trade.symbol,
|
|
187
|
+
"target_symbol": trade.target_symbol,
|
|
188
|
+
"trading_symbol": trade.trading_symbol,
|
|
189
|
+
"status": get_status(stop_loss),
|
|
190
|
+
"trade_id": stop_loss.trade_id,
|
|
191
|
+
"trade_risk_type": stop_loss.trade_risk_type,
|
|
192
|
+
"percentage": stop_loss.percentage,
|
|
193
|
+
"open_price": stop_loss.open_price,
|
|
194
|
+
"sell_percentage": stop_loss.sell_percentage,
|
|
195
|
+
"stop_loss_price": stop_loss.stop_loss_price,
|
|
196
|
+
"sell_amount": stop_loss.sell_amount,
|
|
197
|
+
"sold_amount": stop_loss.sold_amount,
|
|
198
|
+
"active": stop_loss.active,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if hasattr(stop_loss, "sell_prices"):
|
|
202
|
+
data["sell_prices"] = stop_loss.sell_prices
|
|
203
|
+
else:
|
|
204
|
+
data["sell_prices"] = None
|
|
205
|
+
|
|
206
|
+
if hasattr(stop_loss, "high_water_mark"):
|
|
207
|
+
data["high_water_mark"] = stop_loss.high_water_mark
|
|
208
|
+
|
|
209
|
+
if hasattr(stop_loss, "high_water_mark_date"):
|
|
210
|
+
data["high_water_mark_date"] = \
|
|
211
|
+
stop_loss.high_water_mark_date
|
|
212
|
+
else:
|
|
213
|
+
data["high_water_mark_date"] = None
|
|
214
|
+
else:
|
|
215
|
+
data["high_water_mark"] = None
|
|
216
|
+
data["high_water_mark_date"] = None
|
|
217
|
+
selection.append(data)
|
|
218
|
+
|
|
219
|
+
stop_loss_table["Trade (Trade id)"] = [
|
|
220
|
+
f"{stop_loss['symbol'] + ' (' + str(stop_loss['trade_id']) + ')'}"
|
|
221
|
+
for stop_loss in selection
|
|
222
|
+
]
|
|
223
|
+
stop_loss_table["Status"] = [
|
|
224
|
+
f"{stop_loss['status']}"
|
|
225
|
+
for stop_loss in selection
|
|
226
|
+
]
|
|
227
|
+
stop_loss_table["Active"] = [
|
|
228
|
+
f"{stop_loss['active']}"
|
|
229
|
+
for stop_loss in selection
|
|
230
|
+
]
|
|
231
|
+
stop_loss_table["Type"] = [
|
|
232
|
+
f"{stop_loss['trade_risk_type']}" for stop_loss in selection
|
|
233
|
+
]
|
|
234
|
+
stop_loss_table["Stop Loss (Initial Stop Loss)"] = [
|
|
235
|
+
get_stop_loss_price(stop_loss) for stop_loss in selection
|
|
236
|
+
]
|
|
237
|
+
stop_loss_table["Open price"] = [
|
|
238
|
+
f"{float(stop_loss['open_price']):.{price_precision}f} {stop_loss['trading_symbol']}" for stop_loss in selection if stop_loss['open_price'] is not None
|
|
239
|
+
]
|
|
240
|
+
stop_loss_table["Sell price's"] = [
|
|
241
|
+
f"{stop_loss['sell_prices']}" for stop_loss in selection if stop_loss['sell_prices'] is not None
|
|
242
|
+
]
|
|
243
|
+
stop_loss_table["High water mark"] = [
|
|
244
|
+
f"{get_high_water_mark(stop_loss)}" for stop_loss in selection
|
|
245
|
+
]
|
|
246
|
+
stop_loss_table["Percentage"] = [
|
|
247
|
+
f"{float(stop_loss['sell_percentage'])}%" for stop_loss in selection
|
|
248
|
+
]
|
|
249
|
+
stop_loss_table["Size"] = [
|
|
250
|
+
f"{float(stop_loss['sell_amount']):.{price_precision}f} {stop_loss['target_symbol']}" for stop_loss in selection
|
|
251
|
+
]
|
|
252
|
+
stop_loss_table["Sold amount"] = [
|
|
253
|
+
get_sold_amount(stop_loss) for stop_loss in selection
|
|
254
|
+
]
|
|
255
|
+
print(tabulate(stop_loss_table, headers="keys", tablefmt="rounded_grid"))
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def pretty_print_take_profits(
|
|
259
|
+
backtest: Backtest,
|
|
260
|
+
backtest_date_range: BacktestDateRange = None,
|
|
261
|
+
triggered_only=False,
|
|
262
|
+
amount_precision=4,
|
|
263
|
+
price_precision=2,
|
|
264
|
+
time_precision=1,
|
|
265
|
+
percentage_precision=0
|
|
266
|
+
):
|
|
267
|
+
print(f"{COLOR_YELLOW}Take profits overview{COLOR_RESET}")
|
|
268
|
+
take_profit_table = {}
|
|
269
|
+
results = backtest.get_backtest_run(backtest_date_range)
|
|
270
|
+
trades = results.get_trades()
|
|
271
|
+
selection = []
|
|
272
|
+
|
|
273
|
+
def get_high_water_mark(take_profit):
|
|
274
|
+
if take_profit["high_water_mark"] is not None:
|
|
275
|
+
return f"{float(take_profit['high_water_mark']):.{price_precision}f} {take_profit['trading_symbol']} ({format_date(take_profit['high_water_mark_date'])})"
|
|
276
|
+
|
|
277
|
+
return ""
|
|
278
|
+
|
|
279
|
+
def get_sold_amount(take_profit):
|
|
280
|
+
if take_profit["sold_amount"] > 0:
|
|
281
|
+
return f"{float(take_profit['sold_amount']):.{amount_precision}f} {take_profit['target_symbol']}"
|
|
282
|
+
|
|
283
|
+
return ""
|
|
284
|
+
|
|
285
|
+
def get_take_profit_price(take_profit):
|
|
286
|
+
|
|
287
|
+
if TradeRiskType.TRAILING.equals(take_profit["trade_risk_type"]):
|
|
288
|
+
initial_price = take_profit["open_price"]
|
|
289
|
+
percentage = take_profit["percentage"]
|
|
290
|
+
initial_take_profit_price = \
|
|
291
|
+
initial_price * (1 + (percentage / 100))
|
|
292
|
+
return f"{float(take_profit['take_profit_price']):.{price_precision}f} ({(initial_take_profit_price):.{price_precision}f}) ({take_profit['percentage']})% {take_profit['trading_symbol']}"
|
|
293
|
+
else:
|
|
294
|
+
return f"{float(take_profit['take_profit_price']):.{price_precision}f}({take_profit['percentage']})% {take_profit['trading_symbol']}"
|
|
295
|
+
|
|
296
|
+
def get_status(take_profit):
|
|
297
|
+
|
|
298
|
+
if take_profit.sold_amount == 0:
|
|
299
|
+
return "NOT TRIGGERED"
|
|
300
|
+
|
|
301
|
+
if take_profit.sold_amount == take_profit.sell_amount:
|
|
302
|
+
return "TRIGGERED"
|
|
303
|
+
|
|
304
|
+
if take_profit.sold_amount < take_profit.sell_amount:
|
|
305
|
+
return "PARTIALLY TRIGGERED"
|
|
306
|
+
|
|
307
|
+
if triggered_only:
|
|
308
|
+
for trade in trades:
|
|
309
|
+
|
|
310
|
+
if trade.take_profits is not None:
|
|
311
|
+
selection += [
|
|
312
|
+
{
|
|
313
|
+
"symbol": trade.symbol,
|
|
314
|
+
"target_symbol": trade.target_symbol,
|
|
315
|
+
"trading_symbol": trade.trading_symbol,
|
|
316
|
+
"status": get_status(take_profit),
|
|
317
|
+
"trade_id": take_profit.trade_id,
|
|
318
|
+
"trade_risk_type": take_profit.trade_risk_type,
|
|
319
|
+
"percentage": take_profit.percentage,
|
|
320
|
+
"open_price": take_profit.open_price,
|
|
321
|
+
"sell_percentage": take_profit.sell_percentage,
|
|
322
|
+
"high_water_mark": take_profit.high_water_mark,
|
|
323
|
+
"high_water_mark_date": \
|
|
324
|
+
take_profit.high_water_mark_date,
|
|
325
|
+
"take_profit_price": take_profit.take_profit_price,
|
|
326
|
+
"sell_amount": take_profit.sell_amount,
|
|
327
|
+
"sold_amount": take_profit.sold_amount,
|
|
328
|
+
"active": take_profit.active,
|
|
329
|
+
"sell_prices": take_profit.sell_prices
|
|
330
|
+
} for take_profit in trade.take_profits if take_profit.sold_amount > 0
|
|
331
|
+
]
|
|
332
|
+
else:
|
|
333
|
+
for trade in trades:
|
|
334
|
+
|
|
335
|
+
if trade.take_profits is not None:
|
|
336
|
+
|
|
337
|
+
for take_profit in trade.take_profits:
|
|
338
|
+
data = {
|
|
339
|
+
"symbol": trade.symbol,
|
|
340
|
+
"target_symbol": trade.target_symbol,
|
|
341
|
+
"trading_symbol": trade.trading_symbol,
|
|
342
|
+
"status": get_status(take_profit),
|
|
343
|
+
"trade_id": take_profit.trade_id,
|
|
344
|
+
"trade_risk_type": take_profit.trade_risk_type,
|
|
345
|
+
"percentage": take_profit.percentage,
|
|
346
|
+
"open_price": take_profit.open_price,
|
|
347
|
+
"sell_percentage": take_profit.sell_percentage,
|
|
348
|
+
"take_profit_price": take_profit.take_profit_price,
|
|
349
|
+
"sell_amount": take_profit.sell_amount,
|
|
350
|
+
"sold_amount": take_profit.sold_amount,
|
|
351
|
+
"active": take_profit.active
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if hasattr(take_profit, "sell_prices"):
|
|
355
|
+
data["sell_prices"] = take_profit.sell_prices
|
|
356
|
+
else:
|
|
357
|
+
data["sell_prices"] = None
|
|
358
|
+
|
|
359
|
+
if hasattr(take_profit, "high_water_mark"):
|
|
360
|
+
data["high_water_mark"] = take_profit.high_water_mark
|
|
361
|
+
|
|
362
|
+
if hasattr(take_profit, "high_water_mark_date"):
|
|
363
|
+
data["high_water_mark_date"] = \
|
|
364
|
+
take_profit.high_water_mark_date
|
|
365
|
+
else:
|
|
366
|
+
data["high_water_mark_date"] = None
|
|
367
|
+
else:
|
|
368
|
+
data["high_water_mark"] = None
|
|
369
|
+
data["high_water_mark_date"] = None
|
|
370
|
+
|
|
371
|
+
selection.append(data)
|
|
372
|
+
|
|
373
|
+
take_profit_table["Trade (Trade id)"] = [
|
|
374
|
+
f"{stop_loss['symbol'] + ' (' + str(stop_loss['trade_id']) + ')'}"
|
|
375
|
+
for stop_loss in selection
|
|
376
|
+
]
|
|
377
|
+
take_profit_table["Status"] = [
|
|
378
|
+
f"{stop_loss['status']}"
|
|
379
|
+
for stop_loss in selection
|
|
380
|
+
]
|
|
381
|
+
take_profit_table["Active"] = [
|
|
382
|
+
f"{stop_loss['active']}"
|
|
383
|
+
for stop_loss in selection
|
|
384
|
+
]
|
|
385
|
+
take_profit_table["Type"] = [
|
|
386
|
+
f"{stop_loss['trade_risk_type']}" for stop_loss
|
|
387
|
+
in selection
|
|
388
|
+
]
|
|
389
|
+
take_profit_table["Take profit (Initial Take Profit)"] = [
|
|
390
|
+
get_take_profit_price(stop_loss) for stop_loss in selection
|
|
391
|
+
]
|
|
392
|
+
take_profit_table["Open price"] = [
|
|
393
|
+
f"{float(stop_loss['open_price']):.{price_precision}f} {stop_loss['trading_symbol']}" for stop_loss in selection if stop_loss['open_price'] is not None
|
|
394
|
+
]
|
|
395
|
+
take_profit_table["Sell price's"] = [
|
|
396
|
+
f"{stop_loss['sell_prices']}" for stop_loss in selection
|
|
397
|
+
]
|
|
398
|
+
# Print nothing if high water mark is None
|
|
399
|
+
take_profit_table["High water mark"] = [
|
|
400
|
+
f"{get_high_water_mark(stop_loss)}" for stop_loss in selection
|
|
401
|
+
]
|
|
402
|
+
take_profit_table["Percentage"] = [
|
|
403
|
+
f"{float(stop_loss['sell_percentage'])}%" for stop_loss in selection
|
|
404
|
+
]
|
|
405
|
+
take_profit_table["Size"] = [
|
|
406
|
+
f"{float(stop_loss['sell_amount']):.{amount_precision}f} {stop_loss['target_symbol']}" for stop_loss in selection
|
|
407
|
+
]
|
|
408
|
+
take_profit_table["Sold amount"] = [
|
|
409
|
+
get_sold_amount(stop_loss) for stop_loss in selection
|
|
410
|
+
]
|
|
411
|
+
print(tabulate(take_profit_table, headers="keys", tablefmt="rounded_grid"))
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def pretty_print_date_ranges(date_ranges: List[BacktestDateRange]) -> None:
|
|
415
|
+
"""
|
|
416
|
+
Pretty print the date ranges to the console.
|
|
417
|
+
|
|
418
|
+
param date_ranges: The date ranges
|
|
419
|
+
"""
|
|
420
|
+
print(f"{COLOR_YELLOW}Date ranges of backtests:{COLOR_RESET}")
|
|
421
|
+
for date_range in date_ranges:
|
|
422
|
+
start_date = date_range.start_date
|
|
423
|
+
end_date = date_range.end_date
|
|
424
|
+
|
|
425
|
+
if isinstance(start_date, datetime):
|
|
426
|
+
start_date = start_date.strftime(DATETIME_FORMAT)
|
|
427
|
+
|
|
428
|
+
if isinstance(end_date, datetime):
|
|
429
|
+
end_date = end_date.strftime(DATETIME_FORMAT)
|
|
430
|
+
|
|
431
|
+
if date_range.name is not None:
|
|
432
|
+
print(f"{COLOR_GREEN}{date_range.name}: {start_date} - {end_date}{COLOR_RESET}")
|
|
433
|
+
else:
|
|
434
|
+
print(f"{COLOR_GREEN}{start_date} - {end_date}{COLOR_RESET}")
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
def pretty_print_price_efficiency(reports, precision=4):
|
|
438
|
+
"""
|
|
439
|
+
Pretty print the price efficiency of the backtest reports evaluation
|
|
440
|
+
to the console.
|
|
441
|
+
"""
|
|
442
|
+
# Get all symbols of the reports
|
|
443
|
+
print(f"{COLOR_YELLOW}Price noise{COLOR_RESET}")
|
|
444
|
+
rows = []
|
|
445
|
+
|
|
446
|
+
for report in reports:
|
|
447
|
+
|
|
448
|
+
if report.metrics is not None and "efficiency_ratio" in report.metrics:
|
|
449
|
+
price_efficiency = report.metrics["efficiency_ratio"]
|
|
450
|
+
|
|
451
|
+
for symbol in price_efficiency:
|
|
452
|
+
row = {}
|
|
453
|
+
row["Symbol"] = symbol
|
|
454
|
+
row["Efficiency ratio / Noise"] = f"{float(price_efficiency[symbol]):.{precision}f}"
|
|
455
|
+
row["Date"] = f"{report.backtest_start_date} - {report.backtest_end_date}"
|
|
456
|
+
|
|
457
|
+
if report.backtest_date_range.name is not None:
|
|
458
|
+
row["Date"] = f"{report.backtest_date_range.name} " \
|
|
459
|
+
f"{report.backtest_date_range.start_date}" \
|
|
460
|
+
f" - {report.backtest_date_range.end_date}"
|
|
461
|
+
else:
|
|
462
|
+
row["Date"] = f"{report.backtest_start_date} - " \
|
|
463
|
+
f"{report.backtest_end_date}"
|
|
464
|
+
|
|
465
|
+
rows.append(row)
|
|
466
|
+
|
|
467
|
+
# Remove all duplicate rows with the same symbol and date range
|
|
468
|
+
unique_rows = []
|
|
469
|
+
|
|
470
|
+
# Initialize an empty set to track unique (symbol, date) pairs
|
|
471
|
+
seen = set()
|
|
472
|
+
# Initialize a list to store the filtered dictionaries
|
|
473
|
+
filtered_data = []
|
|
474
|
+
|
|
475
|
+
# Iterate through each dictionary in the list
|
|
476
|
+
for entry in rows:
|
|
477
|
+
# Extract the (symbol, date) pair
|
|
478
|
+
pair = (entry["Symbol"], entry["Date"])
|
|
479
|
+
# Check if the pair is already in the set
|
|
480
|
+
if pair not in seen:
|
|
481
|
+
# If not, add the pair to the set and
|
|
482
|
+
# the entry to the filtered list
|
|
483
|
+
seen.add(pair)
|
|
484
|
+
filtered_data.append(entry)
|
|
485
|
+
|
|
486
|
+
print(
|
|
487
|
+
tabulate(
|
|
488
|
+
filtered_data,
|
|
489
|
+
headers="keys",
|
|
490
|
+
tablefmt="rounded_grid"
|
|
491
|
+
)
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def pretty_print_orders(
|
|
496
|
+
backtest: Backtest,
|
|
497
|
+
backtest_date_range: BacktestDateRange = None,
|
|
498
|
+
target_symbol = None,
|
|
499
|
+
order_status = None,
|
|
500
|
+
amount_precesion=4,
|
|
501
|
+
price_precision=2,
|
|
502
|
+
time_precision=1,
|
|
503
|
+
percentage_precision=2
|
|
504
|
+
) -> None:
|
|
505
|
+
"""
|
|
506
|
+
Pretty print the orders of the backtest report to the console.
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
backtest: The backtest
|
|
510
|
+
backtest_date_range: The date range of the backtest
|
|
511
|
+
target_symbol: The target symbol of the orders
|
|
512
|
+
order_status: The status of the orders
|
|
513
|
+
amount_precesion: The precision of the amount
|
|
514
|
+
price_precision: The precision of the price
|
|
515
|
+
time_precision: The precision of the time
|
|
516
|
+
percentage_precision: The precision of the percentage
|
|
517
|
+
|
|
518
|
+
Returns:
|
|
519
|
+
None
|
|
520
|
+
"""
|
|
521
|
+
run = backtest.get_backtest_run(backtest_date_range)
|
|
522
|
+
selection = run.get_orders(
|
|
523
|
+
target_symbol=target_symbol,
|
|
524
|
+
order_status=order_status
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
print(f"{COLOR_YELLOW}Orders overview{COLOR_RESET}")
|
|
528
|
+
orders_table = {}
|
|
529
|
+
orders_table["Pair (Order id)"] = [
|
|
530
|
+
f"{order.target_symbol}/{order.trading_symbol} ({order.id})"
|
|
531
|
+
for order in selection
|
|
532
|
+
]
|
|
533
|
+
orders_table["Status"] = [
|
|
534
|
+
order.status for order in selection
|
|
535
|
+
]
|
|
536
|
+
orders_table["Side"] = [
|
|
537
|
+
order.order_side for order in selection
|
|
538
|
+
]
|
|
539
|
+
orders_table["Size"] = [
|
|
540
|
+
f"{float(order.get_size()):.{amount_precesion}f} {order.trading_symbol}"
|
|
541
|
+
for order in selection
|
|
542
|
+
]
|
|
543
|
+
orders_table["Price"] = [
|
|
544
|
+
f"{float(order.price):.{amount_precesion}f} {order.trading_symbol}"
|
|
545
|
+
for order in selection
|
|
546
|
+
]
|
|
547
|
+
orders_table["Amount"] = [
|
|
548
|
+
f"{float(order.amount):.{amount_precesion}f} {order.target_symbol}"
|
|
549
|
+
for order in selection
|
|
550
|
+
]
|
|
551
|
+
orders_table["Filled"] = [
|
|
552
|
+
f"{float(order.filled):.{amount_precesion}f} {order.target_symbol}"
|
|
553
|
+
for order in selection
|
|
554
|
+
]
|
|
555
|
+
orders_table["Created at"] = [
|
|
556
|
+
order.created_at.strftime("%Y-%m-%d %H:%M") for order in selection if order.created_at is not None
|
|
557
|
+
]
|
|
558
|
+
print(tabulate(orders_table, headers="keys", tablefmt="rounded_grid"))
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def pretty_print_positions(
|
|
562
|
+
backtest,
|
|
563
|
+
backtest_date_range: BacktestDateRange = None,
|
|
564
|
+
symbol = None,
|
|
565
|
+
amount_precision=4,
|
|
566
|
+
price_precision=2,
|
|
567
|
+
time_precision=1,
|
|
568
|
+
percentage_precision=2
|
|
569
|
+
) -> None:
|
|
570
|
+
"""
|
|
571
|
+
Pretty print the positions of the backtest report to the console.
|
|
572
|
+
|
|
573
|
+
Args:
|
|
574
|
+
backtest: The backtest report
|
|
575
|
+
backtest_date_range: The date range of the backtest
|
|
576
|
+
amount_precision: The precision of the amount
|
|
577
|
+
price_precision: The precision of the price
|
|
578
|
+
time_precision: The precision of the time
|
|
579
|
+
percentage_precision: The precision of the percentage
|
|
580
|
+
|
|
581
|
+
Returns:
|
|
582
|
+
None
|
|
583
|
+
"""
|
|
584
|
+
results = backtest.get_backtest_run(backtest_date_range)
|
|
585
|
+
selection = results.get_positions(symbol=symbol)
|
|
586
|
+
|
|
587
|
+
print(f"{COLOR_YELLOW}Positions overview{COLOR_RESET}")
|
|
588
|
+
position_table = {}
|
|
589
|
+
position_table["Position"] = [
|
|
590
|
+
position.symbol for position in selection
|
|
591
|
+
]
|
|
592
|
+
position_table["Amount"] = [
|
|
593
|
+
f"{float(position.amount):.{amount_precision}f}" for position in
|
|
594
|
+
selection
|
|
595
|
+
]
|
|
596
|
+
position_table["Pending buy amount"] = [
|
|
597
|
+
f"{float(position.amount_pending_buy):.{amount_precision}f}"
|
|
598
|
+
for position in selection
|
|
599
|
+
]
|
|
600
|
+
position_table["Pending sell amount"] = [
|
|
601
|
+
f"{float(position.amount_pending_sell):.{amount_precision}f}"
|
|
602
|
+
for position in selection
|
|
603
|
+
]
|
|
604
|
+
position_table[f"Cost ({results.trading_symbol})"] = [
|
|
605
|
+
f"{float(position.cost):.{price_precision}f}"
|
|
606
|
+
for position in selection
|
|
607
|
+
]
|
|
608
|
+
position_table[f"Value ({results.trading_symbol})"] = [
|
|
609
|
+
f"{float(position.value):.{price_precision}f} {results.trading_symbol}"
|
|
610
|
+
for position in selection
|
|
611
|
+
]
|
|
612
|
+
position_table["Percentage of portfolio"] = [
|
|
613
|
+
f"{float(position.percentage_of_portfolio):.{percentage_precision}f}%"
|
|
614
|
+
for position in selection
|
|
615
|
+
]
|
|
616
|
+
position_table[f"Growth ({results.trading_symbol})"] = [
|
|
617
|
+
f"{float(position.growth):.{price_precision}f} {results.trading_symbol}"
|
|
618
|
+
for position in selection
|
|
619
|
+
]
|
|
620
|
+
position_table["Growth_percentage"] = [
|
|
621
|
+
f"{float(position.growth_percentage):.{percentage_precision}f}%"
|
|
622
|
+
for position in selection
|
|
623
|
+
]
|
|
624
|
+
print(
|
|
625
|
+
tabulate(position_table, headers="keys", tablefmt="rounded_grid")
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def pretty_print_trades(
|
|
630
|
+
backtest: Backtest,
|
|
631
|
+
backtest_date_range: BacktestDateRange = None,
|
|
632
|
+
target_symbol = None,
|
|
633
|
+
status = None,
|
|
634
|
+
amount_precision=4,
|
|
635
|
+
price_precision=2,
|
|
636
|
+
time_precision=1,
|
|
637
|
+
percentage_precision=2
|
|
638
|
+
):
|
|
639
|
+
"""
|
|
640
|
+
Pretty print the trades of the backtest report to the console.
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
backtest: The backtest report
|
|
644
|
+
backtest_date_range: The date range of the backtest
|
|
645
|
+
target_symbol: The target symbol of the trades
|
|
646
|
+
status: The status of the trades
|
|
647
|
+
amount_precision: The precision of the amount
|
|
648
|
+
price_precision: The precision of the price
|
|
649
|
+
time_precision: The precision of the time
|
|
650
|
+
percentage_precision: The precision of the percentage
|
|
651
|
+
|
|
652
|
+
Returns:
|
|
653
|
+
None
|
|
654
|
+
"""
|
|
655
|
+
run = backtest.get_backtest_run(backtest_date_range)
|
|
656
|
+
selection = run.get_trades(
|
|
657
|
+
target_symbol=target_symbol,
|
|
658
|
+
trade_status=status
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
def get_status(trade):
|
|
662
|
+
status = "OPEN"
|
|
663
|
+
|
|
664
|
+
if TradeStatus.CLOSED.equals(trade.status):
|
|
665
|
+
status = "CLOSED"
|
|
666
|
+
|
|
667
|
+
if has_triggered_stop_losses(trade):
|
|
668
|
+
status += ", SL"
|
|
669
|
+
|
|
670
|
+
if has_triggered_take_profits(trade):
|
|
671
|
+
status += ", TP"
|
|
672
|
+
|
|
673
|
+
return status
|
|
674
|
+
|
|
675
|
+
def get_close_prices(trade):
|
|
676
|
+
|
|
677
|
+
sell_orders = [
|
|
678
|
+
order for order in trade.orders
|
|
679
|
+
if OrderSide.SELL.equals(order.order_side)
|
|
680
|
+
]
|
|
681
|
+
text = ""
|
|
682
|
+
number_of_sell_orders = 0
|
|
683
|
+
|
|
684
|
+
for sell_order in sell_orders:
|
|
685
|
+
|
|
686
|
+
if number_of_sell_orders > 0:
|
|
687
|
+
text += ", "
|
|
688
|
+
|
|
689
|
+
text += f"{float(sell_order.price):.{price_precision}f}"
|
|
690
|
+
number_of_sell_orders += 1
|
|
691
|
+
|
|
692
|
+
return text
|
|
693
|
+
|
|
694
|
+
def has_triggered_take_profits(trade):
|
|
695
|
+
|
|
696
|
+
if trade.take_profits is None:
|
|
697
|
+
return False
|
|
698
|
+
|
|
699
|
+
triggered = [
|
|
700
|
+
take_profit for take_profit in trade.take_profits if take_profit.sold_amount != 0
|
|
701
|
+
]
|
|
702
|
+
|
|
703
|
+
return len(triggered) > 0
|
|
704
|
+
|
|
705
|
+
def has_triggered_stop_losses(trade):
|
|
706
|
+
|
|
707
|
+
if trade.stop_losses is None:
|
|
708
|
+
return False
|
|
709
|
+
|
|
710
|
+
triggered = [
|
|
711
|
+
stop_loss for stop_loss in trade.stop_losses if stop_loss.sold_amount != 0
|
|
712
|
+
]
|
|
713
|
+
return len(triggered) > 0
|
|
714
|
+
|
|
715
|
+
def get_high_water_mark(trade):
|
|
716
|
+
if trade.high_water_mark is not None:
|
|
717
|
+
return f"{float(trade.high_water_mark):.{price_precision}f} {trade.trading_symbol} ({format_date(trade.high_water_mark_datetime)})"
|
|
718
|
+
|
|
719
|
+
return ""
|
|
720
|
+
|
|
721
|
+
print(f"{COLOR_YELLOW}Trades overview{COLOR_RESET}")
|
|
722
|
+
trades_table = {}
|
|
723
|
+
trades_table["Pair (Trade id)"] = [
|
|
724
|
+
f"{trade.target_symbol}/{trade.trading_symbol} ({trade.id})"
|
|
725
|
+
for trade in selection
|
|
726
|
+
]
|
|
727
|
+
trades_table["Status"] = [
|
|
728
|
+
get_status(trade) for trade in selection
|
|
729
|
+
]
|
|
730
|
+
trades_table["Amount (remaining)"] = [
|
|
731
|
+
f"{float(trade.amount):.{amount_precision}f} ({float(trade.remaining):.{amount_precision}f}) {trade.target_symbol}"
|
|
732
|
+
for trade in selection
|
|
733
|
+
]
|
|
734
|
+
trades_table[f"Net gain ({run.trading_symbol})"] = [
|
|
735
|
+
f"{float(trade.net_gain):.{price_precision}f}"
|
|
736
|
+
for trade in selection
|
|
737
|
+
]
|
|
738
|
+
trades_table["Open date"] = [
|
|
739
|
+
trade.opened_at.strftime("%Y-%m-%d %H:%M") for trade in selection if trade.opened_at is not None
|
|
740
|
+
]
|
|
741
|
+
trades_table["Close date"] = [
|
|
742
|
+
trade.closed_at.strftime("%Y-%m-%d %H:%M") for trade in selection if trade.closed_at is not None
|
|
743
|
+
]
|
|
744
|
+
trades_table["Duration"] = [
|
|
745
|
+
f"{trade.duration:.{time_precision}f} hours" for trade in selection
|
|
746
|
+
]
|
|
747
|
+
# Add (unrealized) to the net gain if the trade is still open
|
|
748
|
+
trades_table[f"Net gain ({run.trading_symbol})"] = [
|
|
749
|
+
f"{float(trade.net_gain_absolute):.{price_precision}f} ({float(trade.net_gain_percentage):.{percentage_precision}f}%)" + (" (unrealized)" if not TradeStatus.CLOSED.equals(trade.status) else "")
|
|
750
|
+
for trade in selection
|
|
751
|
+
]
|
|
752
|
+
trades_table[f"Open price ({run.trading_symbol})"] = [
|
|
753
|
+
f"{trade.open_price:.{price_precision}f}" for trade in selection
|
|
754
|
+
]
|
|
755
|
+
trades_table[
|
|
756
|
+
f"Close price's ({run.trading_symbol})"
|
|
757
|
+
] = [
|
|
758
|
+
get_close_prices(trade) for trade in selection
|
|
759
|
+
]
|
|
760
|
+
trades_table["High water mark"] = [
|
|
761
|
+
get_high_water_mark(trade) for trade in selection
|
|
762
|
+
]
|
|
763
|
+
print(tabulate(trades_table, headers="keys", tablefmt="rounded_grid"))
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def print_number_of_runs(report):
|
|
767
|
+
|
|
768
|
+
if report.number_of_runs == 1:
|
|
769
|
+
|
|
770
|
+
if report.inteval > 1:
|
|
771
|
+
print(f"Strategy ran every {report.interval} {report.time_unit} "
|
|
772
|
+
f"for a total of {report.number_of_runs} time")
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
def pretty_print_backtest(
|
|
776
|
+
backtest: Backtest,
|
|
777
|
+
backtest_date_range: BacktestDateRange = None,
|
|
778
|
+
show_positions=True,
|
|
779
|
+
show_trades=True,
|
|
780
|
+
show_stop_losses=True,
|
|
781
|
+
show_triggered_stop_losses_only=False,
|
|
782
|
+
show_take_profits=True,
|
|
783
|
+
show_triggered_take_profits_only=False,
|
|
784
|
+
amount_precision=4,
|
|
785
|
+
price_precision=2,
|
|
786
|
+
time_precision=1,
|
|
787
|
+
percentage_precision=2
|
|
788
|
+
):
|
|
789
|
+
"""
|
|
790
|
+
Pretty print the backtest report to the console.
|
|
791
|
+
|
|
792
|
+
Args:
|
|
793
|
+
backtest: Backtest - the backtest report
|
|
794
|
+
show_positions: bool - show the positions
|
|
795
|
+
show_trades: bool - show the trades
|
|
796
|
+
show_stop_losses: bool - show the stop losses
|
|
797
|
+
show_triggered_stop_losses_only: bool - show only the triggered stop losses
|
|
798
|
+
show_take_profits: bool - show the take profits
|
|
799
|
+
show_triggered_take_profits_only: bool - show only the triggered take profits
|
|
800
|
+
amount_precision: int - the amount precision
|
|
801
|
+
price_precision: int - the price precision
|
|
802
|
+
time_precision: int - the time precision
|
|
803
|
+
percentage_precision: int - the percentage precision
|
|
804
|
+
|
|
805
|
+
Returns:
|
|
806
|
+
None
|
|
807
|
+
"""
|
|
808
|
+
backtest_results = backtest.get_backtest_run(backtest_date_range)
|
|
809
|
+
backtest_metrics = backtest.get_backtest_metrics(backtest_date_range)
|
|
810
|
+
ascii_art = f"""
|
|
811
|
+
:%%%#+- .=*#%%% {COLOR_GREEN}Backtest report{COLOR_RESET}
|
|
812
|
+
*%%%%%%%+------=*%%%%%%%- {COLOR_GREEN}---------------------------{COLOR_RESET}
|
|
813
|
+
*%%%%%%%%%%%%%%%%%%%%%%%- {COLOR_YELLOW}Start date:{COLOR_RESET}{COLOR_GREEN} {backtest_results.backtest_start_date}{COLOR_RESET}
|
|
814
|
+
.%%%%%%%%%%%%%%%%%%%%%%# {COLOR_YELLOW}End date:{COLOR_RESET}{COLOR_GREEN} {backtest_results.backtest_end_date}{COLOR_RESET}
|
|
815
|
+
#%%%####%%%%%%%%**#%%%+ {COLOR_YELLOW}Number of days:{COLOR_RESET}{COLOR_GREEN}{COLOR_RESET}{COLOR_GREEN} {backtest_results.number_of_days}{COLOR_RESET}
|
|
816
|
+
.:-+*%%%%- {COLOR_PURPLE}-+..#{COLOR_RESET}%%%+.{COLOR_PURPLE}+- +{COLOR_RESET}%%%#*=-: {COLOR_YELLOW}Number of runs:{COLOR_RESET}{COLOR_GREEN} {backtest_results.number_of_runs}{COLOR_RESET}
|
|
817
|
+
.:-=*%%%%. {COLOR_PURPLE}+={COLOR_RESET} .%%# {COLOR_PURPLE}-+.-{COLOR_RESET}%%%%=-:.. {COLOR_YELLOW}Number of orders:{COLOR_RESET}{COLOR_GREEN} {backtest_results.number_of_orders}{COLOR_RESET}
|
|
818
|
+
.:=+#%%%%%*###%%%%#*+#%%%%%%*+-: {COLOR_YELLOW}Initial balance:{COLOR_RESET}{COLOR_GREEN} {backtest_results.initial_unallocated}{COLOR_RESET}
|
|
819
|
+
+%%%%%%%%%%%%%%%%%%%= {COLOR_YELLOW}Final balance:{COLOR_RESET}{COLOR_GREEN} {float(backtest_metrics.final_value):.{price_precision}f}{COLOR_RESET}
|
|
820
|
+
:++ .=#%%%%%%%%%%%%%*- {COLOR_YELLOW}Total net gain:{COLOR_RESET}{COLOR_GREEN} {float(backtest_metrics.total_net_gain):.{price_precision}f} {float(backtest_metrics.total_net_gain_percentage):.{percentage_precision}f}%{COLOR_RESET}
|
|
821
|
+
:++: :+%%%%%%#-. {COLOR_YELLOW}Growth:{COLOR_RESET}{COLOR_GREEN} {float(backtest_metrics.growth):.{price_precision}f} {float(backtest_metrics.growth_percentage):.{percentage_precision}f}%{COLOR_RESET}
|
|
822
|
+
:++: .%%%%%#= {COLOR_YELLOW}Number of trades:{COLOR_RESET}{COLOR_GREEN} {backtest_results.number_of_trades}{COLOR_RESET}
|
|
823
|
+
:++: .%%%%%#= {COLOR_YELLOW}Number of trades closed:{COLOR_RESET}{COLOR_GREEN} {backtest_results.number_of_trades_closed}{COLOR_RESET}
|
|
824
|
+
:++: .#%%%%%#*= {COLOR_YELLOW}Number of trades open(end of backtest):{COLOR_RESET}{COLOR_GREEN} {backtest_results.number_of_trades_open}{COLOR_RESET}
|
|
825
|
+
:++- :%%%%%%%%%+= {COLOR_YELLOW}Percentage positive trades:{COLOR_RESET}{COLOR_GREEN} {float(backtest_metrics.percentage_positive_trades):.{percentage_precision}f}%{COLOR_RESET}
|
|
826
|
+
.++- -%%%%%%%%%%%+= {COLOR_YELLOW}Percentage negative trades:{COLOR_RESET}{COLOR_GREEN} {float(backtest_metrics.percentage_negative_trades):.{percentage_precision}f}%{COLOR_RESET}
|
|
827
|
+
.++- .%%%%%%%%%%%%%+= {COLOR_YELLOW}Average trade size:{COLOR_RESET}{COLOR_GREEN} {float(backtest_metrics.average_trade_size):.{price_precision}f} {backtest_results.trading_symbol}{COLOR_RESET}
|
|
828
|
+
.++- *%%%%%%%%%%%%%*+: {COLOR_YELLOW}Average trade duration:{COLOR_RESET}{COLOR_GREEN} {float(backtest_metrics.average_trade_duration):.{0}f} hours{COLOR_RESET}
|
|
829
|
+
.++- %%%%%%%%%%%%%%#+=
|
|
830
|
+
=++........:::%%%%%%%%%%%%%%*+-
|
|
831
|
+
.=++++++++++**#%%%%%%%%%%%%%++.
|
|
832
|
+
"""
|
|
833
|
+
|
|
834
|
+
print(ascii_art)
|
|
835
|
+
|
|
836
|
+
if show_positions:
|
|
837
|
+
print(f"{COLOR_YELLOW}Positions overview{COLOR_RESET}")
|
|
838
|
+
pretty_print_positions(backtest)
|
|
839
|
+
|
|
840
|
+
if show_trades:
|
|
841
|
+
pretty_print_trades(
|
|
842
|
+
backtest=backtest,
|
|
843
|
+
amount_precision=amount_precision,
|
|
844
|
+
price_precision=price_precision,
|
|
845
|
+
time_precision=time_precision,
|
|
846
|
+
percentage_precision=percentage_precision
|
|
847
|
+
)
|
|
848
|
+
|
|
849
|
+
if show_stop_losses:
|
|
850
|
+
pretty_print_stop_losses(
|
|
851
|
+
backtest=backtest,
|
|
852
|
+
triggered_only=show_triggered_stop_losses_only,
|
|
853
|
+
amount_precision=amount_precision,
|
|
854
|
+
price_precision=price_precision,
|
|
855
|
+
time_precision=time_precision,
|
|
856
|
+
percentage_precision=percentage_precision
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
if show_take_profits:
|
|
860
|
+
pretty_print_take_profits(
|
|
861
|
+
backtest=backtest,
|
|
862
|
+
triggered_only=show_triggered_take_profits_only,
|
|
863
|
+
amount_precision=amount_precision,
|
|
864
|
+
price_precision=price_precision,
|
|
865
|
+
time_precision=time_precision,
|
|
866
|
+
percentage_precision=percentage_precision
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
def get_start_date_from_backtest_report_file(path: str) -> datetime:
|
|
870
|
+
"""
|
|
871
|
+
Function to get the backtest start date from a backtest report file.
|
|
872
|
+
|
|
873
|
+
Parameters:
|
|
874
|
+
path (str): The path to the backtest report file
|
|
875
|
+
|
|
876
|
+
Returns:
|
|
877
|
+
datetime: The backtest start date
|
|
878
|
+
"""
|
|
879
|
+
|
|
880
|
+
# Get the backtest start date from the file name
|
|
881
|
+
backtest_start_date = os.path.basename(path).split("_")[3]
|
|
882
|
+
# Parse the backtest start date
|
|
883
|
+
return datetime.strptime(
|
|
884
|
+
backtest_start_date, DATETIME_FORMAT_BACKTESTING
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
def get_end_date_from_backtest_report_file(path: str) -> datetime:
|
|
889
|
+
"""
|
|
890
|
+
Function to get the backtest end date from a backtest report file.
|
|
891
|
+
|
|
892
|
+
Parameters:
|
|
893
|
+
path (str): The path to the backtest report file
|
|
894
|
+
|
|
895
|
+
Returns:
|
|
896
|
+
datetime: The backtest end date
|
|
897
|
+
"""
|
|
898
|
+
|
|
899
|
+
# Get the backtest end date from the file name
|
|
900
|
+
backtest_end_date = os.path.basename(path).split("_")[5]
|
|
901
|
+
# Parse the backtest end date
|
|
902
|
+
return datetime.strptime(
|
|
903
|
+
backtest_end_date, DATETIME_FORMAT_BACKTESTING
|
|
904
|
+
)
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
def get_algorithm_name_from_backtest_report_file(path: str) -> str:
|
|
908
|
+
"""
|
|
909
|
+
Function to get the algorithm name from a backtest report file.
|
|
910
|
+
|
|
911
|
+
Parameters:
|
|
912
|
+
path (str): The path to the backtest report file
|
|
913
|
+
|
|
914
|
+
Returns:
|
|
915
|
+
str: The algorithm name
|
|
916
|
+
"""
|
|
917
|
+
# Get the word between "report_" and "_backtest_start_date"
|
|
918
|
+
# it can contain _
|
|
919
|
+
# Get the algorithm name from the file name
|
|
920
|
+
algorithm_name = os.path.basename(path).split("_")[1]
|
|
921
|
+
return algorithm_name
|