investing-algorithm-framework 1.3.1__py3-none-any.whl → 7.25.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- investing_algorithm_framework/__init__.py +195 -16
- investing_algorithm_framework/analysis/__init__.py +16 -0
- investing_algorithm_framework/analysis/backtest_data_ranges.py +202 -0
- investing_algorithm_framework/analysis/data.py +170 -0
- investing_algorithm_framework/analysis/markdown.py +91 -0
- investing_algorithm_framework/analysis/ranking.py +298 -0
- investing_algorithm_framework/app/__init__.py +31 -4
- investing_algorithm_framework/app/algorithm/__init__.py +7 -0
- investing_algorithm_framework/app/algorithm/algorithm.py +193 -0
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +118 -0
- investing_algorithm_framework/app/app.py +2233 -264
- investing_algorithm_framework/app/app_hook.py +28 -0
- investing_algorithm_framework/app/context.py +1724 -0
- investing_algorithm_framework/app/eventloop.py +620 -0
- investing_algorithm_framework/app/reporting/__init__.py +27 -0
- investing_algorithm_framework/app/reporting/ascii.py +921 -0
- investing_algorithm_framework/app/reporting/backtest_report.py +349 -0
- investing_algorithm_framework/app/reporting/charts/__init__.py +19 -0
- investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +66 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve.py +37 -0
- investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +74 -0
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +70 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +79 -0
- investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +55 -0
- investing_algorithm_framework/app/reporting/generate.py +185 -0
- investing_algorithm_framework/app/reporting/tables/__init__.py +11 -0
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +217 -0
- investing_algorithm_framework/app/reporting/tables/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/action_handlers/__init__.py +6 -3
- investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +1 -1
- investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +2 -1
- investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +14 -7
- investing_algorithm_framework/app/stateless/exception_handler.py +1 -1
- investing_algorithm_framework/app/strategy.py +873 -52
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/app/web/__init__.py +2 -1
- investing_algorithm_framework/app/web/controllers/__init__.py +2 -2
- investing_algorithm_framework/app/web/controllers/orders.py +4 -3
- investing_algorithm_framework/app/web/controllers/portfolio.py +1 -1
- investing_algorithm_framework/app/web/controllers/positions.py +3 -3
- investing_algorithm_framework/app/web/create_app.py +4 -2
- investing_algorithm_framework/app/web/error_handler.py +1 -1
- investing_algorithm_framework/app/web/schemas/order.py +2 -2
- investing_algorithm_framework/app/web/schemas/position.py +1 -0
- investing_algorithm_framework/cli/__init__.py +0 -0
- investing_algorithm_framework/cli/cli.py +231 -0
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +501 -0
- investing_algorithm_framework/cli/deploy_to_azure_function.py +718 -0
- investing_algorithm_framework/cli/initialize_app.py +603 -0
- investing_algorithm_framework/cli/templates/.gitignore.template +178 -0
- investing_algorithm_framework/cli/templates/app.py.template +18 -0
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +48 -0
- investing_algorithm_framework/cli/templates/app_azure_function.py.template +14 -0
- investing_algorithm_framework/cli/templates/app_web.py.template +18 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +22 -0
- investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +92 -0
- investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +110 -0
- investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +65 -0
- investing_algorithm_framework/cli/templates/azure_function_host.json.template +15 -0
- investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +8 -0
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +3 -0
- investing_algorithm_framework/cli/templates/data_providers.py.template +17 -0
- investing_algorithm_framework/cli/templates/env.example.template +2 -0
- investing_algorithm_framework/cli/templates/env_azure_function.example.template +4 -0
- investing_algorithm_framework/cli/templates/market_data_providers.py.template +9 -0
- investing_algorithm_framework/cli/templates/readme.md.template +135 -0
- investing_algorithm_framework/cli/templates/requirements.txt.template +2 -0
- investing_algorithm_framework/cli/templates/run_backtest.py.template +20 -0
- investing_algorithm_framework/cli/templates/strategy.py.template +124 -0
- investing_algorithm_framework/cli/validate_backtest_checkpoints.py +197 -0
- investing_algorithm_framework/create_app.py +43 -9
- investing_algorithm_framework/dependency_container.py +121 -33
- investing_algorithm_framework/domain/__init__.py +109 -22
- investing_algorithm_framework/domain/algorithm_id.py +69 -0
- investing_algorithm_framework/domain/backtesting/__init__.py +25 -0
- investing_algorithm_framework/domain/backtesting/backtest.py +548 -0
- investing_algorithm_framework/domain/backtesting/backtest_date_range.py +113 -0
- investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +241 -0
- investing_algorithm_framework/domain/backtesting/backtest_metrics.py +470 -0
- investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +275 -0
- investing_algorithm_framework/domain/backtesting/backtest_run.py +663 -0
- investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +162 -0
- investing_algorithm_framework/domain/backtesting/backtest_utils.py +198 -0
- investing_algorithm_framework/domain/backtesting/combine_backtests.py +392 -0
- investing_algorithm_framework/domain/config.py +60 -138
- investing_algorithm_framework/domain/constants.py +23 -34
- 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 +51 -1
- investing_algorithm_framework/domain/models/__init__.py +29 -14
- investing_algorithm_framework/domain/models/app_mode.py +34 -0
- investing_algorithm_framework/domain/models/base_model.py +3 -1
- investing_algorithm_framework/domain/models/data/__init__.py +7 -0
- investing_algorithm_framework/domain/models/data/data_source.py +222 -0
- investing_algorithm_framework/domain/models/data/data_type.py +46 -0
- investing_algorithm_framework/domain/models/event.py +35 -0
- investing_algorithm_framework/domain/models/market/__init__.py +5 -0
- investing_algorithm_framework/domain/models/market/market_credential.py +88 -0
- investing_algorithm_framework/domain/models/order/__init__.py +3 -4
- investing_algorithm_framework/domain/models/order/order.py +243 -86
- investing_algorithm_framework/domain/models/order/order_status.py +2 -2
- investing_algorithm_framework/domain/models/order/order_type.py +1 -3
- investing_algorithm_framework/domain/models/portfolio/__init__.py +7 -2
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +134 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +37 -37
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +208 -0
- investing_algorithm_framework/domain/models/position/__init__.py +3 -2
- investing_algorithm_framework/domain/models/position/position.py +29 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- investing_algorithm_framework/domain/models/position/{position_cost.py → position_snapshot.py} +16 -8
- investing_algorithm_framework/domain/models/risk_rules/__init__.py +7 -0
- investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +51 -0
- investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +55 -0
- investing_algorithm_framework/domain/models/snapshot_interval.py +45 -0
- investing_algorithm_framework/domain/models/strategy_profile.py +33 -0
- investing_algorithm_framework/domain/models/time_frame.py +94 -98
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +111 -2
- investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
- investing_algorithm_framework/domain/models/tracing/trace.py +23 -0
- investing_algorithm_framework/domain/models/trade/__init__.py +11 -0
- investing_algorithm_framework/domain/models/trade/trade.py +389 -0
- investing_algorithm_framework/domain/models/trade/trade_status.py +40 -0
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +332 -0
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +365 -0
- investing_algorithm_framework/domain/order_executor.py +112 -0
- investing_algorithm_framework/domain/portfolio_provider.py +118 -0
- investing_algorithm_framework/domain/services/__init__.py +11 -0
- investing_algorithm_framework/domain/services/market_credential_service.py +37 -0
- investing_algorithm_framework/domain/services/portfolios/__init__.py +5 -0
- investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +9 -0
- investing_algorithm_framework/domain/services/rounding_service.py +27 -0
- investing_algorithm_framework/domain/services/state_handler.py +38 -0
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +16 -4
- investing_algorithm_framework/domain/utils/csv.py +22 -0
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/dates.py +57 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +53 -0
- investing_algorithm_framework/domain/utils/random.py +29 -0
- investing_algorithm_framework/download_data.py +244 -0
- investing_algorithm_framework/infrastructure/__init__.py +39 -11
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +36 -0
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +1152 -0
- investing_algorithm_framework/infrastructure/data_providers/csv.py +568 -0
- investing_algorithm_framework/infrastructure/data_providers/pandas.py +599 -0
- investing_algorithm_framework/infrastructure/database/__init__.py +6 -2
- investing_algorithm_framework/infrastructure/database/sql_alchemy.py +87 -13
- investing_algorithm_framework/infrastructure/models/__init__.py +13 -4
- investing_algorithm_framework/infrastructure/models/decimal_parser.py +14 -0
- investing_algorithm_framework/infrastructure/models/order/__init__.py +2 -2
- investing_algorithm_framework/infrastructure/models/order/order.py +73 -73
- 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 +3 -2
- investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +37 -0
- investing_algorithm_framework/infrastructure/models/portfolio/{portfolio.py → sql_portfolio.py} +57 -3
- investing_algorithm_framework/infrastructure/models/position/__init__.py +2 -2
- investing_algorithm_framework/infrastructure/models/position/position.py +16 -11
- 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 +59 -0
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +55 -0
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +21 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +200 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +19 -0
- investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +199 -0
- investing_algorithm_framework/infrastructure/repositories/__init__.py +13 -5
- investing_algorithm_framework/infrastructure/repositories/order_metadata_repository.py +17 -0
- investing_algorithm_framework/infrastructure/repositories/order_repository.py +32 -19
- investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +56 -0
- investing_algorithm_framework/infrastructure/repositories/position_repository.py +47 -4
- investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +21 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +85 -31
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +71 -0
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +29 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +29 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +9 -2
- investing_algorithm_framework/infrastructure/services/aws/__init__.py +6 -0
- investing_algorithm_framework/infrastructure/services/aws/state_handler.py +193 -0
- investing_algorithm_framework/infrastructure/services/azure/__init__.py +5 -0
- investing_algorithm_framework/infrastructure/services/azure/state_handler.py +158 -0
- investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +9 -0
- investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +2596 -0
- investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +285 -0
- investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +468 -0
- investing_algorithm_framework/services/__init__.py +127 -10
- investing_algorithm_framework/services/configuration_service.py +95 -0
- investing_algorithm_framework/services/data_providers/__init__.py +5 -0
- investing_algorithm_framework/services/data_providers/data_provider_service.py +1058 -0
- investing_algorithm_framework/services/market_credential_service.py +40 -0
- investing_algorithm_framework/services/metrics/__init__.py +119 -0
- investing_algorithm_framework/services/metrics/alpha.py +0 -0
- investing_algorithm_framework/services/metrics/beta.py +0 -0
- investing_algorithm_framework/services/metrics/cagr.py +60 -0
- investing_algorithm_framework/services/metrics/calmar_ratio.py +40 -0
- investing_algorithm_framework/services/metrics/drawdown.py +218 -0
- investing_algorithm_framework/services/metrics/equity_curve.py +24 -0
- investing_algorithm_framework/services/metrics/exposure.py +210 -0
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/services/metrics/mean_daily_return.py +84 -0
- investing_algorithm_framework/services/metrics/price_efficiency.py +57 -0
- investing_algorithm_framework/services/metrics/profit_factor.py +165 -0
- investing_algorithm_framework/services/metrics/recovery.py +113 -0
- investing_algorithm_framework/services/metrics/returns.py +452 -0
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/services/metrics/sharpe_ratio.py +137 -0
- investing_algorithm_framework/services/metrics/sortino_ratio.py +74 -0
- investing_algorithm_framework/services/metrics/standard_deviation.py +156 -0
- investing_algorithm_framework/services/metrics/trades.py +473 -0
- investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
- investing_algorithm_framework/services/metrics/ulcer.py +0 -0
- investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
- investing_algorithm_framework/services/metrics/volatility.py +118 -0
- investing_algorithm_framework/services/metrics/win_rate.py +177 -0
- investing_algorithm_framework/services/order_service/__init__.py +9 -0
- investing_algorithm_framework/services/order_service/order_backtest_service.py +178 -0
- investing_algorithm_framework/services/order_service/order_executor_lookup.py +110 -0
- investing_algorithm_framework/services/order_service/order_service.py +826 -0
- investing_algorithm_framework/services/portfolios/__init__.py +16 -0
- investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +54 -0
- investing_algorithm_framework/services/{portfolio_configuration_service.py → portfolios/portfolio_configuration_service.py} +27 -12
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +106 -0
- investing_algorithm_framework/services/portfolios/portfolio_service.py +188 -0
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +136 -0
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +182 -0
- investing_algorithm_framework/services/positions/__init__.py +7 -0
- investing_algorithm_framework/services/positions/position_service.py +210 -0
- investing_algorithm_framework/services/positions/position_snapshot_service.py +18 -0
- investing_algorithm_framework/services/repository_service.py +8 -2
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +117 -0
- investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +51 -0
- investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +80 -0
- investing_algorithm_framework/services/trade_service/__init__.py +9 -0
- investing_algorithm_framework/services/trade_service/trade_service.py +1099 -0
- investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +39 -0
- investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +41 -0
- investing_algorithm_framework-7.25.6.dist-info/METADATA +535 -0
- investing_algorithm_framework-7.25.6.dist-info/RECORD +268 -0
- {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/WHEEL +1 -2
- investing_algorithm_framework-7.25.6.dist-info/entry_points.txt +3 -0
- investing_algorithm_framework/app/algorithm.py +0 -410
- investing_algorithm_framework/domain/models/market_data/__init__.py +0 -11
- investing_algorithm_framework/domain/models/market_data/asset_price.py +0 -50
- investing_algorithm_framework/domain/models/market_data/ohlcv.py +0 -76
- investing_algorithm_framework/domain/models/market_data/order_book.py +0 -63
- investing_algorithm_framework/domain/models/market_data/ticker.py +0 -92
- investing_algorithm_framework/domain/models/order/order_fee.py +0 -45
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -47
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -205
- investing_algorithm_framework/domain/singleton.py +0 -9
- investing_algorithm_framework/infrastructure/models/order/order_fee.py +0 -21
- investing_algorithm_framework/infrastructure/models/position/position_cost.py +0 -32
- investing_algorithm_framework/infrastructure/repositories/order_fee_repository.py +0 -15
- investing_algorithm_framework/infrastructure/repositories/position_cost_repository.py +0 -16
- investing_algorithm_framework/infrastructure/services/market_service.py +0 -422
- investing_algorithm_framework/services/market_data_service.py +0 -75
- investing_algorithm_framework/services/order_service.py +0 -464
- investing_algorithm_framework/services/portfolio_service.py +0 -105
- investing_algorithm_framework/services/position_cost_service.py +0 -5
- investing_algorithm_framework/services/position_service.py +0 -50
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -219
- investing_algorithm_framework/setup_logging.py +0 -40
- investing_algorithm_framework-1.3.1.dist-info/AUTHORS.md +0 -8
- investing_algorithm_framework-1.3.1.dist-info/METADATA +0 -172
- investing_algorithm_framework-1.3.1.dist-info/RECORD +0 -103
- investing_algorithm_framework-1.3.1.dist-info/top_level.txt +0 -1
- {investing_algorithm_framework-1.3.1.dist-info → investing_algorithm_framework-7.25.6.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from logging import getLogger
|
|
5
|
+
from typing import Tuple, List, Dict
|
|
6
|
+
from datetime import datetime, date
|
|
7
|
+
import json
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
from investing_algorithm_framework.domain.models import Trade
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
logger = getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class BacktestMetrics:
|
|
18
|
+
"""
|
|
19
|
+
Represents the result of a backtest, including metrics such as
|
|
20
|
+
total return, annualized return, volatility, Sharpe ratio,
|
|
21
|
+
and maximum drawdown.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
backtest_date_range_name (str): The name of the date range
|
|
25
|
+
used for the backtest.
|
|
26
|
+
backtest_start_date (datetime): The start date of the backtest.
|
|
27
|
+
backtest_end_date (datetime): The end date of the backtest.
|
|
28
|
+
trading_symbol (str): The trading symbol used in the backtest.
|
|
29
|
+
initial_unallocated (float): The initial unallocated cash
|
|
30
|
+
at the start of the backtest.
|
|
31
|
+
final_value (float): The final value of the portfolio at the end
|
|
32
|
+
of the backtest.
|
|
33
|
+
equity_curve (List[Tuple[datetime, float]]): A list of
|
|
34
|
+
tuples representing the equity curve, where each tuple
|
|
35
|
+
contains a date and the corresponding portfolio value.
|
|
36
|
+
total_growth (float): The growth of the portfolio over the
|
|
37
|
+
backtest period.
|
|
38
|
+
total_growth_percentage (float): The percentage growth of the portfolio
|
|
39
|
+
over the backtest period.
|
|
40
|
+
total_net_gain (float): The total return of the backtest.
|
|
41
|
+
total_net_gain_percentage (float): The total return percentage
|
|
42
|
+
total_loss (float): The total loss of the backtest.
|
|
43
|
+
total_loss_percentage (float): The total loss percentage
|
|
44
|
+
cagr (float): The compound annual growth rate of the backtest.
|
|
45
|
+
sharpe_ratio (float): The Sharpe ratio of the backtest, indicating
|
|
46
|
+
risk-adjusted return.
|
|
47
|
+
rolling_sharpe_ratio (List[Tuple[datetime, float]): A list of rolling
|
|
48
|
+
Sharpe ratios over the backtest period.
|
|
49
|
+
sortino_ratio (float): The Sortino ratio of the backtest, focusing
|
|
50
|
+
on downside risk.
|
|
51
|
+
calmar_ratio (float): The Calmar ratio of the backtest, comparing
|
|
52
|
+
CAGR to maximum drawdown.
|
|
53
|
+
profit_factor (float): The profit factor of the backtest, calculated
|
|
54
|
+
as total profit divided by total loss.
|
|
55
|
+
annual_volatility (float): The annualized volatility of the
|
|
56
|
+
portfolio returns.
|
|
57
|
+
monthly_returns (List[Tuple[datetime, float]]): A list of monthly
|
|
58
|
+
returns during the backtest.
|
|
59
|
+
yearly_returns (List[Tuple[datetime, float]]): A list of yearly returns
|
|
60
|
+
during the backtest.
|
|
61
|
+
drawdown_series (List[Tuple[datetime, float]]): A list of drawdown
|
|
62
|
+
values over the backtest period.
|
|
63
|
+
max_drawdown (float): The maximum drawdown observed during
|
|
64
|
+
the backtest.
|
|
65
|
+
max_drawdown_absolute (float): The maximum absolute drawdown
|
|
66
|
+
observed during the backtest.
|
|
67
|
+
max_daily_drawdown (float): The maximum daily drawdown
|
|
68
|
+
observed during the backtest.
|
|
69
|
+
max_drawdown_duration (int): The duration of the maximum
|
|
70
|
+
drawdown in days.
|
|
71
|
+
trades_per_year (float): The average number of trades
|
|
72
|
+
executed per year.
|
|
73
|
+
trade_per_day (float): The average number of trades executed per day.
|
|
74
|
+
exposure_ratio (float): The exposure ratio, indicating the
|
|
75
|
+
average exposure of the portfolio.
|
|
76
|
+
cumulative_exposure (float): The cumulative exposure, indicating the
|
|
77
|
+
total exposure of the portfolio over the backtest period.
|
|
78
|
+
average_trade_size (float): The average size of trades executed
|
|
79
|
+
during the backtest.
|
|
80
|
+
average_trade_loss (float): The average loss from losing trades.
|
|
81
|
+
average_trade_loss_percentage (float): The average loss percentage
|
|
82
|
+
from losing trades.
|
|
83
|
+
average_trade_gain (float): The average gain from winning trades.
|
|
84
|
+
average_trade_gain_percentage (float): The average gain percentage
|
|
85
|
+
from winning trades.
|
|
86
|
+
average_trade_return (float): The average return from all trades.
|
|
87
|
+
average_trade_return_percentage (float): The average return percentage
|
|
88
|
+
from all trades.
|
|
89
|
+
median_trade_return (float): The median return from all trades.
|
|
90
|
+
median_trade_return_percentage (float): The median return percentage
|
|
91
|
+
from all trades.
|
|
92
|
+
number_of_positive_trades (int): The total number of profitable trades
|
|
93
|
+
executed during the backtest.
|
|
94
|
+
number_of_negative_trades (int): The total number of unprofitable
|
|
95
|
+
trades executed during the backtest.
|
|
96
|
+
best_trade (float): A string representation of the best trade,
|
|
97
|
+
including net gain and percentage.
|
|
98
|
+
worst_trade (float): A string representation of the worst trade,
|
|
99
|
+
including net loss and percentage.
|
|
100
|
+
average_trade_duration (float): The average duration of
|
|
101
|
+
trades in hours.
|
|
102
|
+
number_of_trades (int): The total number of trades executed
|
|
103
|
+
during the backtest.
|
|
104
|
+
number_of_trades_closed (int): The total number of trades close
|
|
105
|
+
during the backtest.
|
|
106
|
+
number_of_trades_opened (int): The total number of trades opened
|
|
107
|
+
during the backtest.
|
|
108
|
+
number_of_trades_open_at_end (int): The number of trades
|
|
109
|
+
still open at the end of the backtest.
|
|
110
|
+
win_rate (float): The win rate of the trades, expressed
|
|
111
|
+
as a percentage.
|
|
112
|
+
current_win_rate (float): The current win rate of the trades,
|
|
113
|
+
including open trades.
|
|
114
|
+
win_loss_ratio (float): The ratio of winning trades
|
|
115
|
+
to losing trades.
|
|
116
|
+
current_win_loss_ratio (float): The current ratio of winning
|
|
117
|
+
trades to losing trades, including open trades.
|
|
118
|
+
percentage_winning_months (float): The percentage of months
|
|
119
|
+
with positive returns.
|
|
120
|
+
percentage_winning_years (float): The percentage of years with
|
|
121
|
+
positive returns.
|
|
122
|
+
percentage_positive_trades (float): The percentage of trades that
|
|
123
|
+
were profitable.
|
|
124
|
+
percentage_negative_trades (float): The percentage of trades that
|
|
125
|
+
were unprofitable.
|
|
126
|
+
average_monthly_return (float): The average monthly return
|
|
127
|
+
of the portfolio.
|
|
128
|
+
average_monthly_return_losing_months (float): The average monthly
|
|
129
|
+
return during losing months.
|
|
130
|
+
average_monthly_return_winning_months (float): The average monthly
|
|
131
|
+
return during winning months.
|
|
132
|
+
best_month (datetime): A string representation of the best month,
|
|
133
|
+
including return and date.
|
|
134
|
+
best_year (datetime): A string representation of the best year,
|
|
135
|
+
including return and date.
|
|
136
|
+
worst_month (datetime): A string representation of the worst month,
|
|
137
|
+
including return and date.
|
|
138
|
+
worst_year (datetime): A string representation of the worst year,
|
|
139
|
+
including return and date.
|
|
140
|
+
metadata (Dict[str, str]): A dictionary to store any additional
|
|
141
|
+
metadata related to the backtest.
|
|
142
|
+
"""
|
|
143
|
+
backtest_start_date: datetime
|
|
144
|
+
backtest_end_date: datetime
|
|
145
|
+
backtest_date_range_name: str = ""
|
|
146
|
+
trading_symbol: str = ""
|
|
147
|
+
initial_unallocated: float = 0.0
|
|
148
|
+
equity_curve: List[Tuple[float, datetime]] = field(default_factory=list)
|
|
149
|
+
total_growth: float = 0.0
|
|
150
|
+
total_growth_percentage: float = 0.0
|
|
151
|
+
total_net_gain: float = 0.0
|
|
152
|
+
total_net_gain_percentage: float = 0.0
|
|
153
|
+
total_loss: float = 0.0
|
|
154
|
+
total_loss_percentage: float = 0.0
|
|
155
|
+
final_value: float = 0.0
|
|
156
|
+
cumulative_return: float = 0.0
|
|
157
|
+
cumulative_return_series: List[Tuple[float, datetime]] = \
|
|
158
|
+
field(default_factory=list)
|
|
159
|
+
cagr: float = 0.0
|
|
160
|
+
sharpe_ratio: float = 0.0
|
|
161
|
+
rolling_sharpe_ratio: List[Tuple[float, datetime]] = \
|
|
162
|
+
field(default_factory=list)
|
|
163
|
+
sortino_ratio: float = 0.0
|
|
164
|
+
calmar_ratio: float = 0.0
|
|
165
|
+
profit_factor: float = 0.0
|
|
166
|
+
gross_profit: float = None
|
|
167
|
+
gross_loss: float = None
|
|
168
|
+
annual_volatility: float = 0.0
|
|
169
|
+
monthly_returns: List[Tuple[float, datetime]] = field(default_factory=list)
|
|
170
|
+
yearly_returns: List[Tuple[float, date]] = field(default_factory=list)
|
|
171
|
+
drawdown_series: List[Tuple[float, datetime]] = field(default_factory=list)
|
|
172
|
+
max_drawdown: float = 0.0
|
|
173
|
+
max_drawdown_absolute: float = 0.0
|
|
174
|
+
max_daily_drawdown: float = 0.0
|
|
175
|
+
max_drawdown_duration: int = 0
|
|
176
|
+
trades_per_year: float = 0.0
|
|
177
|
+
trade_per_day: float = 0.0
|
|
178
|
+
exposure_ratio: float = 0.0
|
|
179
|
+
cumulative_exposure: float = 0.0
|
|
180
|
+
best_trade: Trade = None
|
|
181
|
+
worst_trade: Trade = None
|
|
182
|
+
number_of_positive_trades: int = 0
|
|
183
|
+
percentage_positive_trades: float = 0.0
|
|
184
|
+
number_of_negative_trades: int = 0
|
|
185
|
+
percentage_negative_trades: float = 0.0
|
|
186
|
+
average_trade_duration: float = 0.0
|
|
187
|
+
average_trade_size: float = 0.0
|
|
188
|
+
average_trade_loss: float = 0.0
|
|
189
|
+
average_trade_loss_percentage: float = 0.0
|
|
190
|
+
average_trade_gain: float = 0.0
|
|
191
|
+
average_trade_gain_percentage: float = 0.0
|
|
192
|
+
average_trade_return: float = 0.0
|
|
193
|
+
average_trade_return_percentage: float = 0.0
|
|
194
|
+
current_average_trade_gain: float = 0.0
|
|
195
|
+
current_average_trade_gain_percentage: float = 0.0
|
|
196
|
+
current_average_trade_return: float = 0.0
|
|
197
|
+
current_average_trade_return_percentage: float = 0.0
|
|
198
|
+
current_average_trade_duration: float = 0.0
|
|
199
|
+
current_average_trade_loss: float = 0.0
|
|
200
|
+
current_average_trade_loss_percentage: float = 0.0
|
|
201
|
+
median_trade_return: float = 0.0
|
|
202
|
+
median_trade_return_percentage: float = 0.0
|
|
203
|
+
number_of_trades: int = 0
|
|
204
|
+
number_of_trades_closed: int = 0
|
|
205
|
+
number_of_trades_opened: int = 0
|
|
206
|
+
number_of_trades_open_at_end: int = 0
|
|
207
|
+
win_rate: float = 0.0
|
|
208
|
+
current_win_rate: float = 0.0
|
|
209
|
+
win_loss_ratio: float = 0.0
|
|
210
|
+
current_win_loss_ratio: float = 0.0
|
|
211
|
+
percentage_winning_months: float = 0.0
|
|
212
|
+
percentage_winning_years: float = 0.0
|
|
213
|
+
average_monthly_return: float = 0.0
|
|
214
|
+
average_monthly_return_losing_months: float = 0.0
|
|
215
|
+
average_monthly_return_winning_months: float = 0.0
|
|
216
|
+
best_month: Tuple[float, datetime] = None
|
|
217
|
+
best_year: Tuple[float, date] = None
|
|
218
|
+
worst_month: Tuple[float, datetime] = None
|
|
219
|
+
worst_year: Tuple[float, date] = None
|
|
220
|
+
total_number_of_days: int = None
|
|
221
|
+
metadata: Dict[str, str] = field(default_factory=dict)
|
|
222
|
+
|
|
223
|
+
def __post_init__(self):
|
|
224
|
+
self.total_number_of_days = (self.backtest_end_date -
|
|
225
|
+
self.backtest_start_date).days
|
|
226
|
+
|
|
227
|
+
def to_dict(self) -> dict:
|
|
228
|
+
"""
|
|
229
|
+
Convert the BacktestMetrics instance to a dictionary.
|
|
230
|
+
Ensures all datetime values are serialized to ISO format, but
|
|
231
|
+
leaves strings unchanged.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
dict: A dictionary representation of the BacktestMetrics instance.
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
def ensure_iso(value):
|
|
238
|
+
return value.isoformat() \
|
|
239
|
+
if hasattr(value, "isoformat") else value
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
"backtest_start_date": ensure_iso(self.backtest_start_date),
|
|
243
|
+
"backtest_end_date": ensure_iso(self.backtest_end_date),
|
|
244
|
+
"backtest_date_range_name": self.backtest_date_range_name,
|
|
245
|
+
"trading_symbol": self.trading_symbol,
|
|
246
|
+
"initial_unallocated": self.initial_unallocated,
|
|
247
|
+
"equity_curve": [(value, ensure_iso(date))
|
|
248
|
+
for value, date in self.equity_curve],
|
|
249
|
+
"final_value": self.final_value,
|
|
250
|
+
"total_net_gain": self.total_net_gain,
|
|
251
|
+
"total_net_gain_percentage": self.total_net_gain_percentage,
|
|
252
|
+
"total_growth": self.total_growth,
|
|
253
|
+
"total_growth_percentage": self.total_growth_percentage,
|
|
254
|
+
"total_loss": self.total_loss,
|
|
255
|
+
"total_loss_percentage": self.total_loss_percentage,
|
|
256
|
+
"cumulative_return": self.cumulative_return,
|
|
257
|
+
"cumulative_return_series": [(value, ensure_iso(date))
|
|
258
|
+
for value, date in
|
|
259
|
+
self.cumulative_return_series],
|
|
260
|
+
"cagr": self.cagr,
|
|
261
|
+
"sharpe_ratio": self.sharpe_ratio,
|
|
262
|
+
"rolling_sharpe_ratio": [(value, ensure_iso(date))
|
|
263
|
+
for value, date in
|
|
264
|
+
self.rolling_sharpe_ratio],
|
|
265
|
+
"sortino_ratio": self.sortino_ratio,
|
|
266
|
+
"calmar_ratio": self.calmar_ratio,
|
|
267
|
+
"profit_factor": self.profit_factor,
|
|
268
|
+
"annual_volatility": self.annual_volatility,
|
|
269
|
+
"monthly_returns": [(value, ensure_iso(date))
|
|
270
|
+
for value, date in self.monthly_returns],
|
|
271
|
+
"yearly_returns": [(value, ensure_iso(date))
|
|
272
|
+
for value, date in self.yearly_returns],
|
|
273
|
+
"drawdown_series": [(value, ensure_iso(date))
|
|
274
|
+
for value, date in self.drawdown_series],
|
|
275
|
+
"max_drawdown": self.max_drawdown,
|
|
276
|
+
"max_drawdown_absolute": self.max_drawdown_absolute,
|
|
277
|
+
"max_daily_drawdown": self.max_daily_drawdown,
|
|
278
|
+
"max_drawdown_duration": self.max_drawdown_duration,
|
|
279
|
+
"trades_per_year": self.trades_per_year,
|
|
280
|
+
"trade_per_day": self.trade_per_day,
|
|
281
|
+
"exposure_ratio": self.exposure_ratio,
|
|
282
|
+
"cumulative_exposure": self.cumulative_exposure,
|
|
283
|
+
"average_trade_gain": self.average_trade_gain,
|
|
284
|
+
"average_trade_gain_percentage":
|
|
285
|
+
self.average_trade_gain_percentage,
|
|
286
|
+
"average_trade_loss": self.average_trade_loss,
|
|
287
|
+
"average_trade_loss_percentage":
|
|
288
|
+
self.average_trade_loss_percentage,
|
|
289
|
+
"average_trade_return": self.average_trade_return,
|
|
290
|
+
"average_trade_return_percentage":
|
|
291
|
+
self.average_trade_return_percentage,
|
|
292
|
+
"median_trade_return": self.median_trade_return,
|
|
293
|
+
"median_trade_return_percentage":
|
|
294
|
+
self.median_trade_return_percentage,
|
|
295
|
+
"number_of_positive_trades": self.number_of_positive_trades,
|
|
296
|
+
"percentage_positive_trades": self.percentage_positive_trades,
|
|
297
|
+
"number_of_negative_trades": self.number_of_negative_trades,
|
|
298
|
+
"percentage_negative_trades": self.percentage_negative_trades,
|
|
299
|
+
"best_trade": self.best_trade.to_dict()
|
|
300
|
+
if self.best_trade else None,
|
|
301
|
+
"worst_trade": self.worst_trade.to_dict()
|
|
302
|
+
if self.worst_trade else None,
|
|
303
|
+
"average_trade_duration": self.average_trade_duration,
|
|
304
|
+
"average_trade_size": self.average_trade_size,
|
|
305
|
+
"number_of_trades": self.number_of_trades,
|
|
306
|
+
"number_of_trades_closed": self.number_of_trades_closed,
|
|
307
|
+
"number_of_trades_opened": self.number_of_trades_opened,
|
|
308
|
+
"win_rate": self.win_rate,
|
|
309
|
+
"current_win_rate": self.current_win_rate,
|
|
310
|
+
"win_loss_ratio": self.win_loss_ratio,
|
|
311
|
+
"current_win_loss_ratio": self.current_win_loss_ratio,
|
|
312
|
+
"percentage_winning_months": self.percentage_winning_months,
|
|
313
|
+
"percentage_winning_years": self.percentage_winning_years,
|
|
314
|
+
"average_monthly_return": self.average_monthly_return,
|
|
315
|
+
"average_monthly_return_losing_months":
|
|
316
|
+
self.average_monthly_return_losing_months,
|
|
317
|
+
"average_monthly_return_winning_months":
|
|
318
|
+
self.average_monthly_return_winning_months,
|
|
319
|
+
"best_month": self.best_month,
|
|
320
|
+
"best_year": self.best_year,
|
|
321
|
+
"worst_month": self.worst_month,
|
|
322
|
+
"worst_year": self.worst_year
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
def save(self, file_path: str | Path) -> None:
|
|
326
|
+
"""
|
|
327
|
+
Save the backtest metrics to a file in JSON format. The metrics will
|
|
328
|
+
always be saved in a file named `metrics.json`
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
file_path (str): The directory where the metrics
|
|
332
|
+
file will be saved.
|
|
333
|
+
"""
|
|
334
|
+
with open(file_path, 'w') as file:
|
|
335
|
+
json.dump(self.to_dict(), file, indent=4, default=str)
|
|
336
|
+
|
|
337
|
+
@staticmethod
|
|
338
|
+
def _parse_tuple_list_datetime(
|
|
339
|
+
data: List[List]
|
|
340
|
+
) -> List[Tuple[float, datetime]]:
|
|
341
|
+
"""
|
|
342
|
+
Parse a list of [value, datetime_string]
|
|
343
|
+
into List[Tuple[float, datetime]]
|
|
344
|
+
"""
|
|
345
|
+
return [
|
|
346
|
+
(float(value), datetime.fromisoformat(date_str))
|
|
347
|
+
for value, date_str in data
|
|
348
|
+
]
|
|
349
|
+
|
|
350
|
+
@staticmethod
|
|
351
|
+
def _parse_tuple_list_date(data: List[List]) -> List[Tuple[float, date]]:
|
|
352
|
+
"""
|
|
353
|
+
Parse a list of [value, date_string] into List[Tuple[float, date]]
|
|
354
|
+
"""
|
|
355
|
+
return [
|
|
356
|
+
(float(value), datetime.fromisoformat(date_str).date())
|
|
357
|
+
for value, date_str in data
|
|
358
|
+
]
|
|
359
|
+
|
|
360
|
+
@staticmethod
|
|
361
|
+
def _parse_tuple_datetime(data) -> Tuple[float, datetime]:
|
|
362
|
+
"""Parse a [value, datetime_string] into Tuple[float, datetime]"""
|
|
363
|
+
if data is None:
|
|
364
|
+
return None, None
|
|
365
|
+
|
|
366
|
+
# Check if the value is NaN, None, or the string '<NA>'
|
|
367
|
+
if pd.isna(data[0]) or data[0] is None or data[0] == '<NA>':
|
|
368
|
+
value = pd.NA
|
|
369
|
+
else:
|
|
370
|
+
value = float(data[0])
|
|
371
|
+
|
|
372
|
+
# Parse the datetime string
|
|
373
|
+
if data[1] is None or pd.isna(data[1]) or data[1] == '<NA>':
|
|
374
|
+
date_value = None
|
|
375
|
+
else:
|
|
376
|
+
# Convert the string to a datetime object
|
|
377
|
+
date_value = datetime.fromisoformat(data[1])
|
|
378
|
+
|
|
379
|
+
return (value, date_value)
|
|
380
|
+
|
|
381
|
+
@staticmethod
|
|
382
|
+
def _parse_tuple_date(data) -> Tuple[float, date]:
|
|
383
|
+
"""Parse a [value, date_string] into Tuple[float, date]"""
|
|
384
|
+
if data is None:
|
|
385
|
+
return None, None
|
|
386
|
+
|
|
387
|
+
# Check if the value is NaN, None, or the string '<NA>'
|
|
388
|
+
if pd.isna(data[0]) or data[0] is None or data[0] == '<NA>':
|
|
389
|
+
value = pd.NA
|
|
390
|
+
else:
|
|
391
|
+
value = float(data[0])
|
|
392
|
+
|
|
393
|
+
# Parse the date string
|
|
394
|
+
if data[1] is None or pd.isna(data[1]) or data[1] == '<NA>':
|
|
395
|
+
date = None
|
|
396
|
+
else:
|
|
397
|
+
date = datetime.fromisoformat(data[1]).date()
|
|
398
|
+
|
|
399
|
+
return (value, date)
|
|
400
|
+
|
|
401
|
+
@staticmethod
|
|
402
|
+
def open(file_path: str | Path) -> 'BacktestMetrics':
|
|
403
|
+
"""
|
|
404
|
+
Open a backtest metrics file from a directory and
|
|
405
|
+
return a BacktestMetrics instance.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
file_path (str): The path to the metrics file.
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
BacktestMetrics: An instance of BacktestMetrics
|
|
412
|
+
loaded from the file.
|
|
413
|
+
"""
|
|
414
|
+
if not os.path.exists(file_path):
|
|
415
|
+
raise FileNotFoundError(f"Metrics file not found at {file_path}")
|
|
416
|
+
|
|
417
|
+
with open(file_path, 'r') as file:
|
|
418
|
+
data = json.load(file)
|
|
419
|
+
|
|
420
|
+
# Parse datetime fields
|
|
421
|
+
data['backtest_start_date'] = datetime.fromisoformat(
|
|
422
|
+
data['backtest_start_date']
|
|
423
|
+
)
|
|
424
|
+
data['backtest_end_date'] = datetime.fromisoformat(
|
|
425
|
+
data['backtest_end_date']
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# Parse tuple lists with datetime
|
|
429
|
+
data['equity_curve'] = BacktestMetrics._parse_tuple_list_datetime(
|
|
430
|
+
data.get('equity_curve', [])
|
|
431
|
+
)
|
|
432
|
+
data['rolling_sharpe_ratio'] = BacktestMetrics\
|
|
433
|
+
._parse_tuple_list_datetime(data.get('rolling_sharpe_ratio', []))
|
|
434
|
+
data['monthly_returns'] = BacktestMetrics\
|
|
435
|
+
._parse_tuple_list_datetime(data.get('monthly_returns', []))
|
|
436
|
+
data['drawdown_series'] = BacktestMetrics\
|
|
437
|
+
._parse_tuple_list_datetime(data.get('drawdown_series', []))
|
|
438
|
+
|
|
439
|
+
# Parse tuple lists with date
|
|
440
|
+
data['yearly_returns'] = BacktestMetrics\
|
|
441
|
+
._parse_tuple_list_date(data.get('yearly_returns', []))
|
|
442
|
+
|
|
443
|
+
# Parse single tuples
|
|
444
|
+
data['best_month'] = BacktestMetrics\
|
|
445
|
+
._parse_tuple_datetime(data.get('best_month'))
|
|
446
|
+
data['worst_month'] = BacktestMetrics\
|
|
447
|
+
._parse_tuple_datetime(data.get('worst_month'))
|
|
448
|
+
data['best_year'] = BacktestMetrics\
|
|
449
|
+
._parse_tuple_date(data.get('best_year'))
|
|
450
|
+
data['worst_year'] = BacktestMetrics\
|
|
451
|
+
._parse_tuple_date(data.get('worst_year'))
|
|
452
|
+
|
|
453
|
+
# Parse Trade objects if they exist
|
|
454
|
+
if data.get('best_trade'):
|
|
455
|
+
data['best_trade'] = Trade.from_dict(data['best_trade'])
|
|
456
|
+
if data.get('worst_trade'):
|
|
457
|
+
data['worst_trade'] = Trade.from_dict(data['worst_trade'])
|
|
458
|
+
|
|
459
|
+
return BacktestMetrics(**data)
|
|
460
|
+
|
|
461
|
+
def __repr__(self):
|
|
462
|
+
"""
|
|
463
|
+
Return a string representation of the Backtest instance.
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
str: A string representation of the Backtest instance.
|
|
467
|
+
"""
|
|
468
|
+
return json.dumps(
|
|
469
|
+
self.to_dict(), indent=4, sort_keys=True, default=str
|
|
470
|
+
)
|