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.
Files changed (242) hide show
  1. ml4t/diagnostic/AGENT.md +25 -0
  2. ml4t/diagnostic/__init__.py +166 -0
  3. ml4t/diagnostic/backends/__init__.py +10 -0
  4. ml4t/diagnostic/backends/adapter.py +192 -0
  5. ml4t/diagnostic/backends/polars_backend.py +899 -0
  6. ml4t/diagnostic/caching/__init__.py +40 -0
  7. ml4t/diagnostic/caching/cache.py +331 -0
  8. ml4t/diagnostic/caching/decorators.py +131 -0
  9. ml4t/diagnostic/caching/smart_cache.py +339 -0
  10. ml4t/diagnostic/config/AGENT.md +24 -0
  11. ml4t/diagnostic/config/README.md +267 -0
  12. ml4t/diagnostic/config/__init__.py +219 -0
  13. ml4t/diagnostic/config/barrier_config.py +277 -0
  14. ml4t/diagnostic/config/base.py +301 -0
  15. ml4t/diagnostic/config/event_config.py +148 -0
  16. ml4t/diagnostic/config/feature_config.py +404 -0
  17. ml4t/diagnostic/config/multi_signal_config.py +55 -0
  18. ml4t/diagnostic/config/portfolio_config.py +215 -0
  19. ml4t/diagnostic/config/report_config.py +391 -0
  20. ml4t/diagnostic/config/sharpe_config.py +202 -0
  21. ml4t/diagnostic/config/signal_config.py +206 -0
  22. ml4t/diagnostic/config/trade_analysis_config.py +310 -0
  23. ml4t/diagnostic/config/validation.py +279 -0
  24. ml4t/diagnostic/core/__init__.py +29 -0
  25. ml4t/diagnostic/core/numba_utils.py +315 -0
  26. ml4t/diagnostic/core/purging.py +372 -0
  27. ml4t/diagnostic/core/sampling.py +471 -0
  28. ml4t/diagnostic/errors/__init__.py +205 -0
  29. ml4t/diagnostic/evaluation/AGENT.md +26 -0
  30. ml4t/diagnostic/evaluation/__init__.py +437 -0
  31. ml4t/diagnostic/evaluation/autocorrelation.py +531 -0
  32. ml4t/diagnostic/evaluation/barrier_analysis.py +1050 -0
  33. ml4t/diagnostic/evaluation/binary_metrics.py +910 -0
  34. ml4t/diagnostic/evaluation/dashboard.py +715 -0
  35. ml4t/diagnostic/evaluation/diagnostic_plots.py +1037 -0
  36. ml4t/diagnostic/evaluation/distribution/__init__.py +499 -0
  37. ml4t/diagnostic/evaluation/distribution/moments.py +299 -0
  38. ml4t/diagnostic/evaluation/distribution/tails.py +777 -0
  39. ml4t/diagnostic/evaluation/distribution/tests.py +470 -0
  40. ml4t/diagnostic/evaluation/drift/__init__.py +139 -0
  41. ml4t/diagnostic/evaluation/drift/analysis.py +432 -0
  42. ml4t/diagnostic/evaluation/drift/domain_classifier.py +517 -0
  43. ml4t/diagnostic/evaluation/drift/population_stability_index.py +310 -0
  44. ml4t/diagnostic/evaluation/drift/wasserstein.py +388 -0
  45. ml4t/diagnostic/evaluation/event_analysis.py +647 -0
  46. ml4t/diagnostic/evaluation/excursion.py +390 -0
  47. ml4t/diagnostic/evaluation/feature_diagnostics.py +873 -0
  48. ml4t/diagnostic/evaluation/feature_outcome.py +666 -0
  49. ml4t/diagnostic/evaluation/framework.py +935 -0
  50. ml4t/diagnostic/evaluation/metric_registry.py +255 -0
  51. ml4t/diagnostic/evaluation/metrics/AGENT.md +23 -0
  52. ml4t/diagnostic/evaluation/metrics/__init__.py +133 -0
  53. ml4t/diagnostic/evaluation/metrics/basic.py +160 -0
  54. ml4t/diagnostic/evaluation/metrics/conditional_ic.py +469 -0
  55. ml4t/diagnostic/evaluation/metrics/feature_outcome.py +475 -0
  56. ml4t/diagnostic/evaluation/metrics/ic_statistics.py +446 -0
  57. ml4t/diagnostic/evaluation/metrics/importance_analysis.py +338 -0
  58. ml4t/diagnostic/evaluation/metrics/importance_classical.py +375 -0
  59. ml4t/diagnostic/evaluation/metrics/importance_mda.py +371 -0
  60. ml4t/diagnostic/evaluation/metrics/importance_shap.py +715 -0
  61. ml4t/diagnostic/evaluation/metrics/information_coefficient.py +527 -0
  62. ml4t/diagnostic/evaluation/metrics/interactions.py +772 -0
  63. ml4t/diagnostic/evaluation/metrics/monotonicity.py +226 -0
  64. ml4t/diagnostic/evaluation/metrics/risk_adjusted.py +324 -0
  65. ml4t/diagnostic/evaluation/multi_signal.py +550 -0
  66. ml4t/diagnostic/evaluation/portfolio_analysis/__init__.py +83 -0
  67. ml4t/diagnostic/evaluation/portfolio_analysis/analysis.py +734 -0
  68. ml4t/diagnostic/evaluation/portfolio_analysis/metrics.py +589 -0
  69. ml4t/diagnostic/evaluation/portfolio_analysis/results.py +334 -0
  70. ml4t/diagnostic/evaluation/report_generation.py +824 -0
  71. ml4t/diagnostic/evaluation/signal_selector.py +452 -0
  72. ml4t/diagnostic/evaluation/stat_registry.py +139 -0
  73. ml4t/diagnostic/evaluation/stationarity/__init__.py +97 -0
  74. ml4t/diagnostic/evaluation/stationarity/analysis.py +518 -0
  75. ml4t/diagnostic/evaluation/stationarity/augmented_dickey_fuller.py +296 -0
  76. ml4t/diagnostic/evaluation/stationarity/kpss_test.py +308 -0
  77. ml4t/diagnostic/evaluation/stationarity/phillips_perron.py +365 -0
  78. ml4t/diagnostic/evaluation/stats/AGENT.md +43 -0
  79. ml4t/diagnostic/evaluation/stats/__init__.py +191 -0
  80. ml4t/diagnostic/evaluation/stats/backtest_overfitting.py +219 -0
  81. ml4t/diagnostic/evaluation/stats/bootstrap.py +228 -0
  82. ml4t/diagnostic/evaluation/stats/deflated_sharpe_ratio.py +591 -0
  83. ml4t/diagnostic/evaluation/stats/false_discovery_rate.py +295 -0
  84. ml4t/diagnostic/evaluation/stats/hac_standard_errors.py +108 -0
  85. ml4t/diagnostic/evaluation/stats/minimum_track_record.py +408 -0
  86. ml4t/diagnostic/evaluation/stats/moments.py +164 -0
  87. ml4t/diagnostic/evaluation/stats/rademacher_adjustment.py +436 -0
  88. ml4t/diagnostic/evaluation/stats/reality_check.py +155 -0
  89. ml4t/diagnostic/evaluation/stats/sharpe_inference.py +219 -0
  90. ml4t/diagnostic/evaluation/themes.py +330 -0
  91. ml4t/diagnostic/evaluation/threshold_analysis.py +957 -0
  92. ml4t/diagnostic/evaluation/trade_analysis.py +1136 -0
  93. ml4t/diagnostic/evaluation/trade_dashboard/__init__.py +32 -0
  94. ml4t/diagnostic/evaluation/trade_dashboard/app.py +315 -0
  95. ml4t/diagnostic/evaluation/trade_dashboard/export/__init__.py +18 -0
  96. ml4t/diagnostic/evaluation/trade_dashboard/export/csv.py +82 -0
  97. ml4t/diagnostic/evaluation/trade_dashboard/export/html.py +276 -0
  98. ml4t/diagnostic/evaluation/trade_dashboard/io.py +166 -0
  99. ml4t/diagnostic/evaluation/trade_dashboard/normalize.py +304 -0
  100. ml4t/diagnostic/evaluation/trade_dashboard/stats.py +386 -0
  101. ml4t/diagnostic/evaluation/trade_dashboard/style.py +79 -0
  102. ml4t/diagnostic/evaluation/trade_dashboard/tabs/__init__.py +21 -0
  103. ml4t/diagnostic/evaluation/trade_dashboard/tabs/patterns.py +354 -0
  104. ml4t/diagnostic/evaluation/trade_dashboard/tabs/shap_analysis.py +280 -0
  105. ml4t/diagnostic/evaluation/trade_dashboard/tabs/stat_validation.py +186 -0
  106. ml4t/diagnostic/evaluation/trade_dashboard/tabs/worst_trades.py +236 -0
  107. ml4t/diagnostic/evaluation/trade_dashboard/types.py +129 -0
  108. ml4t/diagnostic/evaluation/trade_shap/__init__.py +102 -0
  109. ml4t/diagnostic/evaluation/trade_shap/alignment.py +188 -0
  110. ml4t/diagnostic/evaluation/trade_shap/characterize.py +413 -0
  111. ml4t/diagnostic/evaluation/trade_shap/cluster.py +302 -0
  112. ml4t/diagnostic/evaluation/trade_shap/explain.py +208 -0
  113. ml4t/diagnostic/evaluation/trade_shap/hypotheses/__init__.py +23 -0
  114. ml4t/diagnostic/evaluation/trade_shap/hypotheses/generator.py +290 -0
  115. ml4t/diagnostic/evaluation/trade_shap/hypotheses/matcher.py +251 -0
  116. ml4t/diagnostic/evaluation/trade_shap/hypotheses/templates.yaml +467 -0
  117. ml4t/diagnostic/evaluation/trade_shap/models.py +386 -0
  118. ml4t/diagnostic/evaluation/trade_shap/normalize.py +116 -0
  119. ml4t/diagnostic/evaluation/trade_shap/pipeline.py +263 -0
  120. ml4t/diagnostic/evaluation/trade_shap_dashboard.py +283 -0
  121. ml4t/diagnostic/evaluation/trade_shap_diagnostics.py +588 -0
  122. ml4t/diagnostic/evaluation/validated_cv.py +535 -0
  123. ml4t/diagnostic/evaluation/visualization.py +1050 -0
  124. ml4t/diagnostic/evaluation/volatility/__init__.py +45 -0
  125. ml4t/diagnostic/evaluation/volatility/analysis.py +351 -0
  126. ml4t/diagnostic/evaluation/volatility/arch.py +258 -0
  127. ml4t/diagnostic/evaluation/volatility/garch.py +460 -0
  128. ml4t/diagnostic/integration/__init__.py +48 -0
  129. ml4t/diagnostic/integration/backtest_contract.py +671 -0
  130. ml4t/diagnostic/integration/data_contract.py +316 -0
  131. ml4t/diagnostic/integration/engineer_contract.py +226 -0
  132. ml4t/diagnostic/logging/__init__.py +77 -0
  133. ml4t/diagnostic/logging/logger.py +245 -0
  134. ml4t/diagnostic/logging/performance.py +234 -0
  135. ml4t/diagnostic/logging/progress.py +234 -0
  136. ml4t/diagnostic/logging/wandb.py +412 -0
  137. ml4t/diagnostic/metrics/__init__.py +9 -0
  138. ml4t/diagnostic/metrics/percentiles.py +128 -0
  139. ml4t/diagnostic/py.typed +1 -0
  140. ml4t/diagnostic/reporting/__init__.py +43 -0
  141. ml4t/diagnostic/reporting/base.py +130 -0
  142. ml4t/diagnostic/reporting/html_renderer.py +275 -0
  143. ml4t/diagnostic/reporting/json_renderer.py +51 -0
  144. ml4t/diagnostic/reporting/markdown_renderer.py +117 -0
  145. ml4t/diagnostic/results/AGENT.md +24 -0
  146. ml4t/diagnostic/results/__init__.py +105 -0
  147. ml4t/diagnostic/results/barrier_results/__init__.py +36 -0
  148. ml4t/diagnostic/results/barrier_results/hit_rate.py +304 -0
  149. ml4t/diagnostic/results/barrier_results/precision_recall.py +266 -0
  150. ml4t/diagnostic/results/barrier_results/profit_factor.py +297 -0
  151. ml4t/diagnostic/results/barrier_results/tearsheet.py +397 -0
  152. ml4t/diagnostic/results/barrier_results/time_to_target.py +305 -0
  153. ml4t/diagnostic/results/barrier_results/validation.py +38 -0
  154. ml4t/diagnostic/results/base.py +177 -0
  155. ml4t/diagnostic/results/event_results.py +349 -0
  156. ml4t/diagnostic/results/feature_results.py +787 -0
  157. ml4t/diagnostic/results/multi_signal_results.py +431 -0
  158. ml4t/diagnostic/results/portfolio_results.py +281 -0
  159. ml4t/diagnostic/results/sharpe_results.py +448 -0
  160. ml4t/diagnostic/results/signal_results/__init__.py +74 -0
  161. ml4t/diagnostic/results/signal_results/ic.py +581 -0
  162. ml4t/diagnostic/results/signal_results/irtc.py +110 -0
  163. ml4t/diagnostic/results/signal_results/quantile.py +392 -0
  164. ml4t/diagnostic/results/signal_results/tearsheet.py +456 -0
  165. ml4t/diagnostic/results/signal_results/turnover.py +213 -0
  166. ml4t/diagnostic/results/signal_results/validation.py +147 -0
  167. ml4t/diagnostic/signal/AGENT.md +17 -0
  168. ml4t/diagnostic/signal/__init__.py +69 -0
  169. ml4t/diagnostic/signal/_report.py +152 -0
  170. ml4t/diagnostic/signal/_utils.py +261 -0
  171. ml4t/diagnostic/signal/core.py +275 -0
  172. ml4t/diagnostic/signal/quantile.py +148 -0
  173. ml4t/diagnostic/signal/result.py +214 -0
  174. ml4t/diagnostic/signal/signal_ic.py +129 -0
  175. ml4t/diagnostic/signal/turnover.py +182 -0
  176. ml4t/diagnostic/splitters/AGENT.md +19 -0
  177. ml4t/diagnostic/splitters/__init__.py +36 -0
  178. ml4t/diagnostic/splitters/base.py +501 -0
  179. ml4t/diagnostic/splitters/calendar.py +421 -0
  180. ml4t/diagnostic/splitters/calendar_config.py +91 -0
  181. ml4t/diagnostic/splitters/combinatorial.py +1064 -0
  182. ml4t/diagnostic/splitters/config.py +322 -0
  183. ml4t/diagnostic/splitters/cpcv/__init__.py +57 -0
  184. ml4t/diagnostic/splitters/cpcv/combinations.py +119 -0
  185. ml4t/diagnostic/splitters/cpcv/partitioning.py +263 -0
  186. ml4t/diagnostic/splitters/cpcv/purge_engine.py +379 -0
  187. ml4t/diagnostic/splitters/cpcv/windows.py +190 -0
  188. ml4t/diagnostic/splitters/group_isolation.py +329 -0
  189. ml4t/diagnostic/splitters/persistence.py +316 -0
  190. ml4t/diagnostic/splitters/utils.py +207 -0
  191. ml4t/diagnostic/splitters/walk_forward.py +757 -0
  192. ml4t/diagnostic/utils/__init__.py +42 -0
  193. ml4t/diagnostic/utils/config.py +542 -0
  194. ml4t/diagnostic/utils/dependencies.py +318 -0
  195. ml4t/diagnostic/utils/sessions.py +127 -0
  196. ml4t/diagnostic/validation/__init__.py +54 -0
  197. ml4t/diagnostic/validation/dataframe.py +274 -0
  198. ml4t/diagnostic/validation/returns.py +280 -0
  199. ml4t/diagnostic/validation/timeseries.py +299 -0
  200. ml4t/diagnostic/visualization/AGENT.md +19 -0
  201. ml4t/diagnostic/visualization/__init__.py +223 -0
  202. ml4t/diagnostic/visualization/backtest/__init__.py +98 -0
  203. ml4t/diagnostic/visualization/backtest/cost_attribution.py +762 -0
  204. ml4t/diagnostic/visualization/backtest/executive_summary.py +895 -0
  205. ml4t/diagnostic/visualization/backtest/interactive_controls.py +673 -0
  206. ml4t/diagnostic/visualization/backtest/statistical_validity.py +874 -0
  207. ml4t/diagnostic/visualization/backtest/tearsheet.py +565 -0
  208. ml4t/diagnostic/visualization/backtest/template_system.py +373 -0
  209. ml4t/diagnostic/visualization/backtest/trade_plots.py +1172 -0
  210. ml4t/diagnostic/visualization/barrier_plots.py +782 -0
  211. ml4t/diagnostic/visualization/core.py +1060 -0
  212. ml4t/diagnostic/visualization/dashboards/__init__.py +36 -0
  213. ml4t/diagnostic/visualization/dashboards/base.py +582 -0
  214. ml4t/diagnostic/visualization/dashboards/importance.py +801 -0
  215. ml4t/diagnostic/visualization/dashboards/interaction.py +263 -0
  216. ml4t/diagnostic/visualization/dashboards.py +43 -0
  217. ml4t/diagnostic/visualization/data_extraction/__init__.py +48 -0
  218. ml4t/diagnostic/visualization/data_extraction/importance.py +649 -0
  219. ml4t/diagnostic/visualization/data_extraction/interaction.py +504 -0
  220. ml4t/diagnostic/visualization/data_extraction/types.py +113 -0
  221. ml4t/diagnostic/visualization/data_extraction/validation.py +66 -0
  222. ml4t/diagnostic/visualization/feature_plots.py +888 -0
  223. ml4t/diagnostic/visualization/interaction_plots.py +618 -0
  224. ml4t/diagnostic/visualization/portfolio/__init__.py +41 -0
  225. ml4t/diagnostic/visualization/portfolio/dashboard.py +514 -0
  226. ml4t/diagnostic/visualization/portfolio/drawdown_plots.py +341 -0
  227. ml4t/diagnostic/visualization/portfolio/returns_plots.py +487 -0
  228. ml4t/diagnostic/visualization/portfolio/risk_plots.py +301 -0
  229. ml4t/diagnostic/visualization/report_generation.py +1343 -0
  230. ml4t/diagnostic/visualization/signal/__init__.py +103 -0
  231. ml4t/diagnostic/visualization/signal/dashboard.py +911 -0
  232. ml4t/diagnostic/visualization/signal/event_plots.py +514 -0
  233. ml4t/diagnostic/visualization/signal/ic_plots.py +635 -0
  234. ml4t/diagnostic/visualization/signal/multi_signal_dashboard.py +974 -0
  235. ml4t/diagnostic/visualization/signal/multi_signal_plots.py +603 -0
  236. ml4t/diagnostic/visualization/signal/quantile_plots.py +625 -0
  237. ml4t/diagnostic/visualization/signal/turnover_plots.py +400 -0
  238. ml4t/diagnostic/visualization/trade_shap/__init__.py +90 -0
  239. ml4t_diagnostic-0.1.0a1.dist-info/METADATA +1044 -0
  240. ml4t_diagnostic-0.1.0a1.dist-info/RECORD +242 -0
  241. ml4t_diagnostic-0.1.0a1.dist-info/WHEEL +4 -0
  242. ml4t_diagnostic-0.1.0a1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,130 @@
1
+ """Base report generation functionality."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from enum import Enum
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from ml4t.diagnostic.results.base import BaseResult
11
+
12
+
13
+ class ReportFormat(str, Enum):
14
+ """Supported report formats."""
15
+
16
+ HTML = "html"
17
+ JSON = "json"
18
+ MARKDOWN = "markdown"
19
+
20
+
21
+ class ReportGenerator(ABC):
22
+ """Base class for report generators.
23
+
24
+ Provides abstract interface for rendering evaluation results
25
+ into different formats (HTML, JSON, Markdown).
26
+
27
+ Subclasses must implement render() method.
28
+
29
+ Examples:
30
+ >>> generator = HTMLReportGenerator()
31
+ >>> report = generator.render(result)
32
+ >>> generator.save(report, "output.html")
33
+ """
34
+
35
+ @abstractmethod
36
+ def render(self, result: BaseResult, **options: Any) -> str:
37
+ """Render result to string format.
38
+
39
+ Args:
40
+ result: Evaluation result to render
41
+ **options: Format-specific rendering options
42
+
43
+ Returns:
44
+ Rendered report as string
45
+ """
46
+ ...
47
+
48
+ def save(self, content: str, filepath: str | Path) -> None:
49
+ """Save rendered report to file.
50
+
51
+ Args:
52
+ content: Rendered report content
53
+ filepath: Output file path
54
+ """
55
+ path = Path(filepath)
56
+ path.parent.mkdir(parents=True, exist_ok=True)
57
+ path.write_text(content, encoding="utf-8")
58
+
59
+
60
+ class ReportFactory:
61
+ """Factory for creating report generators.
62
+
63
+ Provides convenient access to all report generators via format enum.
64
+
65
+ Examples:
66
+ >>> generator = ReportFactory.create(ReportFormat.HTML)
67
+ >>> report = generator.render(result)
68
+
69
+ >>> # Or use convenience method
70
+ >>> report = ReportFactory.render(result, ReportFormat.MARKDOWN)
71
+ """
72
+
73
+ _generators: dict[ReportFormat, type[ReportGenerator]] = {}
74
+
75
+ @classmethod
76
+ def register(cls, format: ReportFormat, generator_class: type[ReportGenerator]) -> None:
77
+ """Register a report generator for a format.
78
+
79
+ Args:
80
+ format: Report format enum
81
+ generator_class: Generator class to register
82
+ """
83
+ cls._generators[format] = generator_class
84
+
85
+ @classmethod
86
+ def create(cls, format: ReportFormat) -> ReportGenerator:
87
+ """Create report generator for format.
88
+
89
+ Args:
90
+ format: Desired report format
91
+
92
+ Returns:
93
+ Report generator instance
94
+
95
+ Raises:
96
+ ValueError: If format not registered
97
+ """
98
+ if format not in cls._generators:
99
+ raise ValueError(
100
+ f"No generator registered for format: {format}. Available: {list(cls._generators.keys())}"
101
+ )
102
+ return cls._generators[format]()
103
+
104
+ @classmethod
105
+ def render(cls, result: BaseResult, format: ReportFormat, **options: Any) -> str:
106
+ """Convenience method to create generator and render in one call.
107
+
108
+ Args:
109
+ result: Evaluation result to render
110
+ format: Desired report format
111
+ **options: Format-specific rendering options
112
+
113
+ Returns:
114
+ Rendered report as string
115
+
116
+ Examples:
117
+ >>> report = ReportFactory.render(result, ReportFormat.HTML)
118
+ >>> report = ReportFactory.render(result, ReportFormat.JSON, indent=4)
119
+ """
120
+ generator = cls.create(format)
121
+ return generator.render(result, **options)
122
+
123
+ @classmethod
124
+ def available_formats(cls) -> list[ReportFormat]:
125
+ """List all registered report formats.
126
+
127
+ Returns:
128
+ List of available formats
129
+ """
130
+ return list(cls._generators.keys())
@@ -0,0 +1,275 @@
1
+ """HTML report renderer with template support."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from ml4t.diagnostic.reporting.base import ReportFormat, ReportGenerator
8
+ from ml4t.diagnostic.results.base import BaseResult
9
+
10
+
11
+ class HTMLReportGenerator(ReportGenerator):
12
+ """Generate HTML reports from evaluation results.
13
+
14
+ Supports custom templates and styling.
15
+
16
+ Examples:
17
+ >>> generator = HTMLReportGenerator()
18
+ >>> report = generator.render(result, template="default")
19
+ >>> generator.save(report, "report.html")
20
+
21
+ >>> # Custom template
22
+ >>> custom_template = '''
23
+ ... <html><body>
24
+ ... <h1>{analysis_type}</h1>
25
+ ... {content}
26
+ ... </body></html>
27
+ ... '''
28
+ >>> report = generator.render(result, custom_template=custom_template)
29
+ """
30
+
31
+ DEFAULT_TEMPLATE = """<!DOCTYPE html>
32
+ <html lang="en">
33
+ <head>
34
+ <meta charset="UTF-8">
35
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
36
+ <title>{title}</title>
37
+ <style>
38
+ body {{
39
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
40
+ max-width: 1200px;
41
+ margin: 0 auto;
42
+ padding: 20px;
43
+ background-color: #f5f5f5;
44
+ }}
45
+ .container {{
46
+ background-color: white;
47
+ padding: 30px;
48
+ border-radius: 8px;
49
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
50
+ }}
51
+ h1 {{
52
+ color: #2c3e50;
53
+ border-bottom: 2px solid #3498db;
54
+ padding-bottom: 10px;
55
+ }}
56
+ h2 {{
57
+ color: #34495e;
58
+ margin-top: 30px;
59
+ }}
60
+ .metadata {{
61
+ background-color: #ecf0f1;
62
+ padding: 15px;
63
+ border-radius: 4px;
64
+ margin-bottom: 20px;
65
+ }}
66
+ .metadata-item {{
67
+ margin: 5px 0;
68
+ }}
69
+ .summary {{
70
+ background-color: #fff9e6;
71
+ border-left: 4px solid #f39c12;
72
+ padding: 15px;
73
+ margin: 20px 0;
74
+ }}
75
+ table {{
76
+ width: 100%;
77
+ border-collapse: collapse;
78
+ margin: 20px 0;
79
+ }}
80
+ th {{
81
+ background-color: #3498db;
82
+ color: white;
83
+ padding: 12px;
84
+ text-align: left;
85
+ }}
86
+ td {{
87
+ padding: 10px;
88
+ border-bottom: 1px solid #ddd;
89
+ }}
90
+ tr:hover {{
91
+ background-color: #f5f5f5;
92
+ }}
93
+ .footer {{
94
+ margin-top: 40px;
95
+ padding-top: 20px;
96
+ border-top: 1px solid #ddd;
97
+ color: #7f8c8d;
98
+ font-size: 0.9em;
99
+ }}
100
+ </style>
101
+ </head>
102
+ <body>
103
+ <div class="container">
104
+ <h1>{title}</h1>
105
+ {metadata}
106
+ {summary}
107
+ {dataframes}
108
+ <div class="footer">
109
+ Generated by ML4T Diagnostic {version}
110
+ </div>
111
+ </div>
112
+ </body>
113
+ </html>"""
114
+
115
+ def render(self, result: BaseResult, **options: Any) -> str:
116
+ """Render result to HTML format.
117
+
118
+ Args:
119
+ result: Evaluation result to render
120
+ **options: HTML rendering options
121
+ - template: Template name or "default" (default: "default")
122
+ - custom_template: Custom HTML template string
123
+ - include_metadata: Include metadata section (default: True)
124
+ - include_dataframes: Include DataFrame tables (default: True)
125
+ - max_rows: Max rows to show in DataFrame tables (default: 50)
126
+
127
+ Returns:
128
+ HTML string
129
+ """
130
+ template = options.get("custom_template") or self.DEFAULT_TEMPLATE
131
+ include_metadata = options.get("include_metadata", True)
132
+ include_dataframes = options.get("include_dataframes", True)
133
+ max_rows = options.get("max_rows", 50)
134
+
135
+ # Build template variables
136
+ analysis_type = result.analysis_type.replace("_", " ").title()
137
+ title = f"{analysis_type} Report"
138
+
139
+ # Metadata section
140
+ metadata_html = ""
141
+ if include_metadata:
142
+ metadata_html = f"""
143
+ <div class="metadata">
144
+ <h2>Metadata</h2>
145
+ <div class="metadata-item"><strong>Analysis Type:</strong> {result.analysis_type}</div>
146
+ <div class="metadata-item"><strong>Created:</strong> {result.created_at}</div>
147
+ <div class="metadata-item"><strong>ML4T Diagnostic Version:</strong> {result.version}</div>
148
+ </div>
149
+ """
150
+
151
+ # Summary section
152
+ summary_html = f"""
153
+ <div class="summary">
154
+ <h2>Summary</h2>
155
+ <pre>{self._escape_html(result.summary())}</pre>
156
+ </div>
157
+ """
158
+
159
+ # DataFrame sections
160
+ dataframes_html = ""
161
+ if include_dataframes:
162
+ try:
163
+ available_dfs = result.list_available_dataframes()
164
+ if available_dfs:
165
+ df_sections = []
166
+ for df_name in available_dfs:
167
+ df = result.get_dataframe(df_name)
168
+
169
+ # Limit rows if needed
170
+ if len(df) > max_rows:
171
+ display_df = df.head(max_rows)
172
+ caption = f"Showing first {max_rows} of {len(df)} rows"
173
+ else:
174
+ display_df = df
175
+ caption = ""
176
+
177
+ table_html = self._dataframe_to_html(
178
+ display_df, title=df_name.replace("_", " ").title(), caption=caption
179
+ )
180
+ df_sections.append(table_html)
181
+
182
+ dataframes_html = "\n".join(df_sections)
183
+ except (NotImplementedError, ValueError):
184
+ # Some results may not have DataFrames
185
+ pass
186
+
187
+ # Fill template
188
+ html = template.format(
189
+ title=title,
190
+ analysis_type=analysis_type,
191
+ metadata=metadata_html,
192
+ summary=summary_html,
193
+ dataframes=dataframes_html,
194
+ version=result.version,
195
+ )
196
+
197
+ return html
198
+
199
+ def _dataframe_to_html(self, df, title: str = "", caption: str = "") -> str:
200
+ """Convert Polars DataFrame to HTML table.
201
+
202
+ Args:
203
+ df: Polars DataFrame
204
+ title: Table title
205
+ caption: Optional caption
206
+
207
+ Returns:
208
+ HTML table string
209
+ """
210
+ html_parts = []
211
+
212
+ if title:
213
+ html_parts.append(f"<h2>{title}</h2>")
214
+
215
+ if caption:
216
+ html_parts.append(f"<p><em>{caption}</em></p>")
217
+
218
+ # Table header
219
+ html_parts.append("<table>")
220
+ html_parts.append("<thead><tr>")
221
+ for col in df.columns:
222
+ html_parts.append(f"<th>{self._escape_html(col)}</th>")
223
+ html_parts.append("</tr></thead>")
224
+
225
+ # Table body
226
+ html_parts.append("<tbody>")
227
+ for row in df.iter_rows():
228
+ html_parts.append("<tr>")
229
+ for val in row:
230
+ formatted = self._format_cell(val)
231
+ html_parts.append(f"<td>{formatted}</td>")
232
+ html_parts.append("</tr>")
233
+ html_parts.append("</tbody>")
234
+ html_parts.append("</table>")
235
+
236
+ return "\n".join(html_parts)
237
+
238
+ def _format_cell(self, value: Any) -> str:
239
+ """Format cell value for HTML display.
240
+
241
+ Args:
242
+ value: Cell value
243
+
244
+ Returns:
245
+ Formatted HTML string
246
+ """
247
+ if value is None:
248
+ return "<em>null</em>"
249
+ elif isinstance(value, float):
250
+ return f"{value:.4f}"
251
+ else:
252
+ return self._escape_html(str(value))
253
+
254
+ def _escape_html(self, text: str) -> str:
255
+ """Escape HTML special characters.
256
+
257
+ Args:
258
+ text: Text to escape
259
+
260
+ Returns:
261
+ Escaped text
262
+ """
263
+ return (
264
+ text.replace("&", "&amp;")
265
+ .replace("<", "&lt;")
266
+ .replace(">", "&gt;")
267
+ .replace('"', "&quot;")
268
+ .replace("'", "&#39;")
269
+ )
270
+
271
+
272
+ # Register with factory
273
+ from ml4t.diagnostic.reporting.base import ReportFactory # noqa: E402
274
+
275
+ ReportFactory.register(ReportFormat.HTML, HTMLReportGenerator)
@@ -0,0 +1,51 @@
1
+ """JSON report renderer."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from ml4t.diagnostic.reporting.base import ReportFormat, ReportGenerator
8
+ from ml4t.diagnostic.results.base import BaseResult
9
+
10
+
11
+ class JSONReportGenerator(ReportGenerator):
12
+ """Generate JSON reports from evaluation results.
13
+
14
+ Leverages BaseResult's built-in JSON serialization.
15
+
16
+ Examples:
17
+ >>> generator = JSONReportGenerator()
18
+ >>> report = generator.render(result, indent=2)
19
+ >>> generator.save(report, "report.json")
20
+ """
21
+
22
+ def render(self, result: BaseResult, **options: Any) -> str:
23
+ """Render result to JSON format.
24
+
25
+ Args:
26
+ result: Evaluation result to render
27
+ **options: JSON rendering options
28
+ - indent: Indentation level (default: 2)
29
+ - exclude_none: Exclude None values (default: False)
30
+
31
+ Returns:
32
+ JSON string
33
+ """
34
+ indent = options.get("indent", 2)
35
+ exclude_none = options.get("exclude_none", False)
36
+
37
+ # Use BaseResult's built-in JSON export
38
+ if exclude_none:
39
+ # Get dict first, then convert to JSON
40
+ data = result.to_dict(exclude_none=True)
41
+ import json
42
+
43
+ return json.dumps(data, indent=indent)
44
+ else:
45
+ return result.to_json_string(indent=indent)
46
+
47
+
48
+ # Register with factory
49
+ from ml4t.diagnostic.reporting.base import ReportFactory # noqa: E402
50
+
51
+ ReportFactory.register(ReportFormat.JSON, JSONReportGenerator)
@@ -0,0 +1,117 @@
1
+ """Markdown report renderer."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from ml4t.diagnostic.reporting.base import ReportFormat, ReportGenerator
8
+ from ml4t.diagnostic.results.base import BaseResult
9
+
10
+
11
+ class MarkdownReportGenerator(ReportGenerator):
12
+ """Generate Markdown reports from evaluation results.
13
+
14
+ Creates human-readable Markdown documentation from results.
15
+
16
+ Examples:
17
+ >>> generator = MarkdownReportGenerator()
18
+ >>> report = generator.render(result, include_metadata=True)
19
+ >>> generator.save(report, "report.md")
20
+ """
21
+
22
+ def render(self, result: BaseResult, **options: Any) -> str:
23
+ """Render result to Markdown format.
24
+
25
+ Args:
26
+ result: Evaluation result to render
27
+ **options: Markdown rendering options
28
+ - include_metadata: Include metadata section (default: True)
29
+ - include_dataframes: Include DataFrame tables (default: True)
30
+ - max_rows: Max rows to show in DataFrame tables (default: 10)
31
+
32
+ Returns:
33
+ Markdown string
34
+ """
35
+ include_metadata = options.get("include_metadata", True)
36
+ include_dataframes = options.get("include_dataframes", True)
37
+ max_rows = options.get("max_rows", 10)
38
+
39
+ sections = []
40
+
41
+ # Title
42
+ analysis_type = result.analysis_type.replace("_", " ").title()
43
+ sections.append(f"# {analysis_type} Report\n")
44
+
45
+ # Metadata section
46
+ if include_metadata:
47
+ sections.append("## Metadata\n")
48
+ sections.append(f"- **Analysis Type**: {result.analysis_type}")
49
+ sections.append(f"- **Created**: {result.created_at}")
50
+ sections.append(f"- **ML4T Diagnostic Version**: {result.version}\n")
51
+
52
+ # Summary section (all results have summary())
53
+ sections.append("## Summary\n")
54
+ sections.append(f"```\n{result.summary()}\n```\n")
55
+
56
+ # DataFrame sections (if available)
57
+ if include_dataframes:
58
+ try:
59
+ available_dfs = result.list_available_dataframes()
60
+ if available_dfs:
61
+ sections.append("## Data Tables\n")
62
+ for df_name in available_dfs:
63
+ df = result.get_dataframe(df_name)
64
+ sections.append(f"### {df_name.replace('_', ' ').title()}\n")
65
+
66
+ # Limit rows if needed
67
+ if len(df) > max_rows:
68
+ display_df = df.head(max_rows)
69
+ sections.append(f"*Showing first {max_rows} of {len(df)} rows*\n")
70
+ else:
71
+ display_df = df
72
+
73
+ # Convert to markdown table (basic implementation)
74
+ sections.append(self._dataframe_to_markdown(display_df))
75
+ sections.append("")
76
+ except (NotImplementedError, ValueError):
77
+ # Some results may not have DataFrames
78
+ pass
79
+
80
+ return "\n".join(sections)
81
+
82
+ def _dataframe_to_markdown(self, df) -> str:
83
+ """Convert Polars DataFrame to Markdown table.
84
+
85
+ Args:
86
+ df: Polars DataFrame
87
+
88
+ Returns:
89
+ Markdown table string
90
+ """
91
+
92
+ # Get column names
93
+ cols = df.columns
94
+
95
+ # Header
96
+ header = "| " + " | ".join(cols) + " |"
97
+ separator = "| " + " | ".join(["---"] * len(cols)) + " |"
98
+
99
+ # Rows
100
+ rows = []
101
+ for row in df.iter_rows():
102
+ formatted = []
103
+ for val in row:
104
+ # Format values
105
+ if isinstance(val, float):
106
+ formatted.append(f"{val:.4f}")
107
+ else:
108
+ formatted.append(str(val))
109
+ rows.append("| " + " | ".join(formatted) + " |")
110
+
111
+ return "\n".join([header, separator] + rows)
112
+
113
+
114
+ # Register with factory
115
+ from ml4t.diagnostic.reporting.base import ReportFactory # noqa: E402
116
+
117
+ ReportFactory.register(ReportFormat.MARKDOWN, MarkdownReportGenerator)
@@ -0,0 +1,24 @@
1
+ # results/ - Result Dataclasses
2
+
3
+ Immutable results with `.to_dict()`, `.summary()`, `.get_dataframe()`.
4
+
5
+ ## Modules
6
+
7
+ | File | Purpose |
8
+ |------|---------|
9
+ | base.py | `BaseResult` abstract |
10
+ | feature_results.py | Feature diagnostics |
11
+ | sharpe_results.py | DSR, PSR results |
12
+ | event_results.py | Event study results |
13
+ | portfolio_results.py | Portfolio analysis |
14
+
15
+ ## Subdirectories
16
+
17
+ | Directory | Purpose |
18
+ |-----------|---------|
19
+ | signal_results/ | Signal analysis results (7 modules) |
20
+ | barrier_results/ | Barrier analysis results (6 modules) |
21
+
22
+ ## Key Classes
23
+
24
+ `SignalResult`, `BarrierTearSheet`, `DSRResult`, `FeatureDiagnosticsResult`, `EventStudyResult`
@@ -0,0 +1,105 @@
1
+ """Type-safe Pydantic result schemas for all evaluation outputs.
2
+
3
+ This module provides structured result schemas for:
4
+ - Feature diagnostics (Module A)
5
+ - Cross-feature analysis (Module B)
6
+ - Feature-outcome relationships (Module C)
7
+ - Portfolio evaluation (Module D)
8
+ - Sharpe framework analysis
9
+
10
+ All results support:
11
+ - JSON serialization
12
+ - DataFrame conversion
13
+ - Human-readable summaries
14
+ - Type-safe access via Pydantic validation
15
+ """
16
+
17
+ from ml4t.diagnostic.results.barrier_results import (
18
+ BarrierTearSheet,
19
+ HitRateResult,
20
+ PrecisionRecallResult,
21
+ ProfitFactorResult,
22
+ TimeToTargetResult,
23
+ )
24
+ from ml4t.diagnostic.results.base import BaseResult
25
+ from ml4t.diagnostic.results.event_results import (
26
+ AbnormalReturnResult,
27
+ EventStudyResult,
28
+ )
29
+ from ml4t.diagnostic.results.feature_results import (
30
+ ACFResult,
31
+ CrossFeatureResult,
32
+ FeatureDiagnosticsResult,
33
+ FeatureOutcomeResult,
34
+ ICAnalysisResult,
35
+ StationarityTestResult,
36
+ ThresholdAnalysisResult,
37
+ )
38
+ from ml4t.diagnostic.results.multi_signal_results import (
39
+ ComparisonResult,
40
+ MultiSignalSummary,
41
+ )
42
+ from ml4t.diagnostic.results.portfolio_results import (
43
+ BayesianComparisonResult,
44
+ PortfolioEvaluationResult,
45
+ PortfolioMetrics,
46
+ )
47
+ from ml4t.diagnostic.results.sharpe_results import (
48
+ DSRResult,
49
+ FDRResult,
50
+ MinTRLResult,
51
+ PSRResult,
52
+ SharpeFrameworkResult,
53
+ )
54
+ from ml4t.diagnostic.results.signal_results import (
55
+ IRtcResult,
56
+ QuantileAnalysisResult,
57
+ RASICResult,
58
+ SignalICResult,
59
+ SignalTearSheet,
60
+ TurnoverAnalysisResult,
61
+ )
62
+
63
+ __all__ = [
64
+ # Base
65
+ "BaseResult",
66
+ # Feature diagnostics (Module A)
67
+ "StationarityTestResult",
68
+ "ACFResult",
69
+ "FeatureDiagnosticsResult",
70
+ # Cross-feature (Module B)
71
+ "CrossFeatureResult",
72
+ # Feature-outcome (Module C)
73
+ "ICAnalysisResult",
74
+ "ThresholdAnalysisResult",
75
+ "FeatureOutcomeResult",
76
+ # Portfolio (Module D)
77
+ "PortfolioMetrics",
78
+ "BayesianComparisonResult",
79
+ "PortfolioEvaluationResult",
80
+ # Sharpe framework
81
+ "PSRResult",
82
+ "MinTRLResult",
83
+ "DSRResult",
84
+ "FDRResult",
85
+ "SharpeFrameworkResult",
86
+ # Signal analysis
87
+ "SignalICResult",
88
+ "RASICResult",
89
+ "QuantileAnalysisResult",
90
+ "TurnoverAnalysisResult",
91
+ "IRtcResult",
92
+ "SignalTearSheet",
93
+ # Event study
94
+ "AbnormalReturnResult",
95
+ "EventStudyResult",
96
+ # Multi-signal analysis
97
+ "MultiSignalSummary",
98
+ "ComparisonResult",
99
+ # Barrier analysis
100
+ "HitRateResult",
101
+ "ProfitFactorResult",
102
+ "PrecisionRecallResult",
103
+ "TimeToTargetResult",
104
+ "BarrierTearSheet",
105
+ ]