← Makalelere geri dön
March 12, 2026
5 dakikalık okuma

Plato Analizi: Sağlam Bir Optimumu Aşırı Uyumdan Nasıl Ayırt Edilir

Plato Analizi: Sağlam Bir Optimumu Aşırı Uyumdan Nasıl Ayırt Edilir
#algotrading
#backtest
#optimizasyon
#aşırı uyum
#plato analizi
#parametre kararlılığı

"Yanılsamasız Backtestler" serisinin 6. makalesi

study.optimize() çalıştırdınız, Optuna +%87 PnL ile bir parametre seti buldu. Heyecanlandınız ve stratejiyi üretime hazırlıyorsunuz. İki hafta canlı işlem sonrasında PnL sıfır civarında. Ne oldu?

Optimizer, parametre uzayında bir iğnenin ucunu buldu. Parametreler geçmiş işlem dizisine mükemmel biçimde uydurulmuştur; ancak piyasa koşullarındaki en ufak sapma tüm yapıyı çökertiyor. Bu klasik aşırı uyumdur ve başlatmadan önce tespit edilebilirdi.

Önceki makalede koordinat inişini Bayes optimizasyonuyla karşılaştırdık ve Optuna'nın optimumu neden daha verimli bulduğunu gösterdik. Bugün — sonraki adım: bulunan optimumun gürültüye uyum sonucu değil, gerçekten sağlam olduğundan nasıl emin olunur.

"En İyi" Parametreleri Bulmak Neden Sadece İşin Yarısıdır

Çok boyutlu parametre uzayında arama Gerçek optimumu aramak için geniş çok boyutlu parametre manzarasında gezinen bir optimizer

Strateji parametre optimizasyonu, çok boyutlu bir uzayda maksimum arayışıdır. Sorun şu ki maksimumlar iki türe ayrılır:

  1. Plato — parametre değişimlerinde PnL'nin sürekli yüksek kaldığı geniş, düz bir bölge. Piyasa koşulları etkin parametreleri %10-20 kaydırsa bile strateji kâr etmeye devam eder.

  2. Keskin zirve — PnL'nin yalnızca tam parametre değerinde yüksek olduğu dar bir tepe. Bir adım kayma kârlılığı çöküyor. Bu neredeyse kesinlikle aşırı uyumdur: optimizer, kararlı bir örüntü değil, tarihsel verinin bir artefaktını bulmuştur.

Dağcılık metaforu: plato, güvenle yürünebilen dağ tablası. Keskin zirve ise ancak dengelenebileceğiniz bir iğne ucu.

Keskin Zirve ile Düz Plato — Görsel Sezgi

Keskin zirve ile düz plato karşılaştırması Sol: sağlam bir plato (geniş, yavaş eğimli masa dağı). Sağ: kırılgan bir keskin zirve (derin vadilerle çevrili iğne ucu)

Eksenlerinin iki strateji parametresi, rengin ise PnL'yi temsil ettiği bir kontur haritası hayal edin. İki örüntü görsel olarak kolayca ayırt edilir:

Plato (sağlam optimum):

  • Aynı rengin geniş alanları
  • PnL seviyeleri arasında yumuşak geçişler
  • Birbirinden uzak izoyigitler
  • Optimumdan +/-%20 kayma PnL'yi en fazla %10 değiştiriyor

Bir ısı haritası hayal edin: ortada — haritanın yaklaşık üçte biri kadar parlak sarı bir dikdörtgen. Renk kenarlara doğru yavaşça turuncuya, ardından kırmızıya geçiyor. Optimum bir nokta değil, bir bölge.

Keskin zirve (aşırı uyum):

  • Soğuk renklerle çevrili dar parlak bir nokta
  • Ani geçişler: optimumun hemen yanında çöküş
  • Tek bir noktanın etrafında sıkışık izoyigitler
  • +/-%5 kayma PnL'yi %50 veya daha fazla düşürüyor

Aynı ısı haritasını, ancak ortada hemen mavi ve morla çevrili küçük sarı bir nokta olduğunu hayal edin. Tek bir "doğru" parametre kombinasyonu.

Parametre Duyarlılığı Analizi

Parametre duyarlılığı dilim grafikleri PnL'nin bireysel parametre değerlerine nasıl bağlı olduğunu gösteren dilim grafikleri — geniş bantlar sağlamlığı, dar kümeler kırılganlığı gösterir

Tek Boyutlu Analiz: PnL ile Tek Parametre

En basit yaklaşım — biri hariç tüm parametreleri sabit tutmak ve PnL'nin değerine nasıl bağlı olduğunu görmek. Optuna bunun için plot_slice sağlar:

import optuna
from optuna.visualization import plot_slice

study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=500)

fig = plot_slice(study, params=["htf_entry_sell", "ltf_momentum", "stop_loss_pct"])
fig.show()

Dilim grafiğinde nelere bakılır:

  • Sağlam parametre: nokta bulutu, optimum yakınında geniş yatay bir bant oluşturur. En iyi denemeler, parametre değerlerinin geniş bir aralığına yayılmıştır.
  • Kırılgan parametre: en iyi denemeler dar bir aralıkta yoğunlaşmıştır. Parametreyi bir veya iki adım kaydırın — kârlılık çöker.

İki Boyutlu Analiz: Kontur Grafikleri (Isı Haritaları)

Kontur grafiği, iki parametrenin etkileşimini aynı anda gösterir. Bu, plato analizi için temel araçtır; çünkü parametreler nadiren bağımsız hareket eder — giriş ve çıkış eşikleri, zaman dilimleri ve pozisyon boyutları birbirine bağlıdır.

from optuna.visualization import plot_contour

fig = plot_contour(study, params=["htf_entry_sell", "htf_exit_buy"])
fig.show()

Sağlam bir parametre çifti için kontur grafiği, engebeli bir oyanın topografik haritasına benzer: yumuşak geniş izoyigitler, aynı rengin büyük alanları. Kırılgan bir çift için kontur grafiği — yanardağ konisinin haritası gibi: tek bir noktanın etrafında sıkışık eş merkezli halkalar.

12 ayrıştırma parametreli bir strateji için bu, (122)=66\binom{12}{2} = 66 ikili kontur grafiği verir. Hepsini incelemenize gerek yok — Optuna'nın en önemli olarak değerlendirdiği parametrelerle başlayın.

Çok Boyutlu Analiz: Parametre Önem Sıralaması

Optuna, her parametrenin hedef fonksiyona katkısını tahmin edebilir:

from optuna.visualization import plot_param_importances

fig = plot_param_importances(study)
fig.show()

Parametre önem grafiği yatay bir histogramdır. Parametreler, PnL varyansına katkılarına göre azalan sırayla sıralanır. İlk 3-4 parametre genellikle varyansın %70-80'ini açıklar.

Kural: bir parametre PnL varyansının %2'sinden azını açıklıyorsa, değeri pratikte sonuç için önemsizdir — tanım gereği sağlamdır. Plato analizini en önemli 5 parametreye odaklayın.

Optuna Görselleştirme Araçları

Optuna kontur grafikleri ve parametre önem görselleştirmesi Önem sıralamasının yanında parametre etkileşim manzarasını gösteren kontur ısı haritaları

plot_slice — Tek Boyutlu Dilimler

import optuna
from optuna.visualization import plot_slice

fig = plot_slice(study, params=[
    "htf_entry_sell", "htf_entry_buy",
    "ltf_momentum_threshold", "stop_loss_pct",
    "take_profit_pct", "trailing_stop_pct"
])
fig.update_layout(height=800, title="Parameter Slice Plots")
fig.show()

Sonuç — bir dağılım grafiği ızgarası. Her alt grafik, hedef fonksiyon değerini (PnL, Y ekseni) tek bir parametre değerine (X ekseni) karşı gösterir. Noktalar bireysel denemelerdir. Sağlam bir parametre için en iyi noktalar (en yüksek PnL), X'in geniş bir aralığına dağılmıştır. Kırılgan biri için — dar bir sütunda gruplandırılmıştır.

plot_contour — İki Boyutlu Konturlar

from optuna.visualization import plot_contour

important_pairs = [
    ["htf_entry_sell", "htf_entry_buy"],
    ["htf_entry_sell", "stop_loss_pct"],
    ["ltf_momentum_threshold", "take_profit_pct"],
]

for params in important_pairs:
    fig = plot_contour(study, params=params)
    fig.update_layout(title=f"Contour: {params[0]} vs {params[1]}")
    fig.show()

Her kontur grafiği, eksenlerde iki parametre bulunan bir ısı haritasıdır. Renk, parametre uzayının belirli bir bölgesindeki ortalama PnL'yi kodlar. Sarı/yeşil — yüksek PnL, mavi/mor — düşük. İzoyigitler aynı PnL'ye sahip noktaları bağlar.

plot_param_importances — Parametre Katkıları

from optuna.visualization import plot_param_importances

fig = plot_param_importances(
    study,
    evaluator=optuna.importance.FanovaImportanceEvaluator()
)
fig.show()

fANOVA (fonksiyonel ANOVA), hedef fonksiyonun varyansını parametreler ve etkileşimleri genelinde ayrıştırır. Bu, doğrusal olmayan etkileri hesaba kattığı için basit korelasyondan daha güçlüdür.

Plato İçin Sayısal Metrikler

Sayısal sağlamlık metrikleri görselleştirmesi Duyarlılık oranı, plato genişliği ve sağlamlık skoru — plato kalitesini resmileştiren üç metrik

Görsel değerlendirme özneldir. Sayılara ihtiyacımız var. İşte "plato" kavramını resmileştiren üç metrik.

Duyarlılık Oranı

PnL değişiminin parametre değişimine oranı:

Si=ΔPnL/PnLoptΔpi/pi,optS_i = \frac{\Delta \text{PnL} / \text{PnL}_{opt}}{\Delta p_i / p_{i,opt}}

burada ΔPnL\Delta \text{PnL}, pip_i parametresi optimumdan Δpi\Delta p_i kadar saptığında meydana gelen PnL düşüşüdür.

Yorum:

  • Si<0.5S_i < 0.5 — parametre sağlamdır: %10'luk kayma %5'ten az PnL düşüşüne neden olur
  • 0.5Si<2.00.5 \leq S_i < 2.0 — orta düzey duyarlılık
  • Si2.0S_i \geq 2.0 — parametre kırılgandır: %10'luk kayma PnL'yi %20+ çöküyor

Plato Genişliği

PnL'nin optimumun X%X\% dahilinde kaldığı parametre bölgesinin genişliği:

Wi(X)=pi,maxpi,minsubject toPnL(pi)(1X/100)×PnLoptW_i(X) = p_{i,max} - p_{i,min} \quad \text{subject to} \quad \text{PnL}(p_i) \geq (1 - X/100) \times \text{PnL}_{opt}

Göreli plato genişliği:

Wirel(X)=Wi(X)pi,maxrangepi,minrangeW_i^{rel}(X) = \frac{W_i(X)}{p_{i,max}^{range} - p_{i,min}^{range}}

burada payda, parametrenin tam arama aralığıdır.

Yorum:

  • Wirel(10%)>0.3W_i^{rel}(10\%) > 0.3 — plato, %10 eşiğinde aralığın %30'undan fazlasını kapsıyor. Sağlam parametre.
  • Wirel(10%)<0.05W_i^{rel}(10\%) < 0.05 — plato, aralığın %5'inden dardır. Kırmızı bayrak.

Sağlamlık Skoru

Tüm parametreler genelinde birleşik bir metrik:

R=i=1k(Wirel(10%))wiR = \prod_{i=1}^{k} \left( W_i^{rel}(10\%) \right)^{w_i}

burada wiw_i, fANOVA'dan elde edilen ii parametresinin normalleştirilmiş önemidir (wi=1\sum w_i = 1).

Ağırlıklı genişliklerin çarpımı katı bir metriktir: önemli bir parametre bile dar bir platoya sahipse, RR düşük olacaktır. Önemsiz parametrelerin (küçük wiw_i ile) neredeyse hiç etkisi yoktur.

Yorum:

  • R>0.1R > 0.1 — strateji sağlamdır
  • 0.01<R0.10.01 < R \leq 0.1 — ek doğrulama gerekli (walk-forward)
  • R0.01R \leq 0.01 — aşırı uyum çok olası

Otomatik Plato Tespiti İçin Python Kodu

Otomatik plato tespiti boru hattı Sağlam platoları ve kırılgan zirveleri belirlemek için parametre manzarasını tarayan otomatik sistem

import numpy as np
import optuna
from optuna.importance import FanovaImportanceEvaluator
from typing import Dict, List, Tuple

def compute_sensitivity_ratio(
    study: optuna.Study,
    param_name: str,
    n_steps: int = 20,
) -> float:
    """
    Tek bir parametre için duyarlılık oranını hesaplar.

    Diğer tüm parametreleri en iyi değerlerinde sabitler, param_name'i değiştirir,
    deneme enterpolasyonu aracılığıyla PnL düşüşünü tahmin eder.
    """
    best_trial = study.best_trial
    best_value = best_trial.values[0]
    best_param = best_trial.params[param_name]

    all_trials = [t for t in study.trials if t.state == optuna.trial.TrialState.COMPLETE]
    all_trials.sort(key=lambda t: t.values[0], reverse=True)
    top_trials = all_trials[:max(10, len(all_trials) // 5)]

    param_values = np.array([t.params[param_name] for t in top_trials])
    pnl_values = np.array([t.values[0] for t in top_trials])

    if best_param == 0 or best_value == 0:
        return float('inf')

    from numpy.polynomial import polynomial as P
    coeffs = np.polyfit(param_values, pnl_values, deg=2)
    dpnl_dparam = 2 * coeffs[0] * best_param + coeffs[1]

    sensitivity = abs(dpnl_dparam * best_param / best_value)
    return sensitivity


def compute_plateau_width(
    study: optuna.Study,
    param_name: str,
    threshold_pct: float = 10.0,
) -> Tuple[float, float]:
    """
    Mutlak ve göreli plato genişliğini hesaplar.

    Döndürür:
        (absolute_width, relative_width)
    """
    best_value = study.best_value
    threshold = best_value * (1 - threshold_pct / 100)

    trials = [t for t in study.trials if t.state == optuna.trial.TrialState.COMPLETE]
    good_trials = [t for t in trials if t.values[0] >= threshold]

    if not good_trials:
        return 0.0, 0.0

    good_params = [t.params[param_name] for t in good_trials]
    all_params = [t.params[param_name] for t in trials]

    plateau_min = min(good_params)
    plateau_max = max(good_params)
    absolute_width = plateau_max - plateau_min

    search_range = max(all_params) - min(all_params)
    relative_width = absolute_width / search_range if search_range > 0 else 0

    return absolute_width, relative_width


def compute_robustness_score(
    study: optuna.Study,
    threshold_pct: float = 10.0,
) -> Dict:
    """
    Birleşik sağlamlık skorunu hesaplar.

    Döndürür:
        parametre başına metrikler ve nihai skoru içeren dict
    """
    evaluator = FanovaImportanceEvaluator()
    importances = optuna.importance.get_param_importances(
        study, evaluator=evaluator
    )

    results = {}
    total_importance = sum(importances.values())

    for param_name, importance in importances.items():
        sensitivity = compute_sensitivity_ratio(study, param_name)
        abs_width, rel_width = compute_plateau_width(
            study, param_name, threshold_pct
        )

        weight = importance / total_importance
        results[param_name] = {
            "importance": importance,
            "weight": weight,
            "sensitivity_ratio": sensitivity,
            "plateau_width_abs": abs_width,
            "plateau_width_rel": rel_width,
        }

    log_score = sum(
        r["weight"] * np.log(max(r["plateau_width_rel"], 1e-10))
        for r in results.values()
    )
    robustness_score = np.exp(log_score)

    return {
        "robustness_score": robustness_score,
        "parameters": results,
        "verdict": (
            "robust" if robustness_score > 0.1
            else "check" if robustness_score > 0.01
            else "overfitting"
        ),
    }

Kullanım

report = compute_robustness_score(study, threshold_pct=10.0)

print(f"Robustness score: {report['robustness_score']:.4f}")
print(f"Verdict: {report['verdict']}")
print()

for name, metrics in report["parameters"].items():
    print(f"  {name}:")
    print(f"    Importance:       {metrics['importance']:.3f}")
    print(f"    Sensitivity:      {metrics['sensitivity_ratio']:.2f}")
    print(f"    Plateau width:    {metrics['plateau_width_rel']:.1%}")
    print()

Örnek çıktı:

Robustness score: 0.1482
Verdict: robust

  htf_entry_sell:
    Importance:       0.312
    Sensitivity:      0.38
    Plateau width:    42.5%

  htf_entry_buy:
    Importance:       0.251
    Sensitivity:      0.45
    Plateau width:    38.1%

  ltf_momentum_threshold:
    Importance:       0.187
    Sensitivity:      1.21
    Plateau width:    22.3%

  stop_loss_pct:
    Importance:       0.098
    Sensitivity:      0.67
    Plateau width:    31.0%

  take_profit_pct:
    Importance:       0.072
    Sensitivity:      0.89
    Plateau width:    28.4%

  trailing_delta:
    Importance:       0.031
    Sensitivity:      0.22
    Plateau width:    55.2%

Ayrıştırma Stratejileriyle Pratik Örnekler

Sağlamlık profiline göre karşılaştırılan üç strateji Strateji A (geniş plato, sağlam), Strateji B (orta), ve Strateji C (keskin zirve, aşırı uyumlu) karşılaştırması

12 ayrıştırma parametreli üç stratejiyi inceleyelim. Her strateji 500 denemeyle Optuna optimizasyonuna tabi tutuldu.

Strateji A (~%55 PnL, ~500 işlem, ~%15 zaman)

Strateji A'nın parametreleri geniş bir plato oluşturuyor. Anahtar parametre htf_entry_sell'i alalım:

  • Optimal değer: 0.020
  • 0.015'teki PnL: +%51 (%7 düşüş)
  • 0.025'teki PnL: +%49 (%11 düşüş)
  • 0.010'daki PnL: +%43 (%22 düşüş)
  • 0.030'daki PnL: +%41 (%25 düşüş)

Bunu tek boyutlu bir grafik olarak hayal ederseniz (X ekseni — htf_entry_sell değeri, Y ekseni — PnL), düz tepeli yumuşak bir parabol göreceksiniz. 0.010-0.030 aralığı platodur; burada PnL optimumun +/-%25'i içinde kalır.

Duyarlılık oranı: S=0.110.25=0.44S = \frac{0.11}{0.25} = 0.44 — sağlam.

%10 eşiğinde plato genişliği: 0.013'ten 0.027'ye, Wrel=0.0140.04=%35W^{rel} = \frac{0.014}{0.04} = \%35.

Strateji B (~%25 PnL, ~40 işlem, ~%5 zaman)

Strateji B az sayıda işlem üzerinde optimize edilmiştir. htf_entry_sell parametresi:

  • Optimal değer: 0.018
  • 0.015'teki PnL: +%24 (%4 düşüş)
  • 0.025'teki PnL: +%9 (%64 düşüş)
  • 0.012'deki PnL: +%11 (%56 düşüş)

Grafikte — asimetrik ve dik bir eğri. Plato yalnızca 0.015-0.020 aralığında mevcuttur. Optimumun sağında — bir uçurum.

Duyarlılık oranı: S=0.640.39=1.64S = \frac{0.64}{0.39} = 1.64 — orta düzey duyarlılık, ancak 40 işlemle bu kırmızı bayraktır. Küçük örneklem + dar plato = yüksek aşırı uyum olasılığı.

%10 eşiğinde plato genişliği: 0.016'dan 0.020'ye, Wrel=0.0040.04=%10W^{rel} = \frac{0.004}{0.04} = \%10.

Strateji C (~%300 PnL, ~400 işlem, ~%45 zaman)

Strateji C çarpıcı PnL gösteriyor, ancak plato analizi sorunları ortaya koyuyor:

  • htf_entry_sell'in optimal değeri: 0.022
  • 0.020'deki PnL: +%295 (%2 düşüş)
  • 0.025'teki PnL: +%142 (%53 düşüş)
  • 0.019'daki PnL: +%128 (%57 düşüş)

Grafikte — karakteristik "iğne": 0.022'de çok yüksek bir zirve, tüm yönlerde ani düşüş. Kontur grafiği soğuk renklerle hemen çevrili parlak bir nokta gösteriyor.

Duyarlılık oranı: S=0.530.14=3.79S = \frac{0.53}{0.14} = 3.79kırılgan. 400 işleme rağmen strateji, tek bir parametrenin tam değerine aşırı bağımlıdır.

%10 eşiğinde plato genişliği: 0.021'den 0.023'e, Wrel=0.0020.04=%5W^{rel} = \frac{0.002}{0.04} = \%5.

Özet Tablo

Strateji PnL İşlemler Duyarlılık Plato genişliği Sağlamlık skoru Karar
Strateji A +%55 ~500 0.44 %35 0.148 Sağlam
Strateji B +%25 ~40 1.64 %10 0.032 Kontrol Et (küçük örneklem)
Strateji C +%300 ~400 3.79 %5 0.008 Aşırı Uyum

Paradoks: +%300 PnL ile Strateji C en düşük sağlamlık skoruna sahip. "Mütevazı" +%55 ile Strateji A en sağlamdır. Bu tipik bir plato analizi sonucudur: etkileyici sayılar çoğunlukla kırılganlığı maskeler.

Her stratejinin güven aralıkları Monte Carlo bootstrap aracılığıyla ayrıca doğrulanabilir — işlemleri yeniden örneklerken PnL dağılımını gösterecektir.

3D Görselleştirme ve Isı Haritaları

Kontur projeksiyonlu 3D parametre manzarası yüzeyi İki parametrede PnL'nin taban düzlemine yansıtılan kontur çizgileriyle 3D yüzey grafiği

En önemli parametre çiftleri için 3D yüzey ve ısı haritası oluşturmak faydalıdır. Bu, manzara şekli hakkında sezgisel anlayış sağlar.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

def plot_parameter_landscape(
    study: "optuna.Study",
    param_x: str,
    param_y: str,
    grid_size: int = 50,
):
    """
    Bir parametre çifti için 3D yüzey grafiği ve ısı haritası oluşturur.
    """
    trials = [t for t in study.trials
              if t.state == optuna.trial.TrialState.COMPLETE]

    x_vals = np.array([t.params[param_x] for t in trials])
    y_vals = np.array([t.params[param_y] for t in trials])
    z_vals = np.array([t.values[0] for t in trials])

    from scipy.interpolate import griddata

    xi = np.linspace(x_vals.min(), x_vals.max(), grid_size)
    yi = np.linspace(y_vals.min(), y_vals.max(), grid_size)
    Xi, Yi = np.meshgrid(xi, yi)
    Zi = griddata((x_vals, y_vals), z_vals, (Xi, Yi), method='cubic')

    fig = plt.figure(figsize=(18, 7))

    ax1 = fig.add_subplot(121, projection='3d')
    surf = ax1.plot_surface(Xi, Yi, Zi, cmap=cm.viridis, alpha=0.85,
                            edgecolor='none')
    ax1.set_xlabel(param_x)
    ax1.set_ylabel(param_y)
    ax1.set_zlabel('PnL, %')
    ax1.set_title('3D Parameter Landscape')
    fig.colorbar(surf, ax=ax1, shrink=0.5)

    ax2 = fig.add_subplot(122)
    hm = ax2.pcolormesh(Xi, Yi, Zi, cmap=cm.viridis, shading='auto')
    contours = ax2.contour(Xi, Yi, Zi, levels=10, colors='white',
                           linewidths=0.8, alpha=0.7)
    ax2.clabel(contours, inline=True, fontsize=8, fmt='%.0f%%')

    best = study.best_trial
    ax2.scatter(best.params[param_x], best.params[param_y],
                color='red', s=100, marker='*', zorder=5, label='Optimum')

    ax2.set_xlabel(param_x)
    ax2.set_ylabel(param_y)
    ax2.set_title('Contour Heatmap')
    ax2.legend()
    fig.colorbar(hm, ax=ax2)

    plt.tight_layout()
    plt.savefig(f'landscape_{param_x}_vs_{param_y}.png', dpi=150)
    plt.show()

Sağlam bir strateji için 3D yüzey grafiği bir masa dağına benzer — düz tepe, yumuşak eğimler. Kırılgan bir strateji için — Matterhorn gibi keskin bir zirve. Isı haritası, 3D görünümünü tamamlar ve aynı bilgiyi izoyigitlerle birlikte tepeden aşağıya bir projeksiyonda gösterir.

Kırmızı Bayraklar: Optimizasyon Sonuçları Şüpheli Göründüğünde

Optimizasyon sonuçları için kırmızı bayrak göstergesi Optimizasyon sonuçlarında potansiyel aşırı uyumu işaret eden uyarı göstergeleri

Optimizasyonun gerçek bir örüntü yerine aşırı uyum bulduğunu gösteren sekiz işaret:

1. Anahtar Parametre İçin Duyarlılık Oranı > 2

PnL, %10'luk parametre kaymasıyla %20'den fazla düşüyorsa — optimum kırılgandır.

2. Plato Genişliği < Arama Aralığının %10'u

"İyi" bölge, keşfedilen aralığın %10'undan azını kaplıyorsa — optimizer büyük olasılıkla bir artefakt bulmuştur.

3. İlk 3 Deneme, Medyanın 2-3 Katı PnL Veriyor

En iyi denemeler, "tepedeki" denemeler değil de geri kalanların arasında aykırı değerlerse — bu bir plato değildir.

top_3_mean = np.mean(sorted([t.values[0] for t in study.trials
                              if t.state == optuna.trial.TrialState.COMPLETE],
                             reverse=True)[:3])
median_pnl = np.median([t.values[0] for t in study.trials
                         if t.state == optuna.trial.TrialState.COMPLETE])

outlier_ratio = top_3_mean / median_pnl
if outlier_ratio > 2.5:
    print(f"UYARI: İlk denemeler medyanın {outlier_ratio:.1f} katı — olası aşırı uyum")

4. Düşük İşlem Sayısı (< 50) ile Yüksek PnL

Küçük örneklem + yüksek PnL = tahminlerde yüksek varyans. 40 işlemde plato analizi başlı başına güvenilmezdir. Bu tür stratejiler için Monte Carlo bootstrap kritiktir.

5. Tek "Sihirli" Parametre Kombinasyonu

Kontur grafiği gri bir alan ortasında tek parlak nokta gösteriyorsa — bu bir strateji değil, veriye uydurulmuş bir kombinasyondur.

6. Çok Fazla Parametre

Her biri 10 değer alan 12 parametre için arama uzayı 101210^{12} kombinasyon içerir. Optuna yaklaşık 500'ünü keşfeder. Böyle bir uzayda "iyi" bir artefakt bulma olasılığı yüksektir. Parametre sayısı ne kadar fazlaysa, plato analizi o kadar sıkı olmalıdır.

7. Örneklem Dışında PnL Keskin Düşüş Gösteriyor

Örneklem içi PnL +%87 ise ve walk-forward +%12 gösteriyorsa — optimizasyon parametreleri eğitim dönemine uydurmuştur. Bu konuda daha fazla bilgi Walk-Forward optimizasyonu makalesinde.

8. Parametreler Aralık Sınırlarına "Yapışmış"

Optimal değer arama ızgarası sınırıyla örtüşüyorsa — optimum aralığın dışında olabilir. Aralığı genişletip optimizasyonu yeniden çalıştırın.

Otomatik Plato Analizi Raporu

Her optimizasyondan sonra oluşturulan tek bir raporda hepsini bir araya getirmek:

import json
from datetime import datetime

def generate_plateau_report(
    study: "optuna.Study",
    strategy_name: str,
    n_trades: int,
    threshold_pct: float = 10.0,
) -> dict:
    """
    Eksiksiz bir plato analizi raporu oluşturur.
    """
    robustness = compute_robustness_score(study, threshold_pct)

    red_flags = []

    sorted_params = sorted(
        robustness["parameters"].items(),
        key=lambda x: x[1]["importance"],
        reverse=True
    )
    for name, metrics in sorted_params[:3]:
        if metrics["sensitivity_ratio"] > 2.0:
            red_flags.append(
                f"High sensitivity for {name}: "
                f"S={metrics['sensitivity_ratio']:.2f}"
            )

    for name, metrics in robustness["parameters"].items():
        if metrics["plateau_width_rel"] < 0.05:
            red_flags.append(
                f"Narrow plateau for {name}: "
                f"W={metrics['plateau_width_rel']:.1%}"
            )

    all_values = sorted(
        [t.values[0] for t in study.trials
         if t.state == optuna.trial.TrialState.COMPLETE],
        reverse=True
    )
    if len(all_values) > 10:
        top3 = np.mean(all_values[:3])
        med = np.median(all_values)
        if med > 0 and top3 / med > 2.5:
            red_flags.append(
                f"Top trials are outliers: "
                f"{top3:.1f} vs median {med:.1f} "
                f"({top3/med:.1f}x)"
            )

    if n_trades < 50:
        red_flags.append(f"Low trade count: {n_trades}")

    report = {
        "strategy": strategy_name,
        "timestamp": datetime.now().isoformat(),
        "best_pnl": study.best_value,
        "n_trials": len(study.trials),
        "n_trades": n_trades,
        "robustness_score": robustness["robustness_score"],
        "verdict": robustness["verdict"],
        "red_flags": red_flags,
        "parameters": robustness["parameters"],
    }

    return report


report = generate_plateau_report(
    study, strategy_name="Strategy A", n_trades=491
)

print(json.dumps(report, indent=2, default=str))

Örnek çıktı:

{
  "strategy": "Strategy A",
  "best_pnl": 55.2,
  "n_trials": 500,
  "n_trades": 491,
  "robustness_score": 0.1482,
  "verdict": "robust",
  "red_flags": [],
  "parameters": {
    "htf_entry_sell": {
      "importance": 0.312,
      "sensitivity_ratio": 0.44,
      "plateau_width_rel": 0.35
    }
  }
}

Walk-Forward Doğrulama ile İlişki

Walk-forward doğrulamanın plato analizini tamamlaması İki tamamlayıcı doğrulama sistemi olarak parametrik sağlamlık (plato analizi) ve zamansal sağlamlık (walk-forward)

Plato analizi ve walk-forward doğrulama (WFO) tamamlayıcı yöntemlerdir:

  • Plato analizi şu soruyu yanıtlar: "Küçük parametre kaymalarında optimum ne kadar kararlıdır?" Bu, parametrik sağlamlık kontrolüdür.
  • Walk-forward şu soruyu yanıtlar: "Parametreler optimizerin görmediği veriler üzerinde işe yarıyor mu?" Bu, zamansal sağlamlık kontrolüdür.

Bir strateji plato analizini geçebilir (geniş plato) ancak walk-forward'da başarısız olabilir (piyasa rejimi değişti). Ve tam tersi — sabit parametrelerle walk-forward'ı geçebilir ancak kırılgan bir optimuma sahip olabilir.

Öneri: her zaman her iki yöntemi kullanın. Bir strateji plato analizini (R>0.1R > 0.1) ve walk-forward'ı (PnLOOS>50%×PnLIS\text{PnL}_{OOS} > 50\% \times \text{PnL}_{IS}) geçerse — bu güçlü bir sağlamlık sinyalidir. Daha fazla ayrıntı Walk-Forward optimizasyonu makalesinde.

Her aşamada PnL güven aralıklarını değerlendirmek için Monte Carlo bootstrap uygulayın. Farklı aktif süreli stratejileri doğru karşılaştırmak için aktif süre başına PnL metriğini kullanın.

Öneriler

Optimizasyon Öncesi

  1. Parametre sayısını sınırlayın. Ne kadar az parametre — o kadar güvenilir plato. 5-7 parametre makul bir maksimum. 12'si zaten artırılmış dikkat gerektirir.

  2. Anlamlı aralıklar belirleyin. Gerçekçi aralık 0.005-0.05 ise htf_entry_sell'i 0.001'den 1.0'a ayarlamayın. Gereksiz geniş aralıklar plato yanılsaması yaratır.

  3. Yeterli deneme sayısı kullanın. 12 parametre için minimum 300-500 deneme. Güvenilir plato analizi için — 1000+.

Optimizasyon Sırasında

  1. Yakınsama izleyin. Optuna 400 denemeden sonra önemli ölçüde daha iyi çözümler bulmaya devam ediyorsa — süreç yakınsamadı ve plato analizi güvenilmez olacak.

  2. Budamayı dikkatli kullanın. Agresif budama (MedianPruner), erken adımlarda kötü görünen ancak tam bir manzara resmi oluşturmak için önemli olan denemeleri kesebilir.

Optimizasyon Sonrasında

  1. Plato raporunu otomatik olarak oluşturun. generate_plateau_report() işlevini optimizasyon boru hattına entegre edin. Görsel değerlendirmeye güvenmeyin — sayıları kullanın.

  2. İlk 5 parametreyi kontrol edin. fANOVA, 3 parametrenin varyansın %80'ini açıkladığını gösteriyorsa — kalan 9'u daha az titizlikle kontrol edebilirsiniz.

  3. Temel stratejiyle karşılaştırın. Varsayılan parametreli strateji (optimizasyon yok) +%30 gösteriyorsa ve optimize edilmiş +%55 gösteriyorsa — fark yalnızca 25 pp ve plato büyük olasılıkla geniştir. Varsayılan %0 gösteriyorsa ve optimize edilmiş +%300 gösteriyorsa — tüm kârlılık kesin parametre uyumuna bağlıdır.

  4. Son kontrol — walk-forward. Plato analizi, sağlamlık için gerekli ama yeterli bir koşul değildir. Her zaman örneklem dışını doğrulayın.

Sonuç

Parametre optimizasyonu güçlü bir araçtır, ancak plato analizi olmadan bir rulet oyunudur. Kararlı bir örüntü mü buldunuz yoksa modeli gürültüye mi uydurdunuz bilmiyorsunuz.

Plato analizinin üç kuralı:

  1. Sağlamlık skorunu hesaplayın. Ağırlıklı plato genişliklerinin çarpımı, tüm parametrelerin sağlamlığını özetleyen tek bir sayı verir. R>0.1R > 0.1 — yeşil ışık.

  2. Anahtar parametreler için duyarlılık oranı < 1. %10'luk parametre kayması %10'dan az PnL düşüşüne neden oluyorsa — parametre sağlamdır. Daha fazlaysa — dikkatli olun.

  3. Kontur grafiklerini görselleştirin. Hiçbir metrik manzara şeklini anlamanın yerini tutamaz. Düz masa dağı — iyi. Keskin iğne — kötü.

Plato analizi optimizasyondan sonra 5 dakika alır ve haftalarca kârsız canlı işlemden kurtarabilir. study.optimize() ile botu başlatmak arasında zorunlu bir adımdır.


Faydalı Bağlantılar

  1. Optuna Belgeleri — Görselleştirme
  2. Hutter, F., Hoos, H., Leyton-Brown, K. — An Efficient Approach for Assessing Hyperparameter Importance (fANOVA, 2014)
  3. Pardo, R. — The Evaluation and Optimization of Trading Strategies
  4. Marcos Lopez de Prado — Advances in Financial Machine Learning, Chapter 11: Dangers of Backtesting
  5. Bailey, D.H. et al. — The Probability of Backtest Overfitting (2015)
  6. Optuna — optuna.visualization.plot_contour
  7. Optuna — optuna.importance.FanovaImportanceEvaluator
  8. Bergstra, J. & Bengio, Y. — Random Search for Hyper-Parameter Optimization (2012)

Atıf

@article{soloviov2026plateauanalysis,
  author = {Soloviov, Eugen},
  title = {Plateau Analysis: How to Distinguish a Robust Optimum from Overfitting},
  year = {2026},
  url = {https://marketmaker.cc/tr/blog/post/plateau-analysis-overfitting},
  version = {0.1.0},
  description = {En iyi strateji parametrelerini bulmak neden işin yalnızca yarısıdır. Kararlı bir platoyu kırılgan bir zirveden görsel ve sayısal olarak nasıl ayırt edersiniz ve neden Optuna kontur grafikleri optimize edilmiş bir stratejiyi üretime geçirmeden önce zorunlu bir adımdır.}
}
Sorumluluk Reddi: Bu makalede sağlanan bilgiler yalnızca eğitim ve bilgilendirme amaçlıdır ve finansal, yatırım veya ticaret tavsiyesi niteliği taşımaz. Kripto para ticareti önemli bir kayıp riski içerir.

Yazarlar

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Piyasanın Önünde Olun

Özel yapay zeka ticaret içgörüleri, piyasa analizi ve platform güncellemeleri için bültenimize abone olun.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.