Backtest05.04.2026schedule3 min read

Walk-Forward Analizi Nedir? Out-of-Sample Test ile Strateji Doğrulama

In-sample optimizasyonun tuzağından kaçmanın tek güvenilir yolu walk-forward analizi. vectorbt ile BIST hisseleri üzerinde adım adım uyguluyoruz.

Bir stratejiyi geçmiş veriyle optimize ettikten sonra "bu işe yarıyor" diyebilmek için o veriyi dışarıda bırakmış olmak gerekir. Kulağa paradoks gibi geliyor ama backtest'in özü bu. Walk-forward analizi bu paradoksu çözen yöntem.

Walk-Forward Analiz Konsepti

Walk-Forward Mantığı

Klasik backtest şöyle çalışır: tüm veriyi al, parametreleri optimize et, aynı veri üzerinde test et. Sonuç iyi çıkar çünkü "cevap anahtarına" bakarak sınav verdiniz.

Walk-forward analizi bunu değiştirir: veriyi pencereler halinde böler. Her pencere için in-sample dönemde optimize et, ardından hiç görmediğin out-of-sample dönemde test et.

Pencere 1: [IN-SAMPLE 12 ay] → [OUT-OF-SAMPLE 3 ay]
Pencere 2:    [IN-SAMPLE 12 ay] → [OUT-OF-SAMPLE 3 ay]
Pencere 3:       [IN-SAMPLE 12 ay] → [OUT-OF-SAMPLE 3 ay]

Adım 1: Kurulum ve Veri Hazırlığı

!pip install vectorbt yfinance --quiet

import vectorbt as vbt
import yfinance as yf
import pandas as pd
import numpy as np

df = yf.download("GARAN.IS", start="2020-01-01", end="2025-01-01", progress=False)
if isinstance(df.columns, pd.MultiIndex):
    df.columns = df.columns.droplevel(1)

close = df["Close"].dropna()
print(f"Toplam veri: {len(close)} gün ({close.index[0].date()} → {close.index[-1].date()})")

Adım 2: Veriyi Walk-Forward Pencerelerine Bölme

n_pencere = 8
pencere_uzun = 365
oos_uzunluk = 90

(is_fiyat, is_idx), (oos_fiyat, oos_idx) = close.vbt.rolling_split(
    n=n_pencere,
    window_len=pencere_uzun,
    set_lens=(oos_uzunluk,),
    left_to_right=False
)

print(f"Her pencerede:")
print(f"  In-sample  : {pencere_uzun - oos_uzunluk} gün")
print(f"  Out-of-sample: {oos_uzunluk} gün")

Strateji Test ve Doğrulama

Adım 3: In-Sample Optimizasyon

windows = np.arange(5, 51, 5)

def en_iyi_parametreyi_bul(fiyat_serisi, windows):
    fast_ma, slow_ma = vbt.MA.run_combs(
        fiyat_serisi, windows, r=2,
        short_names=["fast", "slow"]
    )
    entries = fast_ma.ma_crossed_above(slow_ma)
    exits = fast_ma.ma_crossed_below(slow_ma)

    pf = vbt.Portfolio.from_signals(
        fiyat_serisi, entries, exits,
        init_cash=100_000, fees=0.001, freq="1D"
    )

    sharpe = pf.sharpe_ratio()
    en_iyi = sharpe.idxmax()
    return en_iyi, sharpe.max()

en_iyi_params = []
is_sharpe_listesi = []

for i in range(n_pencere):
    pencere_fiyat = is_fiyat.iloc[:, i]
    params, sharpe = en_iyi_parametreyi_bul(pencere_fiyat, windows)
    en_iyi_params.append(params)
    is_sharpe_listesi.append(sharpe)
    print(f"Pencere {i+1}: fast={params[0]}, slow={params[1]} | IS Sharpe={sharpe:.2f}")

Adım 4: Out-of-Sample Test

oos_sonuclar = []

for i in range(n_pencere):
    fast_win = en_iyi_params[i][0]
    slow_win = en_iyi_params[i][1]
    oos_veri = oos_fiyat.iloc[:, i]

    fast_ma = vbt.MA.run(oos_veri, fast_win)
    slow_ma = vbt.MA.run(oos_veri, slow_win)

    entries = fast_ma.ma_crossed_above(slow_ma)
    exits = fast_ma.ma_crossed_below(slow_ma)

    pf = vbt.Portfolio.from_signals(
        oos_veri, entries, exits,
        init_cash=100_000, fees=0.001, freq="1D"
    )

    oos_getiri = pf.total_return() * 100
    oos_sharpe = pf.sharpe_ratio()
    oos_sonuclar.append({"getiri": oos_getiri, "sharpe": oos_sharpe})

    print(f"Pencere {i+1}: IS Sharpe={is_sharpe_listesi[i]:.2f} → OOS Sharpe={oos_sharpe:.2f}")

Backtest Sonuçları

GARAN.IS, 2020-2024, 8 pencere, 12 ay IS + 3 ay OOS, %0,1 komisyon dahil:

Metrik In-Sample Out-of-Sample
Ortalama Sharpe 1.42 0.71
Pozitif Sharpe Penceresi 8/8 5/8
Ortalama OOS Getiri %6.8 (3 aylık)

Bu sonuçlar geçmiş performansı gösterir, gelecek getiri garantisi vermez.

Sonuç

  • Walk-forward analizi backtest güvenilirliğinin en önemli testi.
  • IS-OOS Sharpe oranı arasındaki açılma tolere edilebilir bir düşüş mü yoksa tam çöküş mü? Genel kural: OOS Sharpe, IS Sharpe'ın en az %50'si olmalı.
  • BIST'te rejim değişimleri hızlı gerçekleşiyor. 6 aylık IS + 2 aylık OOS daha dinamik sonuç verebilir.

Bu yazıdaki kodlar eğitim amaçlıdır; yatırım tavsiyesi değildir.