ml4t-diagnostic 0.1.0a1__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.
- ml4t/diagnostic/AGENT.md +25 -0
- ml4t/diagnostic/__init__.py +166 -0
- ml4t/diagnostic/backends/__init__.py +10 -0
- ml4t/diagnostic/backends/adapter.py +192 -0
- ml4t/diagnostic/backends/polars_backend.py +899 -0
- ml4t/diagnostic/caching/__init__.py +40 -0
- ml4t/diagnostic/caching/cache.py +331 -0
- ml4t/diagnostic/caching/decorators.py +131 -0
- ml4t/diagnostic/caching/smart_cache.py +339 -0
- ml4t/diagnostic/config/AGENT.md +24 -0
- ml4t/diagnostic/config/README.md +267 -0
- ml4t/diagnostic/config/__init__.py +219 -0
- ml4t/diagnostic/config/barrier_config.py +277 -0
- ml4t/diagnostic/config/base.py +301 -0
- ml4t/diagnostic/config/event_config.py +148 -0
- ml4t/diagnostic/config/feature_config.py +404 -0
- ml4t/diagnostic/config/multi_signal_config.py +55 -0
- ml4t/diagnostic/config/portfolio_config.py +215 -0
- ml4t/diagnostic/config/report_config.py +391 -0
- ml4t/diagnostic/config/sharpe_config.py +202 -0
- ml4t/diagnostic/config/signal_config.py +206 -0
- ml4t/diagnostic/config/trade_analysis_config.py +310 -0
- ml4t/diagnostic/config/validation.py +279 -0
- ml4t/diagnostic/core/__init__.py +29 -0
- ml4t/diagnostic/core/numba_utils.py +315 -0
- ml4t/diagnostic/core/purging.py +372 -0
- ml4t/diagnostic/core/sampling.py +471 -0
- ml4t/diagnostic/errors/__init__.py +205 -0
- ml4t/diagnostic/evaluation/AGENT.md +26 -0
- ml4t/diagnostic/evaluation/__init__.py +437 -0
- ml4t/diagnostic/evaluation/autocorrelation.py +531 -0
- ml4t/diagnostic/evaluation/barrier_analysis.py +1050 -0
- ml4t/diagnostic/evaluation/binary_metrics.py +910 -0
- ml4t/diagnostic/evaluation/dashboard.py +715 -0
- ml4t/diagnostic/evaluation/diagnostic_plots.py +1037 -0
- ml4t/diagnostic/evaluation/distribution/__init__.py +499 -0
- ml4t/diagnostic/evaluation/distribution/moments.py +299 -0
- ml4t/diagnostic/evaluation/distribution/tails.py +777 -0
- ml4t/diagnostic/evaluation/distribution/tests.py +470 -0
- ml4t/diagnostic/evaluation/drift/__init__.py +139 -0
- ml4t/diagnostic/evaluation/drift/analysis.py +432 -0
- ml4t/diagnostic/evaluation/drift/domain_classifier.py +517 -0
- ml4t/diagnostic/evaluation/drift/population_stability_index.py +310 -0
- ml4t/diagnostic/evaluation/drift/wasserstein.py +388 -0
- ml4t/diagnostic/evaluation/event_analysis.py +647 -0
- ml4t/diagnostic/evaluation/excursion.py +390 -0
- ml4t/diagnostic/evaluation/feature_diagnostics.py +873 -0
- ml4t/diagnostic/evaluation/feature_outcome.py +666 -0
- ml4t/diagnostic/evaluation/framework.py +935 -0
- ml4t/diagnostic/evaluation/metric_registry.py +255 -0
- ml4t/diagnostic/evaluation/metrics/AGENT.md +23 -0
- ml4t/diagnostic/evaluation/metrics/__init__.py +133 -0
- ml4t/diagnostic/evaluation/metrics/basic.py +160 -0
- ml4t/diagnostic/evaluation/metrics/conditional_ic.py +469 -0
- ml4t/diagnostic/evaluation/metrics/feature_outcome.py +475 -0
- ml4t/diagnostic/evaluation/metrics/ic_statistics.py +446 -0
- ml4t/diagnostic/evaluation/metrics/importance_analysis.py +338 -0
- ml4t/diagnostic/evaluation/metrics/importance_classical.py +375 -0
- ml4t/diagnostic/evaluation/metrics/importance_mda.py +371 -0
- ml4t/diagnostic/evaluation/metrics/importance_shap.py +715 -0
- ml4t/diagnostic/evaluation/metrics/information_coefficient.py +527 -0
- ml4t/diagnostic/evaluation/metrics/interactions.py +772 -0
- ml4t/diagnostic/evaluation/metrics/monotonicity.py +226 -0
- ml4t/diagnostic/evaluation/metrics/risk_adjusted.py +324 -0
- ml4t/diagnostic/evaluation/multi_signal.py +550 -0
- ml4t/diagnostic/evaluation/portfolio_analysis/__init__.py +83 -0
- ml4t/diagnostic/evaluation/portfolio_analysis/analysis.py +734 -0
- ml4t/diagnostic/evaluation/portfolio_analysis/metrics.py +589 -0
- ml4t/diagnostic/evaluation/portfolio_analysis/results.py +334 -0
- ml4t/diagnostic/evaluation/report_generation.py +824 -0
- ml4t/diagnostic/evaluation/signal_selector.py +452 -0
- ml4t/diagnostic/evaluation/stat_registry.py +139 -0
- ml4t/diagnostic/evaluation/stationarity/__init__.py +97 -0
- ml4t/diagnostic/evaluation/stationarity/analysis.py +518 -0
- ml4t/diagnostic/evaluation/stationarity/augmented_dickey_fuller.py +296 -0
- ml4t/diagnostic/evaluation/stationarity/kpss_test.py +308 -0
- ml4t/diagnostic/evaluation/stationarity/phillips_perron.py +365 -0
- ml4t/diagnostic/evaluation/stats/AGENT.md +43 -0
- ml4t/diagnostic/evaluation/stats/__init__.py +191 -0
- ml4t/diagnostic/evaluation/stats/backtest_overfitting.py +219 -0
- ml4t/diagnostic/evaluation/stats/bootstrap.py +228 -0
- ml4t/diagnostic/evaluation/stats/deflated_sharpe_ratio.py +591 -0
- ml4t/diagnostic/evaluation/stats/false_discovery_rate.py +295 -0
- ml4t/diagnostic/evaluation/stats/hac_standard_errors.py +108 -0
- ml4t/diagnostic/evaluation/stats/minimum_track_record.py +408 -0
- ml4t/diagnostic/evaluation/stats/moments.py +164 -0
- ml4t/diagnostic/evaluation/stats/rademacher_adjustment.py +436 -0
- ml4t/diagnostic/evaluation/stats/reality_check.py +155 -0
- ml4t/diagnostic/evaluation/stats/sharpe_inference.py +219 -0
- ml4t/diagnostic/evaluation/themes.py +330 -0
- ml4t/diagnostic/evaluation/threshold_analysis.py +957 -0
- ml4t/diagnostic/evaluation/trade_analysis.py +1136 -0
- ml4t/diagnostic/evaluation/trade_dashboard/__init__.py +32 -0
- ml4t/diagnostic/evaluation/trade_dashboard/app.py +315 -0
- ml4t/diagnostic/evaluation/trade_dashboard/export/__init__.py +18 -0
- ml4t/diagnostic/evaluation/trade_dashboard/export/csv.py +82 -0
- ml4t/diagnostic/evaluation/trade_dashboard/export/html.py +276 -0
- ml4t/diagnostic/evaluation/trade_dashboard/io.py +166 -0
- ml4t/diagnostic/evaluation/trade_dashboard/normalize.py +304 -0
- ml4t/diagnostic/evaluation/trade_dashboard/stats.py +386 -0
- ml4t/diagnostic/evaluation/trade_dashboard/style.py +79 -0
- ml4t/diagnostic/evaluation/trade_dashboard/tabs/__init__.py +21 -0
- ml4t/diagnostic/evaluation/trade_dashboard/tabs/patterns.py +354 -0
- ml4t/diagnostic/evaluation/trade_dashboard/tabs/shap_analysis.py +280 -0
- ml4t/diagnostic/evaluation/trade_dashboard/tabs/stat_validation.py +186 -0
- ml4t/diagnostic/evaluation/trade_dashboard/tabs/worst_trades.py +236 -0
- ml4t/diagnostic/evaluation/trade_dashboard/types.py +129 -0
- ml4t/diagnostic/evaluation/trade_shap/__init__.py +102 -0
- ml4t/diagnostic/evaluation/trade_shap/alignment.py +188 -0
- ml4t/diagnostic/evaluation/trade_shap/characterize.py +413 -0
- ml4t/diagnostic/evaluation/trade_shap/cluster.py +302 -0
- ml4t/diagnostic/evaluation/trade_shap/explain.py +208 -0
- ml4t/diagnostic/evaluation/trade_shap/hypotheses/__init__.py +23 -0
- ml4t/diagnostic/evaluation/trade_shap/hypotheses/generator.py +290 -0
- ml4t/diagnostic/evaluation/trade_shap/hypotheses/matcher.py +251 -0
- ml4t/diagnostic/evaluation/trade_shap/hypotheses/templates.yaml +467 -0
- ml4t/diagnostic/evaluation/trade_shap/models.py +386 -0
- ml4t/diagnostic/evaluation/trade_shap/normalize.py +116 -0
- ml4t/diagnostic/evaluation/trade_shap/pipeline.py +263 -0
- ml4t/diagnostic/evaluation/trade_shap_dashboard.py +283 -0
- ml4t/diagnostic/evaluation/trade_shap_diagnostics.py +588 -0
- ml4t/diagnostic/evaluation/validated_cv.py +535 -0
- ml4t/diagnostic/evaluation/visualization.py +1050 -0
- ml4t/diagnostic/evaluation/volatility/__init__.py +45 -0
- ml4t/diagnostic/evaluation/volatility/analysis.py +351 -0
- ml4t/diagnostic/evaluation/volatility/arch.py +258 -0
- ml4t/diagnostic/evaluation/volatility/garch.py +460 -0
- ml4t/diagnostic/integration/__init__.py +48 -0
- ml4t/diagnostic/integration/backtest_contract.py +671 -0
- ml4t/diagnostic/integration/data_contract.py +316 -0
- ml4t/diagnostic/integration/engineer_contract.py +226 -0
- ml4t/diagnostic/logging/__init__.py +77 -0
- ml4t/diagnostic/logging/logger.py +245 -0
- ml4t/diagnostic/logging/performance.py +234 -0
- ml4t/diagnostic/logging/progress.py +234 -0
- ml4t/diagnostic/logging/wandb.py +412 -0
- ml4t/diagnostic/metrics/__init__.py +9 -0
- ml4t/diagnostic/metrics/percentiles.py +128 -0
- ml4t/diagnostic/py.typed +1 -0
- ml4t/diagnostic/reporting/__init__.py +43 -0
- ml4t/diagnostic/reporting/base.py +130 -0
- ml4t/diagnostic/reporting/html_renderer.py +275 -0
- ml4t/diagnostic/reporting/json_renderer.py +51 -0
- ml4t/diagnostic/reporting/markdown_renderer.py +117 -0
- ml4t/diagnostic/results/AGENT.md +24 -0
- ml4t/diagnostic/results/__init__.py +105 -0
- ml4t/diagnostic/results/barrier_results/__init__.py +36 -0
- ml4t/diagnostic/results/barrier_results/hit_rate.py +304 -0
- ml4t/diagnostic/results/barrier_results/precision_recall.py +266 -0
- ml4t/diagnostic/results/barrier_results/profit_factor.py +297 -0
- ml4t/diagnostic/results/barrier_results/tearsheet.py +397 -0
- ml4t/diagnostic/results/barrier_results/time_to_target.py +305 -0
- ml4t/diagnostic/results/barrier_results/validation.py +38 -0
- ml4t/diagnostic/results/base.py +177 -0
- ml4t/diagnostic/results/event_results.py +349 -0
- ml4t/diagnostic/results/feature_results.py +787 -0
- ml4t/diagnostic/results/multi_signal_results.py +431 -0
- ml4t/diagnostic/results/portfolio_results.py +281 -0
- ml4t/diagnostic/results/sharpe_results.py +448 -0
- ml4t/diagnostic/results/signal_results/__init__.py +74 -0
- ml4t/diagnostic/results/signal_results/ic.py +581 -0
- ml4t/diagnostic/results/signal_results/irtc.py +110 -0
- ml4t/diagnostic/results/signal_results/quantile.py +392 -0
- ml4t/diagnostic/results/signal_results/tearsheet.py +456 -0
- ml4t/diagnostic/results/signal_results/turnover.py +213 -0
- ml4t/diagnostic/results/signal_results/validation.py +147 -0
- ml4t/diagnostic/signal/AGENT.md +17 -0
- ml4t/diagnostic/signal/__init__.py +69 -0
- ml4t/diagnostic/signal/_report.py +152 -0
- ml4t/diagnostic/signal/_utils.py +261 -0
- ml4t/diagnostic/signal/core.py +275 -0
- ml4t/diagnostic/signal/quantile.py +148 -0
- ml4t/diagnostic/signal/result.py +214 -0
- ml4t/diagnostic/signal/signal_ic.py +129 -0
- ml4t/diagnostic/signal/turnover.py +182 -0
- ml4t/diagnostic/splitters/AGENT.md +19 -0
- ml4t/diagnostic/splitters/__init__.py +36 -0
- ml4t/diagnostic/splitters/base.py +501 -0
- ml4t/diagnostic/splitters/calendar.py +421 -0
- ml4t/diagnostic/splitters/calendar_config.py +91 -0
- ml4t/diagnostic/splitters/combinatorial.py +1064 -0
- ml4t/diagnostic/splitters/config.py +322 -0
- ml4t/diagnostic/splitters/cpcv/__init__.py +57 -0
- ml4t/diagnostic/splitters/cpcv/combinations.py +119 -0
- ml4t/diagnostic/splitters/cpcv/partitioning.py +263 -0
- ml4t/diagnostic/splitters/cpcv/purge_engine.py +379 -0
- ml4t/diagnostic/splitters/cpcv/windows.py +190 -0
- ml4t/diagnostic/splitters/group_isolation.py +329 -0
- ml4t/diagnostic/splitters/persistence.py +316 -0
- ml4t/diagnostic/splitters/utils.py +207 -0
- ml4t/diagnostic/splitters/walk_forward.py +757 -0
- ml4t/diagnostic/utils/__init__.py +42 -0
- ml4t/diagnostic/utils/config.py +542 -0
- ml4t/diagnostic/utils/dependencies.py +318 -0
- ml4t/diagnostic/utils/sessions.py +127 -0
- ml4t/diagnostic/validation/__init__.py +54 -0
- ml4t/diagnostic/validation/dataframe.py +274 -0
- ml4t/diagnostic/validation/returns.py +280 -0
- ml4t/diagnostic/validation/timeseries.py +299 -0
- ml4t/diagnostic/visualization/AGENT.md +19 -0
- ml4t/diagnostic/visualization/__init__.py +223 -0
- ml4t/diagnostic/visualization/backtest/__init__.py +98 -0
- ml4t/diagnostic/visualization/backtest/cost_attribution.py +762 -0
- ml4t/diagnostic/visualization/backtest/executive_summary.py +895 -0
- ml4t/diagnostic/visualization/backtest/interactive_controls.py +673 -0
- ml4t/diagnostic/visualization/backtest/statistical_validity.py +874 -0
- ml4t/diagnostic/visualization/backtest/tearsheet.py +565 -0
- ml4t/diagnostic/visualization/backtest/template_system.py +373 -0
- ml4t/diagnostic/visualization/backtest/trade_plots.py +1172 -0
- ml4t/diagnostic/visualization/barrier_plots.py +782 -0
- ml4t/diagnostic/visualization/core.py +1060 -0
- ml4t/diagnostic/visualization/dashboards/__init__.py +36 -0
- ml4t/diagnostic/visualization/dashboards/base.py +582 -0
- ml4t/diagnostic/visualization/dashboards/importance.py +801 -0
- ml4t/diagnostic/visualization/dashboards/interaction.py +263 -0
- ml4t/diagnostic/visualization/dashboards.py +43 -0
- ml4t/diagnostic/visualization/data_extraction/__init__.py +48 -0
- ml4t/diagnostic/visualization/data_extraction/importance.py +649 -0
- ml4t/diagnostic/visualization/data_extraction/interaction.py +504 -0
- ml4t/diagnostic/visualization/data_extraction/types.py +113 -0
- ml4t/diagnostic/visualization/data_extraction/validation.py +66 -0
- ml4t/diagnostic/visualization/feature_plots.py +888 -0
- ml4t/diagnostic/visualization/interaction_plots.py +618 -0
- ml4t/diagnostic/visualization/portfolio/__init__.py +41 -0
- ml4t/diagnostic/visualization/portfolio/dashboard.py +514 -0
- ml4t/diagnostic/visualization/portfolio/drawdown_plots.py +341 -0
- ml4t/diagnostic/visualization/portfolio/returns_plots.py +487 -0
- ml4t/diagnostic/visualization/portfolio/risk_plots.py +301 -0
- ml4t/diagnostic/visualization/report_generation.py +1343 -0
- ml4t/diagnostic/visualization/signal/__init__.py +103 -0
- ml4t/diagnostic/visualization/signal/dashboard.py +911 -0
- ml4t/diagnostic/visualization/signal/event_plots.py +514 -0
- ml4t/diagnostic/visualization/signal/ic_plots.py +635 -0
- ml4t/diagnostic/visualization/signal/multi_signal_dashboard.py +974 -0
- ml4t/diagnostic/visualization/signal/multi_signal_plots.py +603 -0
- ml4t/diagnostic/visualization/signal/quantile_plots.py +625 -0
- ml4t/diagnostic/visualization/signal/turnover_plots.py +400 -0
- ml4t/diagnostic/visualization/trade_shap/__init__.py +90 -0
- ml4t_diagnostic-0.1.0a1.dist-info/METADATA +1044 -0
- ml4t_diagnostic-0.1.0a1.dist-info/RECORD +242 -0
- ml4t_diagnostic-0.1.0a1.dist-info/WHEEL +4 -0
- ml4t_diagnostic-0.1.0a1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
"""Turnover and signal stability visualization plots.
|
|
2
|
+
|
|
3
|
+
This module provides interactive Plotly visualizations for turnover analysis:
|
|
4
|
+
- plot_top_bottom_turnover: Turnover rates for extreme quantiles
|
|
5
|
+
- plot_autocorrelation: Signal rank autocorrelation by lag
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import plotly.graph_objects as go
|
|
14
|
+
|
|
15
|
+
from ml4t.diagnostic.visualization.core import (
|
|
16
|
+
create_base_figure,
|
|
17
|
+
format_percentage,
|
|
18
|
+
get_theme_config,
|
|
19
|
+
validate_theme,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from ml4t.diagnostic.results.signal_results import TurnoverAnalysisResult
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def plot_top_bottom_turnover(
|
|
27
|
+
turnover_result: TurnoverAnalysisResult,
|
|
28
|
+
show_all_quantiles: bool = False,
|
|
29
|
+
theme: str | None = None,
|
|
30
|
+
width: int | None = None,
|
|
31
|
+
height: int | None = None,
|
|
32
|
+
) -> go.Figure:
|
|
33
|
+
"""Plot turnover rates for extreme (top/bottom) quantiles.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
turnover_result : TurnoverAnalysisResult
|
|
38
|
+
Turnover analysis result from SignalAnalysis.compute_turnover_analysis()
|
|
39
|
+
show_all_quantiles : bool, default False
|
|
40
|
+
Show all quantiles instead of just top and bottom
|
|
41
|
+
theme : str | None
|
|
42
|
+
Plot theme (default, dark, print, presentation)
|
|
43
|
+
width : int | None
|
|
44
|
+
Figure width in pixels
|
|
45
|
+
height : int | None
|
|
46
|
+
Figure height in pixels
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
go.Figure
|
|
51
|
+
Interactive Plotly figure
|
|
52
|
+
|
|
53
|
+
Examples
|
|
54
|
+
--------
|
|
55
|
+
>>> turnover_result = analyzer.compute_turnover_analysis()
|
|
56
|
+
>>> fig = plot_top_bottom_turnover(turnover_result)
|
|
57
|
+
>>> fig.show()
|
|
58
|
+
"""
|
|
59
|
+
theme = validate_theme(theme)
|
|
60
|
+
theme_config = get_theme_config(theme)
|
|
61
|
+
|
|
62
|
+
# Get periods and quantiles
|
|
63
|
+
periods = list(turnover_result.quantile_turnover.keys())
|
|
64
|
+
|
|
65
|
+
# Create figure
|
|
66
|
+
fig = create_base_figure(
|
|
67
|
+
title="Quantile Turnover Rates",
|
|
68
|
+
xaxis_title="Period",
|
|
69
|
+
yaxis_title="Turnover Rate",
|
|
70
|
+
width=width or theme_config["defaults"]["bar_height"],
|
|
71
|
+
height=height or theme_config["defaults"]["bar_height"],
|
|
72
|
+
theme=theme,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if show_all_quantiles:
|
|
76
|
+
# Show all quantiles
|
|
77
|
+
first_period = periods[0]
|
|
78
|
+
quantile_labels = list(turnover_result.quantile_turnover[first_period].keys())
|
|
79
|
+
|
|
80
|
+
# Use colorway for all quantiles
|
|
81
|
+
n_quantiles = len(quantile_labels)
|
|
82
|
+
colors = (theme_config["colorway"] * ((n_quantiles // len(theme_config["colorway"])) + 1))[
|
|
83
|
+
:n_quantiles
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
for i, q_label in enumerate(quantile_labels):
|
|
87
|
+
turnover_values = [
|
|
88
|
+
turnover_result.quantile_turnover[p].get(q_label, 0) for p in periods
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
fig.add_trace(
|
|
92
|
+
go.Bar(
|
|
93
|
+
x=periods,
|
|
94
|
+
y=turnover_values,
|
|
95
|
+
name=q_label,
|
|
96
|
+
marker_color=colors[i],
|
|
97
|
+
hovertemplate=f"{q_label}<br>Period: %{{x}}<br>Turnover: %{{y:.2%}}<extra></extra>",
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
fig.update_layout(barmode="group")
|
|
102
|
+
else:
|
|
103
|
+
# Just top and bottom
|
|
104
|
+
top_turnover = [turnover_result.top_quantile_turnover.get(p, 0) for p in periods]
|
|
105
|
+
bottom_turnover = [turnover_result.bottom_quantile_turnover.get(p, 0) for p in periods]
|
|
106
|
+
mean_turnover = [turnover_result.mean_turnover.get(p, 0) for p in periods]
|
|
107
|
+
|
|
108
|
+
# Top quantile
|
|
109
|
+
fig.add_trace(
|
|
110
|
+
go.Bar(
|
|
111
|
+
x=periods,
|
|
112
|
+
y=top_turnover,
|
|
113
|
+
name="Top Quantile",
|
|
114
|
+
marker_color=theme_config["colorway"][2], # Green
|
|
115
|
+
hovertemplate="Top Quantile<br>Period: %{x}<br>Turnover: %{y:.2%}<extra></extra>",
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# Bottom quantile
|
|
120
|
+
fig.add_trace(
|
|
121
|
+
go.Bar(
|
|
122
|
+
x=periods,
|
|
123
|
+
y=bottom_turnover,
|
|
124
|
+
name="Bottom Quantile",
|
|
125
|
+
marker_color=theme_config["colorway"][1], # Red
|
|
126
|
+
hovertemplate="Bottom Quantile<br>Period: %{x}<br>Turnover: %{y:.2%}<extra></extra>",
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Mean turnover line
|
|
131
|
+
fig.add_trace(
|
|
132
|
+
go.Scatter(
|
|
133
|
+
x=periods,
|
|
134
|
+
y=mean_turnover,
|
|
135
|
+
mode="lines+markers",
|
|
136
|
+
name="Mean Turnover",
|
|
137
|
+
line={"color": theme_config["colorway"][0], "width": 2, "dash": "dash"},
|
|
138
|
+
marker={"size": 8},
|
|
139
|
+
hovertemplate="Mean<br>Period: %{x}<br>Turnover: %{y:.2%}<extra></extra>",
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
fig.update_layout(barmode="group")
|
|
144
|
+
|
|
145
|
+
# Format y-axis as percentage
|
|
146
|
+
fig.update_yaxes(tickformat=".0%")
|
|
147
|
+
|
|
148
|
+
# Half-life annotation
|
|
149
|
+
half_lives = turnover_result.half_life
|
|
150
|
+
if half_lives:
|
|
151
|
+
hl_text = "<b>Signal Half-Life:</b><br>"
|
|
152
|
+
for period, hl in half_lives.items():
|
|
153
|
+
if hl is not None:
|
|
154
|
+
hl_text += f"{period}: {hl:.1f} periods<br>"
|
|
155
|
+
else:
|
|
156
|
+
hl_text += f"{period}: N/A<br>"
|
|
157
|
+
|
|
158
|
+
fig.add_annotation(
|
|
159
|
+
text=hl_text,
|
|
160
|
+
xref="paper",
|
|
161
|
+
yref="paper",
|
|
162
|
+
x=0.98,
|
|
163
|
+
y=0.98,
|
|
164
|
+
showarrow=False,
|
|
165
|
+
bgcolor="rgba(255,255,255,0.8)" if theme != "dark" else "rgba(50,50,50,0.8)",
|
|
166
|
+
bordercolor="gray",
|
|
167
|
+
borderwidth=1,
|
|
168
|
+
align="left",
|
|
169
|
+
xanchor="right",
|
|
170
|
+
yanchor="top",
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
return fig
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def plot_autocorrelation(
|
|
177
|
+
turnover_result: TurnoverAnalysisResult,
|
|
178
|
+
period: str | None = None,
|
|
179
|
+
max_lags: int | None = None,
|
|
180
|
+
show_significance: bool = True,
|
|
181
|
+
theme: str | None = None,
|
|
182
|
+
width: int | None = None,
|
|
183
|
+
height: int | None = None,
|
|
184
|
+
) -> go.Figure:
|
|
185
|
+
"""Plot signal rank autocorrelation by lag.
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
turnover_result : TurnoverAnalysisResult
|
|
190
|
+
Turnover analysis result from SignalAnalysis.compute_turnover_analysis()
|
|
191
|
+
period : str | None
|
|
192
|
+
Period to plot. If None, uses first period.
|
|
193
|
+
max_lags : int | None
|
|
194
|
+
Maximum number of lags to show. If None, shows all available.
|
|
195
|
+
show_significance : bool, default True
|
|
196
|
+
Show significance bands (±1.96/√n for 95% CI)
|
|
197
|
+
theme : str | None
|
|
198
|
+
Plot theme
|
|
199
|
+
width : int | None
|
|
200
|
+
Figure width
|
|
201
|
+
height : int | None
|
|
202
|
+
Figure height
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
go.Figure
|
|
207
|
+
Interactive Plotly figure
|
|
208
|
+
|
|
209
|
+
Examples
|
|
210
|
+
--------
|
|
211
|
+
>>> turnover_result = analyzer.compute_turnover_analysis()
|
|
212
|
+
>>> fig = plot_autocorrelation(turnover_result, period="5D")
|
|
213
|
+
>>> fig.show()
|
|
214
|
+
"""
|
|
215
|
+
theme = validate_theme(theme)
|
|
216
|
+
theme_config = get_theme_config(theme)
|
|
217
|
+
|
|
218
|
+
# Get period data
|
|
219
|
+
periods = list(turnover_result.autocorrelation.keys())
|
|
220
|
+
if period is None:
|
|
221
|
+
period = periods[0]
|
|
222
|
+
elif period not in periods:
|
|
223
|
+
raise ValueError(f"Period '{period}' not found. Available: {periods}")
|
|
224
|
+
|
|
225
|
+
ac_values = turnover_result.autocorrelation[period]
|
|
226
|
+
lags = turnover_result.autocorrelation_lags
|
|
227
|
+
|
|
228
|
+
if max_lags is not None:
|
|
229
|
+
lags = lags[:max_lags]
|
|
230
|
+
ac_values = ac_values[:max_lags]
|
|
231
|
+
|
|
232
|
+
# Create figure
|
|
233
|
+
fig = create_base_figure(
|
|
234
|
+
title=f"Signal Rank Autocorrelation ({period})",
|
|
235
|
+
xaxis_title="Lag (Periods)",
|
|
236
|
+
yaxis_title="Autocorrelation",
|
|
237
|
+
width=width or theme_config["defaults"]["bar_height"],
|
|
238
|
+
height=height or theme_config["defaults"]["bar_height"],
|
|
239
|
+
theme=theme,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Bar chart for autocorrelation
|
|
243
|
+
colors = [
|
|
244
|
+
theme_config["colorway"][0] if ac >= 0 else theme_config["colorway"][1] for ac in ac_values
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
fig.add_trace(
|
|
248
|
+
go.Bar(
|
|
249
|
+
x=lags,
|
|
250
|
+
y=ac_values,
|
|
251
|
+
marker_color=colors,
|
|
252
|
+
hovertemplate="Lag %{x}<br>AC: %{y:.4f}<extra></extra>",
|
|
253
|
+
name="Autocorrelation",
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Zero line
|
|
258
|
+
fig.add_hline(y=0, line_dash="solid", line_color="gray", opacity=0.5)
|
|
259
|
+
|
|
260
|
+
# Significance bands
|
|
261
|
+
if show_significance:
|
|
262
|
+
# Approximate CI: ±1.96/√n where n is number of observations
|
|
263
|
+
# Using a reasonable default if we don't have exact n
|
|
264
|
+
n_obs = 252 # Approximate trading days
|
|
265
|
+
ci = 1.96 / np.sqrt(n_obs)
|
|
266
|
+
|
|
267
|
+
fig.add_hline(
|
|
268
|
+
y=ci,
|
|
269
|
+
line_dash="dash",
|
|
270
|
+
line_color="red",
|
|
271
|
+
opacity=0.5,
|
|
272
|
+
annotation_text="95% CI",
|
|
273
|
+
annotation_position="right",
|
|
274
|
+
)
|
|
275
|
+
fig.add_hline(
|
|
276
|
+
y=-ci,
|
|
277
|
+
line_dash="dash",
|
|
278
|
+
line_color="red",
|
|
279
|
+
opacity=0.5,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
# Half-life annotation
|
|
283
|
+
half_life = turnover_result.half_life.get(period)
|
|
284
|
+
mean_ac = turnover_result.mean_autocorrelation.get(period, 0)
|
|
285
|
+
|
|
286
|
+
summary_text = (
|
|
287
|
+
f"<b>Signal Persistence:</b><br>"
|
|
288
|
+
f"Mean AC (Lag 1-5): {mean_ac:.4f}<br>"
|
|
289
|
+
f"Half-life: {half_life:.1f} periods"
|
|
290
|
+
if half_life
|
|
291
|
+
else "Half-life: N/A"
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
fig.add_annotation(
|
|
295
|
+
text=summary_text,
|
|
296
|
+
xref="paper",
|
|
297
|
+
yref="paper",
|
|
298
|
+
x=0.98,
|
|
299
|
+
y=0.98,
|
|
300
|
+
showarrow=False,
|
|
301
|
+
bgcolor="rgba(255,255,255,0.8)" if theme != "dark" else "rgba(50,50,50,0.8)",
|
|
302
|
+
bordercolor="gray",
|
|
303
|
+
borderwidth=1,
|
|
304
|
+
align="left",
|
|
305
|
+
xanchor="right",
|
|
306
|
+
yanchor="top",
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# Decay interpretation
|
|
310
|
+
if len(ac_values) > 0:
|
|
311
|
+
ac0 = ac_values[0]
|
|
312
|
+
decay_rate = "fast" if ac0 < 0.5 else "moderate" if ac0 < 0.8 else "slow"
|
|
313
|
+
|
|
314
|
+
fig.add_annotation(
|
|
315
|
+
text=f"Signal decay: {decay_rate}",
|
|
316
|
+
xref="paper",
|
|
317
|
+
yref="paper",
|
|
318
|
+
x=0.5,
|
|
319
|
+
y=-0.15,
|
|
320
|
+
showarrow=False,
|
|
321
|
+
font={"size": 12},
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
return fig
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def plot_turnover_heatmap(
|
|
328
|
+
turnover_result: TurnoverAnalysisResult,
|
|
329
|
+
theme: str | None = None,
|
|
330
|
+
width: int | None = None,
|
|
331
|
+
height: int | None = None,
|
|
332
|
+
) -> go.Figure:
|
|
333
|
+
"""Plot turnover rates as a heatmap (quantile × period).
|
|
334
|
+
|
|
335
|
+
Parameters
|
|
336
|
+
----------
|
|
337
|
+
turnover_result : TurnoverAnalysisResult
|
|
338
|
+
Turnover analysis result from SignalAnalysis.compute_turnover_analysis()
|
|
339
|
+
theme : str | None
|
|
340
|
+
Plot theme
|
|
341
|
+
width : int | None
|
|
342
|
+
Figure width
|
|
343
|
+
height : int | None
|
|
344
|
+
Figure height
|
|
345
|
+
|
|
346
|
+
Returns
|
|
347
|
+
-------
|
|
348
|
+
go.Figure
|
|
349
|
+
Interactive Plotly heatmap
|
|
350
|
+
"""
|
|
351
|
+
theme = validate_theme(theme)
|
|
352
|
+
theme_config = get_theme_config(theme)
|
|
353
|
+
|
|
354
|
+
# Build matrix
|
|
355
|
+
periods = list(turnover_result.quantile_turnover.keys())
|
|
356
|
+
first_period = periods[0]
|
|
357
|
+
quantile_labels = list(turnover_result.quantile_turnover[first_period].keys())
|
|
358
|
+
|
|
359
|
+
z_rows = []
|
|
360
|
+
for q_label in quantile_labels:
|
|
361
|
+
row = [turnover_result.quantile_turnover[p].get(q_label, 0) for p in periods]
|
|
362
|
+
z_rows.append(row)
|
|
363
|
+
|
|
364
|
+
z_matrix = np.array(z_rows)
|
|
365
|
+
|
|
366
|
+
# Create figure
|
|
367
|
+
fig = create_base_figure(
|
|
368
|
+
title="Turnover Rates by Quantile and Period",
|
|
369
|
+
xaxis_title="Period",
|
|
370
|
+
yaxis_title="Quantile",
|
|
371
|
+
width=width or theme_config["defaults"]["heatmap_height"] - 200,
|
|
372
|
+
height=height or 300 + 30 * len(quantile_labels),
|
|
373
|
+
theme=theme,
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
fig.add_trace(
|
|
377
|
+
go.Heatmap(
|
|
378
|
+
z=z_matrix,
|
|
379
|
+
x=periods,
|
|
380
|
+
y=quantile_labels,
|
|
381
|
+
colorscale="Oranges",
|
|
382
|
+
colorbar={"title": "Turnover", "tickformat": ".0%"},
|
|
383
|
+
hovertemplate="Quantile: %{y}<br>Period: %{x}<br>Turnover: %{z:.2%}<extra></extra>",
|
|
384
|
+
)
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
# Add text annotations
|
|
388
|
+
for i, q_label in enumerate(quantile_labels):
|
|
389
|
+
for j, period in enumerate(periods):
|
|
390
|
+
val = z_matrix[i, j]
|
|
391
|
+
text_color = "white" if val > 0.5 else "black"
|
|
392
|
+
fig.add_annotation(
|
|
393
|
+
x=period,
|
|
394
|
+
y=q_label,
|
|
395
|
+
text=format_percentage(val),
|
|
396
|
+
showarrow=False,
|
|
397
|
+
font={"size": 10, "color": text_color},
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
return fig
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Trade SHAP visualization components.
|
|
2
|
+
|
|
3
|
+
This package contains visualization and dashboard functions for Trade SHAP analysis.
|
|
4
|
+
|
|
5
|
+
Main Functions:
|
|
6
|
+
- run_diagnostics_dashboard: Run the interactive Streamlit dashboard
|
|
7
|
+
- run_polished_dashboard: Run the polished version of the dashboard
|
|
8
|
+
- extract_trade_returns: Extract trade returns from results
|
|
9
|
+
- extract_trade_data: Extract trade data for visualization
|
|
10
|
+
- export_trades_to_csv: Export trades to CSV format
|
|
11
|
+
- export_patterns_to_csv: Export patterns to CSV format
|
|
12
|
+
- export_full_report_html: Export full HTML report
|
|
13
|
+
|
|
14
|
+
Visualization Tabs:
|
|
15
|
+
- render_statistical_validation_tab: Statistical validation visualizations
|
|
16
|
+
- render_worst_trades_tab: Worst trades analysis visualizations
|
|
17
|
+
- render_shap_analysis_tab: SHAP analysis visualizations
|
|
18
|
+
- render_patterns_tab: Error pattern visualizations
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> from ml4t.diagnostic.visualization.trade_shap import run_diagnostics_dashboard
|
|
22
|
+
>>> run_diagnostics_dashboard(result)
|
|
23
|
+
|
|
24
|
+
Note:
|
|
25
|
+
All functions in this module require Streamlit to be installed. Functions are
|
|
26
|
+
lazy-loaded to avoid slow import time when Streamlit is not needed.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
# Lazy loading to avoid Streamlit import overhead (~1.3s) at module load time
|
|
30
|
+
# Functions are imported on first access
|
|
31
|
+
_dashboard_module = None
|
|
32
|
+
|
|
33
|
+
# All public names for the module
|
|
34
|
+
_LAZY_IMPORTS = {
|
|
35
|
+
"cached_extract_trade_data",
|
|
36
|
+
"cached_extract_trade_returns",
|
|
37
|
+
"display_data_summary",
|
|
38
|
+
"export_full_report_html",
|
|
39
|
+
"export_patterns_to_csv",
|
|
40
|
+
"export_trades_to_csv",
|
|
41
|
+
"extract_trade_data",
|
|
42
|
+
"extract_trade_returns",
|
|
43
|
+
"load_data_from_file",
|
|
44
|
+
"render_patterns_tab",
|
|
45
|
+
"render_shap_analysis_tab",
|
|
46
|
+
"render_statistical_validation_tab",
|
|
47
|
+
"render_worst_trades_tab",
|
|
48
|
+
"run_diagnostics_dashboard",
|
|
49
|
+
"run_polished_dashboard",
|
|
50
|
+
"serialize_result_for_cache",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def __getattr__(name: str):
|
|
55
|
+
"""Lazy load dashboard functions to avoid importing Streamlit at module load."""
|
|
56
|
+
global _dashboard_module
|
|
57
|
+
|
|
58
|
+
if name in _LAZY_IMPORTS:
|
|
59
|
+
if _dashboard_module is None:
|
|
60
|
+
from ml4t.diagnostic.evaluation import trade_shap_dashboard as _mod
|
|
61
|
+
|
|
62
|
+
_dashboard_module = _mod
|
|
63
|
+
return getattr(_dashboard_module, name)
|
|
64
|
+
|
|
65
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
# Main dashboard functions
|
|
70
|
+
"run_diagnostics_dashboard",
|
|
71
|
+
"run_polished_dashboard",
|
|
72
|
+
# Data extraction
|
|
73
|
+
"extract_trade_returns",
|
|
74
|
+
"extract_trade_data",
|
|
75
|
+
"load_data_from_file",
|
|
76
|
+
"display_data_summary",
|
|
77
|
+
# Visualization tabs
|
|
78
|
+
"render_statistical_validation_tab",
|
|
79
|
+
"render_worst_trades_tab",
|
|
80
|
+
"render_shap_analysis_tab",
|
|
81
|
+
"render_patterns_tab",
|
|
82
|
+
# Export functions
|
|
83
|
+
"export_trades_to_csv",
|
|
84
|
+
"export_patterns_to_csv",
|
|
85
|
+
"export_full_report_html",
|
|
86
|
+
# Caching utilities
|
|
87
|
+
"serialize_result_for_cache",
|
|
88
|
+
"cached_extract_trade_returns",
|
|
89
|
+
"cached_extract_trade_data",
|
|
90
|
+
]
|