investing-algorithm-framework 6.9.1__py3-none-any.whl → 7.19.15__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 +147 -44
- investing_algorithm_framework/app/__init__.py +23 -6
- investing_algorithm_framework/app/algorithm/algorithm.py +5 -41
- investing_algorithm_framework/app/algorithm/algorithm_factory.py +17 -10
- 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 +1322 -707
- investing_algorithm_framework/app/context.py +196 -88
- investing_algorithm_framework/app/eventloop.py +590 -0
- investing_algorithm_framework/app/reporting/__init__.py +16 -5
- investing_algorithm_framework/app/reporting/ascii.py +57 -202
- investing_algorithm_framework/app/reporting/backtest_report.py +284 -170
- investing_algorithm_framework/app/reporting/charts/__init__.py +10 -2
- 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 +11 -26
- investing_algorithm_framework/app/reporting/charts/line_chart.py +11 -0
- investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +51 -0
- investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +1 -1
- investing_algorithm_framework/app/reporting/generate.py +100 -114
- investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +40 -32
- investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +34 -27
- investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +23 -19
- investing_algorithm_framework/app/reporting/tables/trades_table.py +1 -1
- investing_algorithm_framework/app/reporting/tables/utils.py +1 -0
- investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +10 -16
- investing_algorithm_framework/app/strategy.py +315 -175
- investing_algorithm_framework/app/task.py +5 -3
- investing_algorithm_framework/cli/cli.py +30 -12
- investing_algorithm_framework/cli/deploy_to_aws_lambda.py +131 -34
- investing_algorithm_framework/cli/initialize_app.py +20 -1
- investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +18 -6
- 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_requirements.txt.template +2 -2
- investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +1 -1
- investing_algorithm_framework/create_app.py +3 -5
- investing_algorithm_framework/dependency_container.py +25 -39
- investing_algorithm_framework/domain/__init__.py +45 -38
- 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 +605 -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 +27 -0
- investing_algorithm_framework/domain/constants.py +6 -34
- investing_algorithm_framework/domain/data_provider.py +200 -56
- investing_algorithm_framework/domain/exceptions.py +34 -1
- investing_algorithm_framework/domain/models/__init__.py +10 -19
- investing_algorithm_framework/domain/models/base_model.py +0 -6
- 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/{market_data_type.py → data/data_type.py} +7 -7
- investing_algorithm_framework/domain/models/market/market_credential.py +6 -0
- investing_algorithm_framework/domain/models/order/order.py +34 -13
- investing_algorithm_framework/domain/models/order/order_status.py +1 -1
- investing_algorithm_framework/domain/models/order/order_type.py +1 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio.py +14 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +5 -1
- investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +51 -11
- investing_algorithm_framework/domain/models/position/__init__.py +2 -1
- investing_algorithm_framework/domain/models/position/position.py +9 -0
- investing_algorithm_framework/domain/models/position/position_size.py +41 -0
- 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 +0 -1
- investing_algorithm_framework/domain/models/strategy_profile.py +19 -151
- investing_algorithm_framework/domain/models/time_frame.py +7 -0
- investing_algorithm_framework/domain/models/time_interval.py +33 -0
- investing_algorithm_framework/domain/models/time_unit.py +63 -1
- investing_algorithm_framework/domain/models/trade/__init__.py +0 -2
- investing_algorithm_framework/domain/models/trade/trade.py +56 -32
- investing_algorithm_framework/domain/models/trade/trade_status.py +8 -2
- investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +106 -41
- investing_algorithm_framework/domain/models/trade/trade_take_profit.py +161 -99
- investing_algorithm_framework/domain/order_executor.py +19 -0
- investing_algorithm_framework/domain/portfolio_provider.py +20 -1
- investing_algorithm_framework/domain/services/__init__.py +0 -13
- investing_algorithm_framework/domain/strategy.py +1 -29
- investing_algorithm_framework/domain/utils/__init__.py +5 -1
- investing_algorithm_framework/domain/utils/custom_tqdm.py +22 -0
- investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +19 -0
- investing_algorithm_framework/domain/utils/polars.py +17 -14
- investing_algorithm_framework/download_data.py +40 -10
- investing_algorithm_framework/infrastructure/__init__.py +13 -25
- investing_algorithm_framework/infrastructure/data_providers/__init__.py +7 -4
- investing_algorithm_framework/infrastructure/data_providers/ccxt.py +811 -546
- investing_algorithm_framework/infrastructure/data_providers/csv.py +433 -122
- 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 +81 -0
- investing_algorithm_framework/infrastructure/models/__init__.py +0 -13
- investing_algorithm_framework/infrastructure/models/order/order.py +9 -3
- investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +27 -8
- investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +21 -7
- investing_algorithm_framework/infrastructure/order_executors/__init__.py +2 -0
- investing_algorithm_framework/infrastructure/order_executors/backtest_oder_executor.py +28 -0
- investing_algorithm_framework/infrastructure/repositories/repository.py +16 -2
- investing_algorithm_framework/infrastructure/repositories/trade_repository.py +2 -2
- investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +6 -0
- investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +6 -0
- investing_algorithm_framework/infrastructure/services/__init__.py +0 -4
- investing_algorithm_framework/services/__init__.py +105 -8
- investing_algorithm_framework/services/backtesting/backtest_service.py +536 -476
- investing_algorithm_framework/services/configuration_service.py +14 -4
- 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/{app/reporting → services}/metrics/__init__.py +48 -17
- investing_algorithm_framework/{app/reporting → services}/metrics/drawdown.py +10 -10
- investing_algorithm_framework/{app/reporting → services}/metrics/equity_curve.py +2 -2
- investing_algorithm_framework/{app/reporting → services}/metrics/exposure.py +60 -2
- investing_algorithm_framework/services/metrics/generate.py +358 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/profit_factor.py +36 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/recovery.py +2 -2
- investing_algorithm_framework/{app/reporting → services}/metrics/returns.py +146 -147
- investing_algorithm_framework/services/metrics/risk_free_rate.py +28 -0
- investing_algorithm_framework/{app/reporting/metrics/sharp_ratio.py → services/metrics/sharpe_ratio.py} +6 -10
- investing_algorithm_framework/{app/reporting → services}/metrics/sortino_ratio.py +3 -7
- investing_algorithm_framework/services/metrics/trades.py +500 -0
- investing_algorithm_framework/services/metrics/volatility.py +97 -0
- investing_algorithm_framework/{app/reporting → services}/metrics/win_rate.py +70 -3
- investing_algorithm_framework/services/order_service/order_backtest_service.py +21 -31
- investing_algorithm_framework/services/order_service/order_service.py +9 -71
- investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -2
- investing_algorithm_framework/services/portfolios/portfolio_service.py +3 -13
- investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +62 -96
- investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -3
- investing_algorithm_framework/services/repository_service.py +5 -2
- investing_algorithm_framework/services/trade_order_evaluator/__init__.py +9 -0
- investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +113 -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 +7 -1
- investing_algorithm_framework/services/trade_service/trade_service.py +51 -29
- 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.19.15.dist-info/METADATA +537 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/RECORD +159 -148
- investing_algorithm_framework/app/reporting/evaluation.py +0 -243
- investing_algorithm_framework/app/reporting/metrics/risk_free_rate.py +0 -8
- investing_algorithm_framework/app/reporting/metrics/volatility.py +0 -69
- investing_algorithm_framework/cli/templates/requirements_azure_function.txt.template +0 -3
- investing_algorithm_framework/domain/models/backtesting/__init__.py +0 -9
- investing_algorithm_framework/domain/models/backtesting/backtest_date_range.py +0 -47
- investing_algorithm_framework/domain/models/backtesting/backtest_position.py +0 -120
- investing_algorithm_framework/domain/models/backtesting/backtest_reports_evaluation.py +0 -0
- investing_algorithm_framework/domain/models/backtesting/backtest_results.py +0 -440
- investing_algorithm_framework/domain/models/data_source.py +0 -21
- investing_algorithm_framework/domain/models/date_range.py +0 -64
- investing_algorithm_framework/domain/models/trade/trade_risk_type.py +0 -34
- investing_algorithm_framework/domain/models/trading_data_types.py +0 -48
- investing_algorithm_framework/domain/models/trading_time_frame.py +0 -223
- investing_algorithm_framework/domain/services/market_data_sources.py +0 -543
- investing_algorithm_framework/domain/services/market_service.py +0 -153
- investing_algorithm_framework/domain/services/observable.py +0 -51
- investing_algorithm_framework/domain/services/observer.py +0 -19
- investing_algorithm_framework/infrastructure/models/market_data_sources/__init__.py +0 -16
- investing_algorithm_framework/infrastructure/models/market_data_sources/ccxt.py +0 -746
- investing_algorithm_framework/infrastructure/models/market_data_sources/csv.py +0 -270
- investing_algorithm_framework/infrastructure/models/market_data_sources/pandas.py +0 -312
- investing_algorithm_framework/infrastructure/services/market_service/__init__.py +0 -5
- investing_algorithm_framework/infrastructure/services/market_service/ccxt_market_service.py +0 -471
- investing_algorithm_framework/infrastructure/services/performance_service/__init__.py +0 -7
- investing_algorithm_framework/infrastructure/services/performance_service/backtest_performance_service.py +0 -2
- investing_algorithm_framework/infrastructure/services/performance_service/performance_service.py +0 -322
- investing_algorithm_framework/services/market_data_source_service/__init__.py +0 -10
- investing_algorithm_framework/services/market_data_source_service/backtest_market_data_source_service.py +0 -269
- investing_algorithm_framework/services/market_data_source_service/data_provider_service.py +0 -350
- investing_algorithm_framework/services/market_data_source_service/market_data_source_service.py +0 -377
- investing_algorithm_framework/services/strategy_orchestrator_service.py +0 -296
- investing_algorithm_framework-6.9.1.dist-info/METADATA +0 -440
- /investing_algorithm_framework/{app/reporting → services}/metrics/alpha.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/beta.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/cagr.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/calmar_ratio.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/mean_daily_return.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/price_efficiency.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/standard_deviation.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/treynor_ratio.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/ulcer.py +0 -0
- /investing_algorithm_framework/{app/reporting → services}/metrics/value_at_risk.py +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/LICENSE +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/WHEEL +0 -0
- {investing_algorithm_framework-6.9.1.dist-info → investing_algorithm_framework-7.19.15.dist-info}/entry_points.txt +0 -0
|
@@ -5,19 +5,16 @@ from plotly import graph_objects as go
|
|
|
5
5
|
|
|
6
6
|
def get_equity_curve_with_drawdown_chart(equity_curve_series, drawdown_series):
|
|
7
7
|
equity_curve_df = pd.DataFrame(
|
|
8
|
-
equity_curve_series, columns=["
|
|
8
|
+
equity_curve_series, columns=["value", "datetime"]
|
|
9
9
|
)
|
|
10
10
|
drawdown_df = pd.DataFrame(
|
|
11
|
-
drawdown_series, columns=["
|
|
11
|
+
drawdown_series, columns=["value", "datetime"]
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
# Normalize equity to start at 1
|
|
15
|
-
equity_curve_df["value"] = (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# Split into above and below 1
|
|
19
|
-
above_1 = equity_curve_df[equity_curve_df["value"] >= 1]
|
|
20
|
-
below_1 = equity_curve_df[equity_curve_df["value"] < 1]
|
|
15
|
+
equity_curve_df["value"] = (
|
|
16
|
+
equity_curve_df["value"] / equity_curve_df["value"].iloc[0]
|
|
17
|
+
)
|
|
21
18
|
|
|
22
19
|
fig = make_subplots(
|
|
23
20
|
rows=2,
|
|
@@ -28,27 +25,15 @@ def get_equity_curve_with_drawdown_chart(equity_curve_series, drawdown_series):
|
|
|
28
25
|
subplot_titles=["", ""]
|
|
29
26
|
)
|
|
30
27
|
|
|
31
|
-
#
|
|
32
|
-
fig.add_trace(
|
|
33
|
-
go.Scatter(
|
|
34
|
-
x=above_1["datetime"],
|
|
35
|
-
y=above_1["value"],
|
|
36
|
-
mode="lines",
|
|
37
|
-
line=dict(color="rgba(34, 139, 34, 0.9)", width=1.5),
|
|
38
|
-
name="Cumulative Equity (Gains)"
|
|
39
|
-
),
|
|
40
|
-
row=1,
|
|
41
|
-
col=1
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
# Equity curve - losses (red)
|
|
28
|
+
# Draw equity curve
|
|
45
29
|
fig.add_trace(
|
|
46
30
|
go.Scatter(
|
|
47
|
-
x=
|
|
48
|
-
y=
|
|
31
|
+
x=equity_curve_df["datetime"],
|
|
32
|
+
y=equity_curve_df["value"],
|
|
49
33
|
mode="lines",
|
|
50
|
-
line=dict(color="rgba(
|
|
51
|
-
name="
|
|
34
|
+
line=dict(color="rgba(0, 128, 0, 0.8)", width=1),
|
|
35
|
+
name="Equity Curve",
|
|
36
|
+
hovertemplate="<b>Equity</b><br>%{x}<br>Value: %{y:.2f}<extra></extra>"
|
|
52
37
|
),
|
|
53
38
|
row=1,
|
|
54
39
|
col=1
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import plotly.graph_objects as go
|
|
2
|
+
import pandas as pd
|
|
3
|
+
import plotly.io as pio
|
|
4
|
+
|
|
5
|
+
def get_ohlcv_data_completeness_chart(
|
|
6
|
+
df,
|
|
7
|
+
timeframe='1min',
|
|
8
|
+
windowsize=100,
|
|
9
|
+
title="OHLCV Data completenes"
|
|
10
|
+
):
|
|
11
|
+
df = df.copy()
|
|
12
|
+
df['Datetime'] = pd.to_datetime(df['Datetime'])
|
|
13
|
+
df = df.sort_values('Datetime').tail(windowsize)
|
|
14
|
+
start = df['Datetime'].iloc[0]
|
|
15
|
+
end = df['Datetime'].iloc[-1]
|
|
16
|
+
freq = pd.to_timedelta(timeframe)
|
|
17
|
+
expected = pd.date_range(start, end, freq=freq)
|
|
18
|
+
actual = df['Datetime']
|
|
19
|
+
missing = expected.difference(actual)
|
|
20
|
+
|
|
21
|
+
# Calculte the percentage completeness
|
|
22
|
+
completeness = len(actual) / len(expected) * 100
|
|
23
|
+
title += f" ({completeness:.2f}% complete)"
|
|
24
|
+
fig = go.Figure()
|
|
25
|
+
fig.add_trace(
|
|
26
|
+
go.Scatter(
|
|
27
|
+
x=actual,
|
|
28
|
+
y=[1]*len(actual),
|
|
29
|
+
mode='markers',
|
|
30
|
+
name='Present',
|
|
31
|
+
marker=dict(color='green', size=6)
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
fig.add_trace(
|
|
35
|
+
go.Scatter(
|
|
36
|
+
x=missing,
|
|
37
|
+
y=[1]*len(missing),
|
|
38
|
+
mode='markers',
|
|
39
|
+
name='Missing',
|
|
40
|
+
marker=dict(color='red', size=6, symbol='x')
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
fig.update_layout(
|
|
44
|
+
title=title,
|
|
45
|
+
xaxis_title='Datetime',
|
|
46
|
+
yaxis=dict(showticklabels=False),
|
|
47
|
+
height=300,
|
|
48
|
+
showlegend=True
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
return pio.to_html(fig, full_html=False, include_plotlyjs='cdn')
|
|
@@ -2,7 +2,7 @@ import pandas as pd
|
|
|
2
2
|
import plotly.graph_objects as go
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
def
|
|
5
|
+
def get_rolling_sharpe_ratio_chart(rolling_sharpe_ratio_series):
|
|
6
6
|
"""
|
|
7
7
|
Generates a Plotly figure showing the rolling Sharpe ratio series.
|
|
8
8
|
|
|
@@ -1,126 +1,77 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import logging
|
|
3
|
+
import pandas as pd
|
|
2
4
|
from jinja2 import Environment, FileSystemLoader
|
|
3
5
|
|
|
4
6
|
from .tables import create_html_time_metrics_table, \
|
|
5
7
|
create_html_trade_metrics_table, create_html_key_metrics_table, \
|
|
6
8
|
create_html_trades_table
|
|
7
9
|
from .charts import get_equity_curve_with_drawdown_chart, \
|
|
8
|
-
|
|
9
|
-
get_yearly_returns_bar_chart
|
|
10
|
-
from .
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
get_best_trade, get_best_trade_date, get_worst_trade, \
|
|
18
|
-
get_worst_trade_date, get_average_trade_duration, \
|
|
19
|
-
get_percentage_winning_months, get_percentage_winning_years, \
|
|
20
|
-
get_average_monthly_return, \
|
|
21
|
-
get_average_monthly_return_losing_months, get_win_loss_ratio, \
|
|
22
|
-
get_average_monthly_return_winning_months, get_best_month, \
|
|
23
|
-
get_best_year, get_worst_month, get_worst_year, get_trades_per_year
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def add_metrics(report, risk_free_rate=None) -> "BacktestReport":
|
|
10
|
+
get_rolling_sharpe_ratio_chart, get_monthly_returns_heatmap_chart, \
|
|
11
|
+
get_yearly_returns_bar_chart, get_ohlcv_data_completeness_chart
|
|
12
|
+
from investing_algorithm_framework.domain import TimeFrame
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("investing_algorithm_framework")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_symbol_from_file_name(file_name: str) -> str:
|
|
27
19
|
"""
|
|
28
|
-
|
|
20
|
+
Extract the symbol from the file name.
|
|
29
21
|
|
|
30
22
|
Args:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
risk-free rate will be retrieved from the US Treasury (10-year
|
|
36
|
-
yield) using the `get_risk_free_rate` function.
|
|
23
|
+
file_name (str): The file name from which to extract the symbol.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
str: The extracted symbol.
|
|
37
27
|
"""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
),
|
|
86
|
-
"Exposure": get_exposure(
|
|
87
|
-
report.results.get_trades(),
|
|
88
|
-
report.results.backtest_start_date,
|
|
89
|
-
report.results.backtest_end_date
|
|
90
|
-
),
|
|
91
|
-
"Trades winning percentage": get_win_rate(report.results.get_trades()),
|
|
92
|
-
"Trades average gain": get_average_gain(report.results.get_trades()),
|
|
93
|
-
"Trades average loss": get_average_loss(report.results.get_trades()),
|
|
94
|
-
"Best Trade": get_best_trade(report.results.get_trades()),
|
|
95
|
-
"Best Trade Date": get_best_trade_date(report.results.get_trades()),
|
|
96
|
-
"Worst Trade": get_worst_trade(report.results.get_trades()),
|
|
97
|
-
"Worst Trade Date": get_worst_trade_date(report.results.get_trades()),
|
|
98
|
-
"Average Trade Duration": get_average_trade_duration(
|
|
99
|
-
report.results.get_trades()),
|
|
100
|
-
"Number of Trades": len(report.results.get_trades()),
|
|
101
|
-
"Win Rate": get_win_rate(report.results.get_trades()),
|
|
102
|
-
"Win/Loss Ratio": get_win_loss_ratio(report.results.get_trades()),
|
|
103
|
-
"Percentage Winning Months": get_percentage_winning_months(
|
|
104
|
-
report.results.portfolio_snapshots),
|
|
105
|
-
"Percentage Winning Years": get_percentage_winning_years(
|
|
106
|
-
report.results.portfolio_snapshots),
|
|
107
|
-
"Average Monthly Return": get_average_monthly_return(
|
|
108
|
-
report.results.portfolio_snapshots),
|
|
109
|
-
"Average Monthly Return (Losing Months)":
|
|
110
|
-
get_average_monthly_return_losing_months(
|
|
111
|
-
report.results.portfolio_snapshots
|
|
112
|
-
),
|
|
113
|
-
"Average Monthly Return (Winning Months)":
|
|
114
|
-
get_average_monthly_return_winning_months(
|
|
115
|
-
report.results.portfolio_snapshots
|
|
116
|
-
),
|
|
117
|
-
"Best Month": get_best_month(report.results.portfolio_snapshots),
|
|
118
|
-
"Best Year": get_best_year(report.results.portfolio_snapshots),
|
|
119
|
-
"Worst Month": get_worst_month(report.results.portfolio_snapshots),
|
|
120
|
-
"Worst Year": get_worst_year(report.results.portfolio_snapshots),
|
|
121
|
-
}
|
|
122
|
-
report.metrics = results
|
|
123
|
-
return report
|
|
28
|
+
# Assuming the file name format is "symbol_timeframe.csv"
|
|
29
|
+
return file_name.split('_')[0].upper()
|
|
30
|
+
|
|
31
|
+
def get_market_from_file_name(file_name: str) -> str:
|
|
32
|
+
"""
|
|
33
|
+
Extract the market from the file name.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
file_name (str): The file name from which to extract the market.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
str: The extracted market.
|
|
40
|
+
"""
|
|
41
|
+
# Assuming the file name format is "symbol_market_timeframe.csv"
|
|
42
|
+
parts = file_name.split('_')
|
|
43
|
+
if len(parts) < 2:
|
|
44
|
+
raise ValueError("File name does not contain a valid market.")
|
|
45
|
+
return parts[1].upper()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_time_frame_from_file_name(file_name: str) -> TimeFrame:
|
|
49
|
+
"""
|
|
50
|
+
Extract the time frame from the file name.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
file_name (str): The file name from which to extract the time frame.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
TimeFrame: The extracted time frame.
|
|
57
|
+
"""
|
|
58
|
+
parts = file_name.split('_')
|
|
59
|
+
|
|
60
|
+
if len(parts) < 3:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
"File name does not contain a valid time frame."
|
|
63
|
+
)
|
|
64
|
+
time_frame_str = parts[3]
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
return TimeFrame.from_string(time_frame_str)
|
|
68
|
+
except ValueError:
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f"Could not extract time frame from file name: {file_path}. "
|
|
71
|
+
f"Expected format 'OHLCV_<SYMBOL>_<MARKET>_<TIME_FRAME>_<START_DATE>_<END_DATE>.csv', "
|
|
72
|
+
f"got '{time_frame_str}'."
|
|
73
|
+
)
|
|
74
|
+
|
|
124
75
|
|
|
125
76
|
def add_html_report(report) -> "BacktestReport":
|
|
126
77
|
"""
|
|
@@ -131,7 +82,7 @@ def add_html_report(report) -> "BacktestReport":
|
|
|
131
82
|
content will be added.
|
|
132
83
|
|
|
133
84
|
Returns:
|
|
134
|
-
Backtestreport
|
|
85
|
+
Backtestreport: The updated report with HTML content.
|
|
135
86
|
"""
|
|
136
87
|
metrics = report.metrics
|
|
137
88
|
# Create plots
|
|
@@ -142,7 +93,7 @@ def add_html_report(report) -> "BacktestReport":
|
|
|
142
93
|
full_html=False, include_plotlyjs='cdn',
|
|
143
94
|
config={'responsive': True}, default_width="90%"
|
|
144
95
|
)
|
|
145
|
-
rolling_sharpe_ratio_fig =
|
|
96
|
+
rolling_sharpe_ratio_fig = get_rolling_sharpe_ratio_chart(
|
|
146
97
|
metrics["Rolling Sharpe Ratio"]
|
|
147
98
|
)
|
|
148
99
|
rolling_sharpe_ratio_plot_html = rolling_sharpe_ratio_fig.to_html(
|
|
@@ -164,6 +115,40 @@ def add_html_report(report) -> "BacktestReport":
|
|
|
164
115
|
config={'responsive': True}
|
|
165
116
|
)
|
|
166
117
|
|
|
118
|
+
# Create OHLCV data completeness charts
|
|
119
|
+
data_files = report.data_files
|
|
120
|
+
ohlcv_data_completeness_charts_html = ""
|
|
121
|
+
|
|
122
|
+
for file in data_files:
|
|
123
|
+
try:
|
|
124
|
+
if file.endswith('.csv'):
|
|
125
|
+
df = pd.read_csv(file, parse_dates=['Datetime'])
|
|
126
|
+
file_name = os.path.basename(file)
|
|
127
|
+
symbol = get_symbol_from_file_name(file_name)
|
|
128
|
+
market = get_market_from_file_name(file_name)
|
|
129
|
+
time_frame = get_time_frame_from_file_name(file_name)
|
|
130
|
+
title = f"OHLCV Data Completeness for {market} - {symbol} - {time_frame.value}"
|
|
131
|
+
ohlcv_data_completeness_chart_html = \
|
|
132
|
+
get_ohlcv_data_completeness_chart(
|
|
133
|
+
df,
|
|
134
|
+
timeframe=time_frame.value,
|
|
135
|
+
windowsize=200,
|
|
136
|
+
title=title
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
ohlcv_data_completeness_charts_html += (
|
|
140
|
+
'<div class="ohlcv-data-completeness-chart">'
|
|
141
|
+
f'{ohlcv_data_completeness_chart_html}'
|
|
142
|
+
'</div>'
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.warning(
|
|
147
|
+
"Error creating OHLCV data completeness " +
|
|
148
|
+
f"chart for {file}: {e}"
|
|
149
|
+
)
|
|
150
|
+
continue
|
|
151
|
+
|
|
167
152
|
# Create HTML tables
|
|
168
153
|
key_metrics_table_html = create_html_key_metrics_table(
|
|
169
154
|
metrics, report.results
|
|
@@ -194,6 +179,7 @@ def add_html_report(report) -> "BacktestReport":
|
|
|
194
179
|
trades_metrics_table_html=trades_metrics_table_html,
|
|
195
180
|
time_metrics_table_html=time_metrics_table_html,
|
|
196
181
|
trades_table_html=trades_table_html,
|
|
182
|
+
data_completeness_charts_html=ohlcv_data_completeness_charts_html
|
|
197
183
|
)
|
|
198
184
|
report.html_report = html_rendered
|
|
199
185
|
return report
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
|
|
3
|
-
from .utils import safe_format,
|
|
3
|
+
from .utils import safe_format, safe_format_percentage
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def highlight_sharpe_and_sortino(row):
|
|
@@ -138,7 +138,6 @@ def highlight_max_drawdown(row):
|
|
|
138
138
|
if value < 5:
|
|
139
139
|
styles['Value'] = 'color: #006400; font-weight: bold;' # dark green
|
|
140
140
|
elif 10 >= value > 5:
|
|
141
|
-
print("10 >= value > 5")
|
|
142
141
|
styles['Value'] = 'color: #32CD32; font-weight: bold;' # lime green
|
|
143
142
|
elif 20 >= value > 10:
|
|
144
143
|
styles['Value'] = 'color: #FFD700; font-weight: bold;' # gold
|
|
@@ -150,42 +149,51 @@ def highlight_max_drawdown(row):
|
|
|
150
149
|
|
|
151
150
|
|
|
152
151
|
def create_html_key_metrics_table(results, report):
|
|
153
|
-
copy_results = results.copy()
|
|
152
|
+
copy_results = results.to_dict().copy()
|
|
153
|
+
format_str = "{:.2f}"
|
|
154
|
+
|
|
154
155
|
# Format some values to percentages and floats
|
|
155
|
-
copy_results['Total Return'] =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
copy_results['
|
|
160
|
-
copy_results['
|
|
161
|
-
copy_results['
|
|
162
|
-
copy_results['
|
|
163
|
-
copy_results['
|
|
164
|
-
copy_results['
|
|
165
|
-
copy_results['Max Drawdown
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
# copy_results['Sharpe Ratio'] = f"{copy_results['Sharpe Ratio']:.2f}"
|
|
170
|
-
# copy_results['Sortino Ratio'] = f"{copy_results['Sortino Ratio']:.2f}"
|
|
171
|
-
# copy_results['Profit Factor'] = f"{copy_results['Profit Factor']:.2f}"
|
|
172
|
-
# copy_results['Calmar Ratio'] = f"{copy_results['Calmar Ratio']:.2f}"
|
|
173
|
-
# copy_results['Annual Volatility'] = f"{copy_results['Annual Volatility'] * 100:.2f}%"
|
|
174
|
-
# copy_results['Max Drawdown'] = f"{copy_results['Max Drawdown'] * 100:.2f}%"
|
|
175
|
-
# copy_results['Max Drawdown Absolute'] = f"{copy_results['Max Drawdown Absolute']:.2f} {report.trading_symbol}"
|
|
176
|
-
# copy_results['Max Daily Drawdown'] = f"{copy_results['Max Daily Drawdown'] * 100:.2f}%"
|
|
177
|
-
# copy_results['Max Drawdown Duration'] = f"{copy_results['Max Drawdown Duration']} hours - {copy_results['Max Drawdown Duration'] // 24} days"
|
|
156
|
+
copy_results['Total Return'] = (
|
|
157
|
+
f"{safe_format(copy_results['total_net_gain'], format_str)} "
|
|
158
|
+
f"({safe_format_percentage(copy_results['total_net_gain_percentage'], format_str)}%)"
|
|
159
|
+
)
|
|
160
|
+
copy_results['CAGR'] = f"{safe_format_percentage(copy_results['cagr'],format_str)}%"
|
|
161
|
+
copy_results['Sharpe Ratio'] = safe_format(copy_results['sharpe_ratio'], format_str)
|
|
162
|
+
copy_results['Sortino Ratio'] = safe_format(copy_results['sortino_ratio'], format_str)
|
|
163
|
+
copy_results['Profit Factor'] = safe_format(copy_results['profit_factor'], format_str)
|
|
164
|
+
copy_results['Calmar Ratio'] = safe_format(copy_results['calmar_ratio'], format_str)
|
|
165
|
+
copy_results['Annual Volatility'] = f"{safe_format_percentage(copy_results['annual_volatility'], format_str)}%"
|
|
166
|
+
copy_results['Max Drawdown'] = f"{safe_format_percentage(copy_results['max_drawdown'], format_str)}%"
|
|
167
|
+
copy_results['Max Drawdown Absolute'] = f"{safe_format(copy_results['max_drawdown_absolute'], format_str)} {report.trading_symbol}"
|
|
168
|
+
copy_results['Max Daily Drawdown'] = f"{safe_format_percentage(copy_results['max_daily_drawdown'], format_str)}%"
|
|
169
|
+
copy_results['Max Drawdown Duration'] = f"{copy_results['max_drawdown_duration']} hours - {copy_results['max_drawdown_duration'] // 24} days"
|
|
178
170
|
|
|
179
171
|
stats = {
|
|
180
172
|
"Metric": [
|
|
181
|
-
"Total Return",
|
|
182
|
-
"
|
|
173
|
+
"Total Return",
|
|
174
|
+
"CAGR",
|
|
175
|
+
"Sharpe Ratio",
|
|
176
|
+
"Sortino Ratio",
|
|
177
|
+
"Profit Factor",
|
|
178
|
+
"Calmar Ratio",
|
|
179
|
+
"Annual Volatility",
|
|
180
|
+
"Max Drawdown",
|
|
181
|
+
"Max Drawdown Absolute",
|
|
182
|
+
"Max Daily Drawdown",
|
|
183
|
+
"Max Drawdown Duration"
|
|
183
184
|
],
|
|
184
185
|
"Value": [
|
|
185
|
-
copy_results['Total Return'],
|
|
186
|
-
copy_results['
|
|
187
|
-
copy_results['
|
|
188
|
-
copy_results['
|
|
186
|
+
copy_results['Total Return'],
|
|
187
|
+
copy_results['CAGR'],
|
|
188
|
+
copy_results['Sharpe Ratio'],
|
|
189
|
+
copy_results['Sortino Ratio'],
|
|
190
|
+
copy_results['Profit Factor'],
|
|
191
|
+
copy_results['Calmar Ratio'],
|
|
192
|
+
copy_results['Annual Volatility'],
|
|
193
|
+
copy_results['Max Drawdown'],
|
|
194
|
+
copy_results['Max Drawdown Absolute'],
|
|
195
|
+
copy_results['Max Daily Drawdown'],
|
|
196
|
+
copy_results['Max Drawdown Duration']
|
|
189
197
|
]
|
|
190
198
|
}
|
|
191
199
|
|
|
@@ -1,40 +1,45 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
|
|
3
|
-
from .utils import
|
|
3
|
+
from .utils import safe_format_percentage, safe_format_date
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
def create_html_time_metrics_table(results, report):
|
|
7
|
-
copy_results = results.copy()
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
copy_results['
|
|
7
|
+
copy_results = results.to_dict().copy()
|
|
8
|
+
start_date = report.backtest_start_date
|
|
9
|
+
end_date = report.backtest_end_date
|
|
10
|
+
string_format = "{:.2f}"
|
|
11
|
+
# Format dates
|
|
12
|
+
copy_results['Start Date'] = safe_format_date(start_date, "%Y-%m-%d %H:%M")
|
|
13
|
+
copy_results['End Date'] = safe_format_date(end_date, "%Y-%m-%d %H:%M")
|
|
14
|
+
copy_results['Percentage Winning Months'] = f"{safe_format_percentage(copy_results['percentage_winning_months'], string_format)}%"
|
|
15
|
+
copy_results['Percentage Winning Years'] = f"{safe_format_percentage(copy_results['percentage_winning_years'], string_format)}%"
|
|
16
|
+
copy_results['Average Monthly Return'] = f"{safe_format_percentage(copy_results['average_monthly_return'], string_format)}%"
|
|
17
|
+
copy_results['Average Monthly Return (Losing Months)'] = f"{safe_format_percentage(copy_results['average_monthly_return_losing_months'], string_format)}%"
|
|
18
|
+
copy_results['Average Monthly Return (Winning Months)'] = f"{safe_format_percentage(copy_results['average_monthly_return_winning_months'], string_format)}%"
|
|
13
19
|
|
|
14
|
-
if isinstance(copy_results['
|
|
15
|
-
copy_results['
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
)
|
|
20
|
+
if isinstance(copy_results['best_month'], tuple):
|
|
21
|
+
percentage = copy_results['best_month'][0]
|
|
22
|
+
date = copy_results['best_month'][1]
|
|
23
|
+
copy_results['Best Month'] = f"{safe_format_percentage(percentage, string_format)}% {safe_format_date(date, '%b %Y')}"
|
|
19
24
|
|
|
20
|
-
if isinstance(copy_results['
|
|
21
|
-
copy_results['
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if isinstance(copy_results['
|
|
26
|
-
copy_results['
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
copy_results['
|
|
32
|
-
|
|
33
|
-
safe_format_date(copy_results['Worst Year'][1], "%Y")
|
|
34
|
-
)
|
|
25
|
+
if isinstance(copy_results['worst_month'], tuple):
|
|
26
|
+
percentage = copy_results['worst_month'][0]
|
|
27
|
+
date = copy_results['worst_month'][1]
|
|
28
|
+
copy_results['Worst Month'] = f"{safe_format_percentage(percentage, string_format)}% {safe_format_date(date, '%b %Y')}"
|
|
29
|
+
|
|
30
|
+
if isinstance(copy_results['best_year'], tuple):
|
|
31
|
+
percentage = copy_results['best_year'][0]
|
|
32
|
+
date = copy_results['best_year'][1]
|
|
33
|
+
copy_results['Best Year'] = f"{safe_format_percentage(percentage, string_format)}% {safe_format_date(date, '%b %Y')}"
|
|
34
|
+
if isinstance(copy_results['worst_year'], tuple):
|
|
35
|
+
percentage = copy_results['worst_year'][0]
|
|
36
|
+
date = copy_results['worst_year'][1]
|
|
37
|
+
copy_results['Worst Year'] = f"{safe_format_percentage(percentage, string_format)}% {safe_format_date(date, '%b %Y')}"
|
|
35
38
|
|
|
36
39
|
stats = {
|
|
37
40
|
"Metric": [
|
|
41
|
+
"Start Date",
|
|
42
|
+
"End Date",
|
|
38
43
|
"% Winning Months",
|
|
39
44
|
"% Winning Years",
|
|
40
45
|
"AVG Mo Return",
|
|
@@ -46,6 +51,8 @@ def create_html_time_metrics_table(results, report):
|
|
|
46
51
|
"Worst Year",
|
|
47
52
|
],
|
|
48
53
|
"Value": [
|
|
54
|
+
copy_results['Start Date'],
|
|
55
|
+
copy_results['End Date'],
|
|
49
56
|
copy_results['Percentage Winning Months'],
|
|
50
57
|
copy_results['Percentage Winning Years'],
|
|
51
58
|
copy_results['Average Monthly Return'],
|