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,316 @@
1
+ """ML4T Data integration contract for data quality validation.
2
+
3
+ This module defines the API contract between ML4T Data and ML4T Diagnostic for
4
+ data quality assessment. ML4T Data can use these contracts to report data quality
5
+ to ML4T Diagnostic for validation before feature engineering.
6
+
7
+ Example workflow:
8
+ >>> from ml4t.data import DataManager
9
+ >>> from ml4t.diagnostic.integration import DataQualityReport, DataAnomaly
10
+ >>>
11
+ >>> # 1. Load data with quality report
12
+ >>> dm = DataManager(storage_config)
13
+ >>> data, quality = dm.load_with_quality("AAPL", start="2020-01-01")
14
+ >>>
15
+ >>> # 2. Check quality before proceeding
16
+ >>> if not quality.is_acceptable():
17
+ ... print(quality.summary())
18
+ ... raise DataQualityError(quality.recommendations)
19
+ >>>
20
+ >>> # 3. Proceed with feature engineering
21
+ >>> features = compute_features(data)
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from datetime import datetime
27
+ from enum import Enum
28
+
29
+ from pydantic import BaseModel, Field
30
+
31
+
32
+ class AnomalyType(str, Enum):
33
+ """Types of data anomalies that can be detected.
34
+
35
+ These anomaly types align with common data quality issues in financial data:
36
+
37
+ - MISSING_DATA: Gaps in the data (e.g., missing trading days)
38
+ - STALE_DATA: Same value repeated (stuck price feed)
39
+ - PRICE_SPIKE: Abnormal price movement (e.g., >10 std devs)
40
+ - NEGATIVE_PRICE: Invalid negative price (should be positive)
41
+ - ZERO_VOLUME: No trading activity (suspicious for liquid assets)
42
+ - OHLC_VIOLATION: High < Low or similar OHLC logic errors
43
+ - TIMESTAMP_GAP: Unexpected gap in timestamps
44
+ - DUPLICATE_TIMESTAMP: Same timestamp appears multiple times
45
+ - OUTLIER: Statistical outlier (not necessarily error)
46
+ """
47
+
48
+ MISSING_DATA = "missing_data"
49
+ STALE_DATA = "stale_data"
50
+ PRICE_SPIKE = "price_spike"
51
+ NEGATIVE_PRICE = "negative_price"
52
+ ZERO_VOLUME = "zero_volume"
53
+ OHLC_VIOLATION = "ohlc_violation"
54
+ TIMESTAMP_GAP = "timestamp_gap"
55
+ DUPLICATE_TIMESTAMP = "duplicate_timestamp"
56
+ OUTLIER = "outlier"
57
+
58
+
59
+ class Severity(str, Enum):
60
+ """Severity level of data anomalies.
61
+
62
+ - INFO: Informational, no action needed
63
+ - WARNING: Potential issue, review recommended
64
+ - ERROR: Definite issue, correction needed
65
+ - CRITICAL: Severe issue, data may be unusable
66
+ """
67
+
68
+ INFO = "info"
69
+ WARNING = "warning"
70
+ ERROR = "error"
71
+ CRITICAL = "critical"
72
+
73
+
74
+ class DataAnomaly(BaseModel):
75
+ """Record of a single data anomaly detected.
76
+
77
+ Represents a specific data quality issue found during validation.
78
+ Used to communicate detailed findings from ML4T Data to ML4T Diagnostic.
79
+
80
+ Attributes:
81
+ anomaly_type: Type of anomaly detected
82
+ severity: Severity level
83
+ timestamp: When the anomaly occurred
84
+ symbol: Which asset (if multi-asset)
85
+ description: Human-readable description
86
+ value: The problematic value (if applicable)
87
+ expected_range: Expected value range (if applicable)
88
+ suggested_fix: Recommended correction
89
+
90
+ Example:
91
+ >>> anomaly = DataAnomaly(
92
+ ... anomaly_type=AnomalyType.PRICE_SPIKE,
93
+ ... severity=Severity.ERROR,
94
+ ... timestamp=datetime(2024, 1, 15, 10, 30),
95
+ ... symbol="AAPL",
96
+ ... description="Price moved 15 std devs in 1 minute",
97
+ ... value=999.99,
98
+ ... expected_range=(150.0, 200.0),
99
+ ... suggested_fix="Replace with interpolated value"
100
+ ... )
101
+ """
102
+
103
+ anomaly_type: AnomalyType = Field(..., description="Type of anomaly")
104
+ severity: Severity = Field(..., description="Severity level")
105
+ timestamp: datetime = Field(..., description="When anomaly occurred")
106
+ symbol: str | None = Field(None, description="Asset symbol (if applicable)")
107
+ description: str = Field(..., description="Human-readable description")
108
+ value: float | None = Field(None, description="Problematic value")
109
+ expected_range: tuple[float, float] | None = Field(None, description="Expected value range")
110
+ suggested_fix: str | None = Field(None, description="Recommended correction")
111
+
112
+
113
+ class DataQualityMetrics(BaseModel):
114
+ """Quantitative metrics for data quality assessment.
115
+
116
+ These metrics provide a numerical summary of data quality that can be
117
+ used for automated quality gates.
118
+
119
+ Attributes:
120
+ completeness: Fraction of expected data points present [0.0, 1.0]
121
+ timeliness: How up-to-date the data is (e.g., minutes since last update)
122
+ accuracy_score: Estimated data accuracy based on validation checks [0.0, 1.0]
123
+ consistency_score: How consistent the data is (no OHLC violations, etc.) [0.0, 1.0]
124
+ n_records: Total number of records
125
+ n_anomalies: Total anomalies detected
126
+ n_critical: Number of critical severity anomalies
127
+ n_error: Number of error severity anomalies
128
+ n_warning: Number of warning severity anomalies
129
+
130
+ Example:
131
+ >>> metrics = DataQualityMetrics(
132
+ ... completeness=0.98,
133
+ ... timeliness=5.0,
134
+ ... accuracy_score=0.95,
135
+ ... consistency_score=1.0,
136
+ ... n_records=10000,
137
+ ... n_anomalies=12,
138
+ ... n_critical=0,
139
+ ... n_error=2,
140
+ ... n_warning=10
141
+ ... )
142
+ """
143
+
144
+ completeness: float = Field(..., ge=0.0, le=1.0, description="Data completeness [0,1]")
145
+ timeliness: float = Field(..., ge=0.0, description="Minutes since last update")
146
+ accuracy_score: float = Field(..., ge=0.0, le=1.0, description="Accuracy score [0,1]")
147
+ consistency_score: float = Field(..., ge=0.0, le=1.0, description="Consistency score [0,1]")
148
+ n_records: int = Field(..., ge=0, description="Total records")
149
+ n_anomalies: int = Field(..., ge=0, description="Total anomalies")
150
+ n_critical: int = Field(default=0, ge=0, description="Critical anomalies")
151
+ n_error: int = Field(default=0, ge=0, description="Error anomalies")
152
+ n_warning: int = Field(default=0, ge=0, description="Warning anomalies")
153
+
154
+
155
+ class DataQualityReport(BaseModel):
156
+ """Complete data quality report from ML4T Data.
157
+
158
+ This is the primary output format for data quality validation.
159
+ ML4T Data generates this report when loading data, and ML4T Diagnostic
160
+ can use it to decide whether to proceed with analysis.
161
+
162
+ Attributes:
163
+ symbol: Asset symbol or identifier
164
+ source: Data source/provider name
165
+ date_range: Start and end dates of the data
166
+ frequency: Data frequency (e.g., "1min", "1d", "tick")
167
+ metrics: Quantitative quality metrics
168
+ anomalies: List of detected anomalies
169
+ recommendations: Human-readable recommendations
170
+ is_production_ready: Whether data meets production quality standards
171
+ created_at: When this report was generated
172
+
173
+ Example:
174
+ >>> report = DataQualityReport(
175
+ ... symbol="AAPL",
176
+ ... source="databento",
177
+ ... date_range=(datetime(2024, 1, 1), datetime(2024, 6, 30)),
178
+ ... frequency="1min",
179
+ ... metrics=DataQualityMetrics(
180
+ ... completeness=0.995,
181
+ ... timeliness=1.0,
182
+ ... accuracy_score=0.99,
183
+ ... consistency_score=1.0,
184
+ ... n_records=100000,
185
+ ... n_anomalies=3,
186
+ ... ),
187
+ ... anomalies=[],
188
+ ... recommendations=["Data quality is excellent"],
189
+ ... is_production_ready=True
190
+ ... )
191
+ """
192
+
193
+ symbol: str = Field(..., description="Asset symbol")
194
+ source: str = Field(..., description="Data source/provider")
195
+ date_range: tuple[datetime, datetime] = Field(..., description="Data date range")
196
+ frequency: str = Field(..., description="Data frequency (1min, 1d, tick)")
197
+ metrics: DataQualityMetrics = Field(..., description="Quality metrics")
198
+ anomalies: list[DataAnomaly] = Field(default_factory=list, description="Detected anomalies")
199
+ recommendations: list[str] = Field(default_factory=list, description="Recommendations")
200
+ is_production_ready: bool = Field(..., description="Meets production standards")
201
+ created_at: datetime = Field(
202
+ default_factory=datetime.utcnow, description="Report generation time"
203
+ )
204
+
205
+ def is_acceptable(
206
+ self,
207
+ min_completeness: float = 0.95,
208
+ max_critical: int = 0,
209
+ max_errors: int = 5,
210
+ ) -> bool:
211
+ """Check if data quality meets acceptance criteria.
212
+
213
+ Parameters
214
+ ----------
215
+ min_completeness : float, default 0.95
216
+ Minimum acceptable completeness ratio
217
+ max_critical : int, default 0
218
+ Maximum allowed critical anomalies
219
+ max_errors : int, default 5
220
+ Maximum allowed error anomalies
221
+
222
+ Returns
223
+ -------
224
+ bool
225
+ True if data meets all criteria
226
+ """
227
+ return (
228
+ self.metrics.completeness >= min_completeness
229
+ and self.metrics.n_critical <= max_critical
230
+ and self.metrics.n_error <= max_errors
231
+ )
232
+
233
+ def summary(self) -> str:
234
+ """Generate human-readable summary of data quality.
235
+
236
+ Returns
237
+ -------
238
+ str
239
+ Formatted summary string
240
+ """
241
+ lines = [
242
+ "=" * 50,
243
+ f"Data Quality Report: {self.symbol}",
244
+ "=" * 50,
245
+ "",
246
+ f"Source: {self.source}",
247
+ f"Date range: {self.date_range[0].date()} to {self.date_range[1].date()}",
248
+ f"Frequency: {self.frequency}",
249
+ f"Records: {self.metrics.n_records:,}",
250
+ "",
251
+ "--- Quality Metrics ---",
252
+ f"Completeness: {self.metrics.completeness:.1%}",
253
+ f"Accuracy: {self.metrics.accuracy_score:.1%}",
254
+ f"Consistency: {self.metrics.consistency_score:.1%}",
255
+ "",
256
+ "--- Anomalies ---",
257
+ f"Critical: {self.metrics.n_critical}",
258
+ f"Errors: {self.metrics.n_error}",
259
+ f"Warnings: {self.metrics.n_warning}",
260
+ "",
261
+ f"Production Ready: {'YES' if self.is_production_ready else 'NO'}",
262
+ ]
263
+
264
+ if self.recommendations:
265
+ lines.append("")
266
+ lines.append("--- Recommendations ---")
267
+ for rec in self.recommendations:
268
+ lines.append(f" - {rec}")
269
+
270
+ return "\n".join(lines)
271
+
272
+ def to_dict(self) -> dict:
273
+ """Export to dictionary format.
274
+
275
+ Returns
276
+ -------
277
+ dict
278
+ Dictionary representation suitable for JSON serialization
279
+ """
280
+ return self.model_dump(mode="json")
281
+
282
+
283
+ class DataValidationRequest(BaseModel):
284
+ """Request from ML4T Diagnostic to ML4T Data for validation.
285
+
286
+ Allows ML4T Diagnostic to specify what validation checks are needed.
287
+ ML4T Data can use this to customize the quality report.
288
+
289
+ Attributes:
290
+ symbol: Asset to validate
291
+ date_range: Date range to validate (optional)
292
+ checks: Specific checks to run
293
+ thresholds: Custom thresholds for validation
294
+ include_details: Whether to include detailed anomaly records
295
+
296
+ Example:
297
+ >>> request = DataValidationRequest(
298
+ ... symbol="AAPL",
299
+ ... checks=["completeness", "price_spikes", "ohlc_validation"],
300
+ ... thresholds={"price_spike_std": 5.0},
301
+ ... include_details=True
302
+ ... )
303
+ """
304
+
305
+ symbol: str = Field(..., description="Asset to validate")
306
+ date_range: tuple[datetime, datetime] | None = Field(
307
+ None, description="Optional date range to validate"
308
+ )
309
+ checks: list[str] = Field(
310
+ default_factory=lambda: ["completeness", "stale_data", "price_spikes", "ohlc_validation"],
311
+ description="Validation checks to run",
312
+ )
313
+ thresholds: dict[str, float] = Field(
314
+ default_factory=dict, description="Custom thresholds for validation checks"
315
+ )
316
+ include_details: bool = Field(default=True, description="Include detailed anomaly records")
@@ -0,0 +1,226 @@
1
+ """ML4T Engineer integration contract for preprocessing recommendations.
2
+
3
+ This module defines the API contract between ML4T Diagnostic and ML4T Engineer for feature
4
+ preprocessing recommendations. After evaluating features, ML4T Diagnostic can recommend
5
+ transforms that ML4T Engineer should apply.
6
+
7
+ Example workflow:
8
+ >>> from ml4t.diagnostic.evaluation import FeatureEvaluator
9
+ >>> from ml4t.diagnostic.integration import EngineerConfig
10
+ >>>
11
+ >>> # 1. Evaluate features
12
+ >>> evaluator = FeatureEvaluator(config)
13
+ >>> results = evaluator.evaluate(features_df)
14
+ >>>
15
+ >>> # 2. Get preprocessing recommendations
16
+ >>> eng_config = results.to_engineer_config()
17
+ >>>
18
+ >>> # 3. Export for ML4T Engineer
19
+ >>> preprocessing_dict = eng_config.to_dict()
20
+ >>>
21
+ >>> # 4. Use with ML4T Engineer
22
+ >>> # from ml4t.engineer import PreprocessingPipeline
23
+ >>> # pipeline = PreprocessingPipeline(preprocessing_dict)
24
+ >>> # transformed = pipeline.transform(features_df)
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from enum import Enum
30
+
31
+ from pydantic import BaseModel, Field
32
+
33
+
34
+ class TransformType(str, Enum):
35
+ """Supported transform types matching ML4T Engineer API.
36
+
37
+ These transforms align with ML4T Engineer's PreprocessingPipeline.
38
+ Each transform addresses specific statistical issues:
39
+
40
+ - NONE: Feature is good as-is
41
+ - LOG: Reduce right skew, stabilize variance
42
+ - SQRT: Reduce right skew (milder than log)
43
+ - STANDARDIZE: Zero mean, unit variance (z-score)
44
+ - NORMALIZE: Scale to [0, 1] range
45
+ - WINSORIZE: Cap outliers at percentiles
46
+ - DIFF: First difference for non-stationary series
47
+ """
48
+
49
+ NONE = "none"
50
+ LOG = "log"
51
+ SQRT = "sqrt"
52
+ STANDARDIZE = "standardize"
53
+ NORMALIZE = "normalize"
54
+ WINSORIZE = "winsorize"
55
+ DIFF = "diff"
56
+
57
+
58
+ class PreprocessingRecommendation(BaseModel):
59
+ """Recommendation for preprocessing a single feature.
60
+
61
+ ML4T Diagnostic generates these recommendations based on diagnostics:
62
+ - Stationarity tests → recommend differencing
63
+ - Distribution analysis → recommend transforms for skew
64
+ - Outlier detection → recommend winsorization
65
+ - Scale issues → recommend normalization
66
+
67
+ Attributes:
68
+ feature_name: Name of the feature
69
+ transform: Recommended transform type
70
+ reason: Human-readable explanation of why this transform
71
+ confidence: Confidence in recommendation [0.0, 1.0]
72
+ diagnostics: Optional diagnostic details that led to recommendation
73
+
74
+ Example:
75
+ >>> rec = PreprocessingRecommendation(
76
+ ... feature_name="returns",
77
+ ... transform=TransformType.DIFF,
78
+ ... reason="Feature is non-stationary (ADF p=0.82)",
79
+ ... confidence=0.95
80
+ ... )
81
+ """
82
+
83
+ feature_name: str = Field(..., description="Feature name")
84
+ transform: TransformType = Field(..., description="Recommended transform")
85
+ reason: str = Field(..., description="Explanation for recommendation")
86
+ confidence: float = Field(..., ge=0.0, le=1.0, description="Confidence [0.0, 1.0]")
87
+ diagnostics: dict[str, float] | None = Field(
88
+ None, description="Optional diagnostic values (e.g., {'adf_pvalue': 0.82})"
89
+ )
90
+
91
+
92
+ class EngineerConfig(BaseModel):
93
+ """Configuration for ML4T Engineer PreprocessingPipeline.
94
+
95
+ This is the output format that ML4T Engineer can consume.
96
+ Contains recommendations for all features that need preprocessing.
97
+
98
+ Attributes:
99
+ recommendations: List of feature preprocessing recommendations
100
+ metadata: Optional metadata about evaluation context
101
+
102
+ Example:
103
+ >>> config = EngineerConfig(recommendations=[
104
+ ... PreprocessingRecommendation(
105
+ ... feature_name="rsi_14",
106
+ ... transform=TransformType.WINSORIZE,
107
+ ... reason="Outliers detected at 1st and 99th percentile",
108
+ ... confidence=0.85
109
+ ... ),
110
+ ... PreprocessingRecommendation(
111
+ ... feature_name="log_returns",
112
+ ... transform=TransformType.NONE,
113
+ ... reason="Already stationary and normally distributed",
114
+ ... confidence=0.90
115
+ ... )
116
+ ... ])
117
+ >>> eng_dict = config.to_dict()
118
+ """
119
+
120
+ recommendations: list[PreprocessingRecommendation] = Field(
121
+ ..., description="Feature preprocessing recommendations"
122
+ )
123
+ metadata: dict[str, str] | None = Field(
124
+ None, description="Optional metadata (e.g., eval timestamp, config)"
125
+ )
126
+
127
+ def to_dict(self) -> dict[str, dict[str, str | float | dict[str, float]]]:
128
+ """Export to ML4T Engineer-compatible format.
129
+
130
+ Returns dictionary mapping feature names to preprocessing configs:
131
+ {
132
+ "feature_name": {
133
+ "transform": "diff",
134
+ "reason": "Non-stationary",
135
+ "confidence": 0.95,
136
+ "diagnostics": {...}
137
+ }
138
+ }
139
+
140
+ Returns:
141
+ Dictionary in ML4T Engineer PreprocessingPipeline format
142
+
143
+ Example:
144
+ >>> config.to_dict()
145
+ {
146
+ 'rsi_14': {
147
+ 'transform': 'winsorize',
148
+ 'reason': 'Outliers detected',
149
+ 'confidence': 0.85
150
+ }
151
+ }
152
+ """
153
+ result: dict[str, dict[str, str | float | dict[str, float]]] = {}
154
+ for rec in self.recommendations:
155
+ feature_dict: dict[str, str | float | dict[str, float]] = {
156
+ "transform": rec.transform.value,
157
+ "reason": rec.reason,
158
+ "confidence": rec.confidence,
159
+ }
160
+ if rec.diagnostics:
161
+ feature_dict["diagnostics"] = rec.diagnostics
162
+ result[rec.feature_name] = feature_dict
163
+ return result
164
+
165
+ def get_recommendations_by_transform(
166
+ self, transform: TransformType
167
+ ) -> list[PreprocessingRecommendation]:
168
+ """Filter recommendations by transform type.
169
+
170
+ Useful for analyzing patterns in recommendations:
171
+ - How many features need differencing?
172
+ - Which features can stay unchanged?
173
+
174
+ Args:
175
+ transform: Transform type to filter by
176
+
177
+ Returns:
178
+ List of recommendations with matching transform
179
+
180
+ Example:
181
+ >>> config.get_recommendations_by_transform(TransformType.DIFF)
182
+ [PreprocessingRecommendation(feature_name='returns', ...)]
183
+ """
184
+ return [rec for rec in self.recommendations if rec.transform == transform]
185
+
186
+ def summary(self) -> str:
187
+ """Human-readable summary of recommendations.
188
+
189
+ Returns:
190
+ Formatted summary string
191
+
192
+ Example:
193
+ >>> print(config.summary())
194
+ ML4T Engineer Preprocessing Recommendations
195
+ ==========================================
196
+ Total features: 5
197
+ - DIFF: 2 features
198
+ - WINSORIZE: 1 feature
199
+ - NONE: 2 features
200
+ """
201
+ lines = ["ML4T Engineer Preprocessing Recommendations", "=" * 44]
202
+ lines.append(f"Total features: {len(self.recommendations)}")
203
+ lines.append("")
204
+
205
+ # Count by transform type
206
+ transform_counts: dict[TransformType, int] = {}
207
+ for rec in self.recommendations:
208
+ transform_counts[rec.transform] = transform_counts.get(rec.transform, 0) + 1
209
+
210
+ # Sort by count (descending)
211
+ for transform, count in sorted(transform_counts.items(), key=lambda x: x[1], reverse=True):
212
+ lines.append(f" {transform.value.upper()}: {count} features")
213
+
214
+ # Show high-confidence recommendations
215
+ high_conf = [rec for rec in self.recommendations if rec.confidence >= 0.9]
216
+ if high_conf:
217
+ lines.append("")
218
+ lines.append(f"High-confidence recommendations (≥0.9): {len(high_conf)}")
219
+ for rec in high_conf[:5]: # Show top 5
220
+ lines.append(
221
+ f" {rec.feature_name}: {rec.transform.value} (conf={rec.confidence:.2f})"
222
+ )
223
+ if len(high_conf) > 5:
224
+ lines.append(f" ... and {len(high_conf) - 5} more")
225
+
226
+ return "\n".join(lines)
@@ -0,0 +1,77 @@
1
+ """
2
+ ML4T Diagnostic Logging and Debugging Infrastructure
3
+
4
+ Provides structured logging with levels, progress tracking, debug mode,
5
+ and performance metrics for ML4T Diagnostic library operations.
6
+
7
+ Features:
8
+ - Structured JSON logging
9
+ - Configurable log levels (DEBUG, INFO, WARNING, ERROR)
10
+ - Progress indicators for long-running operations
11
+ - Debug mode for intermediate results
12
+ - Performance metrics tracking
13
+ - Context-aware logging
14
+ - Weights & Biases integration
15
+
16
+ Example:
17
+ >>> from ml4t.diagnostic.logging import get_logger, set_log_level, LogLevel
18
+ >>> logger = get_logger(__name__)
19
+ >>> set_log_level(LogLevel.DEBUG)
20
+ >>> logger.info("Computing Sharpe ratio", n_samples=100)
21
+ >>> with logger.timed("sharpe_computation"):
22
+ ... sharpe = compute_sharpe_ratio(returns)
23
+ """
24
+
25
+ # Structured logging
26
+ from ml4t.diagnostic.logging.logger import (
27
+ LogLevel,
28
+ QEvalLogger,
29
+ configure_logging,
30
+ get_log_level,
31
+ get_logger,
32
+ set_log_level,
33
+ )
34
+
35
+ # Performance metrics
36
+ from ml4t.diagnostic.logging.performance import (
37
+ PerformanceMonitor,
38
+ PerformanceTracker,
39
+ get_performance_monitor,
40
+ measure_time,
41
+ timed,
42
+ )
43
+
44
+ # Progress tracking
45
+ from ml4t.diagnostic.logging.progress import (
46
+ ProgressBar,
47
+ ProgressTracker,
48
+ progress_indicator,
49
+ spinner,
50
+ )
51
+
52
+ # Experiment tracking (Weights & Biases)
53
+ from ml4t.diagnostic.logging.wandb import WandbLogger, log_experiment
54
+
55
+ __all__: list[str] = [
56
+ # Logger
57
+ "QEvalLogger",
58
+ "get_logger",
59
+ "set_log_level",
60
+ "get_log_level",
61
+ "configure_logging",
62
+ "LogLevel",
63
+ # Progress
64
+ "ProgressBar",
65
+ "progress_indicator",
66
+ "ProgressTracker",
67
+ "spinner",
68
+ # Performance
69
+ "PerformanceTracker",
70
+ "PerformanceMonitor",
71
+ "get_performance_monitor",
72
+ "timed",
73
+ "measure_time",
74
+ # WandB
75
+ "WandbLogger",
76
+ "log_experiment",
77
+ ]