portfolio_opt / report_builders /html_validation.py
engineportf's picture
Upload folder using huggingface_hub
ba790e8 verified
Raw
History Blame Contribute Delete
7.61 kB
def build_econometric_validation_html(dm_results, var_results, psr_results=None, dsr_results=None, pt_results=None, lb_results=None):
validation_html = ""
if dm_results or var_results or psr_results or dsr_results:
val_rows = ""
if dm_results:
dm_pass = dm_results.get('significant', False) and dm_results.get('winner') not in ['Naive Mean', 'Model 2']
dm_col = "#3fb950" if dm_pass else "#e3b341"
if dm_pass:
dm_diag = "Model successfully outperforms a naive historical baseline with statistical significance."
elif not dm_results.get('significant', False):
dm_diag = "Model fails to statistically distinguish itself from a simple historical average."
else:
dm_diag = "Model underperforms the naive historical baseline."
val_rows += (
f'<div class="mc"><div class="ml" title="Diebold-Mariano Test: Does the ML model statistically beat a naive historical baseline?">'
f'Predictive Alpha (DM Test) &#9432;</div>'
f'<div class="mv" style="color:{dm_col}">{"PASS" if dm_pass else "INCONCLUSIVE"}</div>'
f'<div class="ml" style="margin-top:4px">p = {dm_results.get("p_value", 1.0):.4f}</div>'
f'<div class="ml" style="margin-top:2px;color:#8b949e;font-size:.72rem">{dm_diag}</div></div>'
)
if var_results:
var_pass = var_results.get('overall_pass', False)
var_col = "#3fb950" if var_pass else "#f85149"
uc_p = var_results.get('unconditional_coverage', {}).get('p_value', 0.0)
ind_p = var_results.get('independence', {}).get('p_value', 0.0)
diag_text = var_results.get('diagnostic', '')
val_rows += (
f'<div class="mc"><div class="ml" title="Christoffersen Test: Does the VaR limit hold up against real-world volatility clustering?">'
f'Tail Risk Validity (VaR) &#9432;</div>'
f'<div class="mv" style="color:{var_col}">{"PASS" if var_pass else "FAIL"}</div>'
f'<div class="ml" style="margin-top:4px">Coverage p = {uc_p:.4f} &nbsp;|&nbsp; Independence p = {ind_p:.4f}</div>'
+ (f'<div class="ml" style="margin-top:2px;color:#8b949e;font-size:.72rem">{diag_text}</div>' if diag_text and not var_pass else '')
+ '</div>'
)
if psr_results:
psr_pass = psr_results.get('p_value', 1.0) < 0.05
psr_col = "#3fb950" if psr_pass else "#e3b341"
if psr_pass:
psr_diag = "Sharpe ratio is statistically robust even after accounting for non-normal skew and fat tails."
else:
psr_diag = "Sharpe ratio is not statistically significant; outperformance may be driven by extreme outliers or luck."
val_rows += (
f'<div class="mc"><div class="ml" title="Probabilistic Sharpe Ratio: Is the Sharpe ratio statistically significant given non-normal returns?">'
f'Probabilistic Sharpe (PSR) &#9432;</div>'
f'<div class="mv" style="color:{psr_col}">{"PASS" if psr_pass else "INCONCLUSIVE"}</div>'
f'<div class="ml" style="margin-top:4px">p = {psr_results.get("p_value", 1.0):.4f} (Obs: {psr_results.get("observed_sharpe", 0.0):.2f})</div>'
f'<div class="ml" style="margin-top:2px;color:#8b949e;font-size:.72rem">{psr_diag}</div></div>'
)
if dsr_results:
dsr_pass = dsr_results.get('p_value', 1.0) < 0.05
dsr_col = "#3fb950" if dsr_pass else "#e3b341"
if dsr_pass:
dsr_diag = "Strategy is robust against overfitting and multiple testing bias (data mining)."
else:
dsr_diag = "Cannot rule out multiple testing bias; strategy might be overfitted to historical data."
val_rows += (
f'<div class="mc"><div class="ml" title="Deflated Sharpe Ratio: Is the strategy robust against multiple testing bias?">'
f'Deflated Sharpe (DSR) &#9432;</div>'
f'<div class="mv" style="color:{dsr_col}">{"PASS" if dsr_pass else "INCONCLUSIVE"}</div>'
f'<div class="ml" style="margin-top:4px">p = {dsr_results.get("p_value", 1.0):.4f}</div>'
f'<div class="ml" style="margin-top:2px;color:#8b949e;font-size:.72rem">{dsr_diag}</div></div>'
)
if pt_results:
pt_pass = pt_results.get('significant', False)
pt_col = "#3fb950" if pt_pass else "#e3b341"
val_rows += (
f'<div class="mc"><div class="ml" title="Pesaran-Timmermann Test: Does the ML model predict market direction better than a coin flip?">'
f'Directional Accuracy (PT Test) &#9432;</div>'
f'<div class="mv" style="color:{pt_col}">{"PASS" if pt_pass else "INCONCLUSIVE"}</div>'
f'<div class="ml" style="margin-top:4px">p = {pt_results.get("p_value", 1.0):.4f}</div></div>'
)
if lb_results:
lb_pass = not lb_results.get('significant', True) # Null hypothesis is NO autocorrelation. We want p > 0.05
lb_col = "#3fb950" if lb_pass else "#e3b341"
val_rows += (
f'<div class="mc"><div class="ml" title="Ljung-Box Test: Did the GARCH model successfully capture all volatility clustering (no autocorrelation in squared residuals)?">'
f'GARCH Autocorrelation (Ljung-Box) &#9432;</div>'
f'<div class="mv" style="color:{lb_col}">{"PASS" if lb_pass else "FAIL"}</div>'
f'<div class="ml" style="margin-top:4px">p = {lb_results.get("p_value", 0.0):.4f}</div></div>'
)
verdicts = []
if dm_results:
if dm_results.get('significant', False) and dm_results.get('winner') not in ['Naive Mean', 'Model 2']:
verdicts.append("Your model statistically outperforms the naive historical baseline at 95% confidence.")
else:
verdicts.append("The model does not show statistically significant outperformance over a naive historical mean.")
if var_results:
if var_results.get('overall_pass', False):
verdicts.append("Tail risk metrics (VaR) hold up well against real-world volatility clustering.")
else:
verdicts.append("VaR breaches cluster significantly; consider widening your rebalance frequency or reducing risk limits.")
if psr_results and psr_results.get('p_value', 1.0) < 0.05:
verdicts.append("The Sharpe ratio is robust even under non-normal return distributions.")
verdict_html = ""
if verdicts:
verdict_html = (
f'<div style="background:#161b22;border-left:4px solid #58a6ff;padding:10px 14px;margin-bottom:16px;border-radius:4px;font-size:0.85rem;color:#c9d1d9">'
f'<strong style="color:#58a6ff">Diagnostic Verdict:</strong> {" ".join(verdicts)}'
f'</div>'
)
validation_html = (
'<p class="st">Advanced Econometric Validation</p>'
f'{verdict_html}'
f'<div class="mg">{val_rows}</div>'
)
return validation_html