File size: 3,981 Bytes
8e50444
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import sys, os
import numpy as np, pandas as pd
import warnings; warnings.filterwarnings('ignore')

sys.path.insert(0, os.path.dirname(__file__))
from backtesting.engines.v30_causal_engine import get_data, evaluate_slice, V30_PARAMS
from backtesting.audits.v53_causal_audit import run_v53_causal

def test_4_1_param_surface(dc, spy, vf, daily_ret):
    print("--- Test 4.1: Parameter Sensitivity Surface ---")
    
    rebal_grid = [21, 30, 40, 60]
    pick_grid = [5, 10, 15, 20]
    
    print("Sweeping parameter grid...", end="", flush=True)
    results = {}
    for r in rebal_grid:
        for p in pick_grid:
            params = V30_PARAMS.copy()
            params['rebal_days'] = r
            params['top_n'] = p
            c = run_v53_causal(dc, spy, vf, daily_ret, **params)
            m = evaluate_slice(c, "2008-01-01", "2025-12-31")
            results[(r, p)] = m['sharpe']
            print(".", end="", flush=True)
    print("\n")
    
    sharpes = list(results.values())
    min_s, max_s = min(sharpes), max(sharpes)
    
    for r in rebal_grid:
        row_str = f"Rebal {r:2d}d | "
        for p in pick_grid:
            row_str += f"Pick{p:2d}: {results[(r, p)]:.2f}  "
        print(row_str)
        
    print(f"\nSurface Range: {min_s:.2f} to {max_s:.2f}")
    if min_s >= 0.70:
        print("Result: PASS (Smooth, robust surface)")
    elif min_s < 0.60:
        print("Result: FAIL (Cliff edge detected)")
    else:
        print("Result: WEAK PASS (Acceptable but contains weak spots)")
        
    return results

def test_4_2_single_param_overfit(dc, spy, vf, daily_ret, results):
    print("\n--- Test 4.2: Single Parameter Overfit Detection ---")
    
    # Find the parameter set that gave the highest full-period Sharpe
    best_params = max(results, key=results.get)
    best_r, best_p = best_params
    best_s = results[best_params]
    
    base_s = results.get((60, 15), 0)
    
    print(f"Base Params (60d, 15 picks) Sharpe: {base_s:.4f}")
    print(f"Best Params ({best_r}d, {best_p} picks) Sharpe: {best_s:.4f}")
    
    diff = best_s - base_s
    
    if diff > 0.10:
        print("\nSignificant improvement detected. Running Train/Test Split on best params...")
        params = V30_PARAMS.copy()
        params['rebal_days'] = best_r
        params['top_n'] = best_p
        
        c = run_v53_causal(dc, spy, vf, daily_ret, **params)
        m_train = evaluate_slice(c, "2008-01-01", "2018-12-31")
        m_test = evaluate_slice(c, "2019-01-01", "2025-12-31")
        
        print(f"Train Sharpe (2008-2018): {m_train['sharpe']:.4f}")
        print(f"Test Sharpe  (2019-2025): {m_test['sharpe']:.4f}")
        
        # Original baseline test sharpe
        c_orig = run_v53_causal(dc, spy, vf, daily_ret, **V30_PARAMS)
        m_orig_test = evaluate_slice(c_orig, "2019-01-01", "2025-12-31")
        orig_test_s = m_orig_test['sharpe']
        
        test_diff = m_test['sharpe'] - orig_test_s
        print(f"Out-of-sample edge over base params: {test_diff:+.4f}")
        
        if test_diff > 0.10:
            print("Result: PASS (Improvement is genuine out-of-sample)")
        elif test_diff < 0:
            print("Result: FAIL (Pure overfit, optimization disappeared out-of-sample)")
        else:
            print("Result: WEAK PASS (Marginal out-of-sample improvement)")
            
    else:
        print("\nNo single parameter caused dramatic (>0.10) overfit behavior.")
        print("Result: PASS (Base parameters are near optimal or stable)")


if __name__ == "__main__":
    print("========================================")
    print(" V53 FRAMEWORK VALIDATION - PHASE 4")
    print("========================================")
    dc, spy, vf, daily_ret = get_data()
    
    res = test_4_1_param_surface(dc, spy, vf, daily_ret)
    test_4_2_single_param_overfit(dc, spy, vf, daily_ret, res)