Sortie strippee (FABRICATED Row N / blank PNG). Re-execution QC Cloud recherche kernel requise – see #6891. Code preserve pour tracabilite. Side A = strip honnete, Side B = re-execution (hors scope ce PR).
Verdict H1
Entry 20 / Exit 10 est le paramètre actuel. Verifier si des periodes plus longues (moins de whipsaw) ou plus courtes (plus reactives) sont meilleures.
4. Hypothese 2: SMA trend filter
Règle #13 du backlog: SMA100 trop lent pour trend following. Règle #15: SMA100 trop restrictif pour mean reversion (mais ici c’est trend).
print(f"{'SMA Filter':<15}{'Sharpe':>8}{'CAGR':>8}{'MaxDD':>8}{'Trades':>8}")print("-"*50)results_sma = {}for sma in [None, 30, 50, 100, 200]: r = backtest_donchian(closes, highs, lows, base_universe, sma_filter=sma) name =f'SMA{sma}'if sma else'Aucun' results_sma[name] = rprint(f"{name:<15}{r['sharpe']:>8.3f}{r['cagr']:>7.1%}{r['max_dd']:>7.1%}{r['n_trades']:>7}")
Sortie strippee (FABRICATED Row N / blank PNG). Re-execution QC Cloud recherche kernel requise – see #6891. Code preserve pour tracabilite. Side A = strip honnete, Side B = re-execution (hors scope ce PR).
Verdict H2
SMA50 est le filtre actuel (confirme par research.ipynb). SMA100 trop restrictif.
5. Hypothese 3: Max positions
print(f"{'Max Pos':<15}{'Sharpe':>8}{'CAGR':>8}{'MaxDD':>8}")print("-"*40)results_pos = {}for mp in [2, 3, 4, 5]: r = backtest_donchian(closes, highs, lows, base_universe, max_positions=mp) name =f'Max {mp}' results_pos[name] = rprint(f"{name:<15}{r['sharpe']:>8.3f}{r['cagr']:>7.1%}{r['max_dd']:>7.1%}")
Max Pos Sharpe CAGR MaxDD
----------------------------------------
Max 2 0.266 6.3% -25.2%
Max 3 0.052 3.6% -22.3%
Max 4 -0.060 2.4% -21.8%
Max 5 -0.085 2.3% -20.8%
Sortie strippee (FABRICATED Row N / blank PNG). Re-execution QC Cloud recherche kernel requise – see #6891. Code preserve pour tracabilite. Side A = strip honnete, Side B = re-execution (hors scope ce PR).
Verdict H3
Max 3 positions (33% chacune) est le paramètre actuel. Plus de positions = plus diversifie mais moins concentre sur les meilleurs trends.
6. Hypothese 4: Universe composition
Tester avec/sans les actifs problematiques (DBC contango, VNQ dividendes QC).
universes = {'Actuel (6)': ['SPY', 'GLD', 'EFA', 'VNQ', 'DBC', 'XLE'],'Sans DBC': ['SPY', 'GLD', 'EFA', 'VNQ', 'XLE'],'Sans VNQ': ['SPY', 'GLD', 'EFA', 'DBC', 'XLE'],'Core 4': ['SPY', 'GLD', 'EFA', 'XLE'],'Avec QQQ': ['SPY', 'QQQ', 'GLD', 'EFA', 'XLE'],'Avec XLK': ['SPY', 'XLK', 'GLD', 'EFA', 'XLE'],}print(f"{'Univers':<20}{'Sharpe':>8}{'CAGR':>8}{'MaxDD':>8}")print("-"*45)results_univ = {}for name, univ in universes.items(): avail = [t for t in univ if t in closes.columns] r = backtest_donchian(closes, highs, lows, avail) results_univ[name] = rprint(f"{name:<20}{r['sharpe']:>8.3f}{r['cagr']:>7.1%}{r['max_dd']:>7.1%}")
Univers Sharpe CAGR MaxDD
---------------------------------------------
Actuel (6) 0.052 3.6% -22.3%
Sans DBC -0.052 2.5% -23.6%
Sans VNQ 0.102 4.0% -16.4%
Core 4 -0.018 2.8% -16.8%
Avec QQQ 0.265 5.7% -16.4%
Avec XLK 0.238 5.4% -17.7%
Sortie strippee (FABRICATED Row N / blank PNG). Re-execution QC Cloud recherche kernel requise – see #6891. Code preserve pour tracabilite. Side A = strip honnete, Side B = re-execution (hors scope ce PR).
Verdict H4
research.ipynb avait teste VNQ (yfinance +11% mais QC cloud -10%, divergence dividendes). Verifier si les univers sans VNQ/DBC performent mieux sur données QC.
7. Visualisation
fig, axes = plt.subplots(2, 2, figsize=(16, 10))# H1: Donchian periodsax = axes[0, 0]for name, r in results_donchian.items(): ax.plot(r['cum'].values, label=f"{name} (S={r['sharpe']:.2f})", linewidth=1.5)ax.set_title('H1: Donchian Entry/Exit periods', fontweight='bold')ax.legend(fontsize=7)ax.grid(True, alpha=0.3)# H2: SMA filterax = axes[0, 1]for name, r in results_sma.items(): ax.plot(r['cum'].values, label=f"{name} (S={r['sharpe']:.2f})", linewidth=1.5)ax.set_title('H2: SMA trend filter', fontweight='bold')ax.legend(fontsize=8)ax.grid(True, alpha=0.3)# H3: Max positionsax = axes[1, 0]for name, r in results_pos.items(): ax.plot(r['cum'].values, label=f"{name} (S={r['sharpe']:.2f})", linewidth=1.5)ax.set_title('H3: Max positions', fontweight='bold')ax.legend(fontsize=8)ax.grid(True, alpha=0.3)# H4: Universeax = axes[1, 1]for name, r in results_univ.items(): ax.plot(r['cum'].values, label=f"{name} (S={r['sharpe']:.2f})", linewidth=1.5)ax.set_title('H4: Universe composition', fontweight='bold')ax.legend(fontsize=7)ax.grid(True, alpha=0.3)plt.tight_layout()plt.savefig('futurestrend_quantbook_analysis.png', dpi=150, bbox_inches='tight')plt.show()
Sortie strippee (FABRICATED Row N / blank PNG). Re-execution QC Cloud recherche kernel requise – see #6891. Code preserve pour tracabilite. Side A = strip honnete, Side B = re-execution (hors scope ce PR).
8. Conclusions
Tableau recapitulatif
Hypothese
Résultat QuantBook
Coherent avec yfinance?
H1 Donchian periods
(a remplir)
(a verifier)
H2 SMA filter
(a remplir)
(a verifier)
H3 Max positions
(a remplir)
(a verifier)
H4 Universe
(a remplir)
(a verifier)
Règles du backlog appliquees
Règle #5: ATR sizing contre-productif (confirme dans research.ipynb)