← Kembali ke artikel
March 12, 2026
5 menit baca

Analisis Plateau: Cara Membedakan Optimum yang Robust dari Overfitting

Analisis Plateau: Cara Membedakan Optimum yang Robust dari Overfitting
#algotrading
#backtest
#optimasi
#overfitting
#analisis plateau
#stabilitas parameter

Artikel ke-6 dalam seri "Backtest Tanpa Ilusi"

Anda menjalankan study.optimize(), Optuna menemukan sekumpulan parameter dengan PnL +87%. Anda antusias dan mempersiapkan strategi untuk produksi. Dua minggu live trading kemudian, PnL mendekati nol. Apa yang terjadi?

Optimizer menemukan ujung jarum dalam ruang parameter. Parameter tersebut sangat cocok dengan urutan historis transaksi — tetapi sedikit saja perubahan dalam kondisi pasar akan menghancurkan seluruh konstruksi. Ini adalah overfitting klasik, dan hal itu bisa dideteksi sebelum peluncuran.

Dalam artikel sebelumnya kami membandingkan coordinate descent dengan optimasi Bayesian dan menunjukkan mengapa Optuna menemukan optimum dengan lebih efisien. Hari ini — langkah berikutnya: cara memastikan bahwa optimum yang ditemukan bersifat robust, bukan hasil fitting terhadap noise.

Mengapa Menemukan Parameter "Terbaik" Hanyalah Separuh Pekerjaan

Mencari melalui ruang parameter multidimensi Sebuah optimizer menavigasi lanskap parameter multidimensi yang luas untuk mencari optimum sejati

Optimasi parameter strategi adalah pencarian maksimum dalam ruang multidimensi. Masalahnya adalah bahwa maksimum hadir dalam dua jenis:

  1. Plateau — wilayah datar yang lebar di mana PnL secara konsisten tinggi di seluruh variasi parameter. Bahkan jika kondisi pasar menggeser parameter efektif sebesar 10-20%, strategi akan tetap menghasilkan profit.

  2. Puncak tajam — puncak sempit di mana PnL hanya tinggi pada nilai parameter yang tepat. Pergeseran satu langkah saja menghancurkan profitabilitas. Ini hampir pasti overfitting: optimizer menemukan artefak dari data historis, bukan pola yang stabil.

Metafora mendaki gunung: plateau adalah dataran tinggi pegunungan yang bisa Anda jalani dengan aman. Puncak tajam adalah ujung jarum tempat Anda hanya bisa menyeimbangkan diri.

Puncak Tajam vs Plateau Datar — Intuisi Visual

Perbandingan puncak tajam versus plateau datar Kiri: plateau yang robust (gunung meja yang lebar dengan lereng yang landai). Kanan: puncak tajam yang rapuh (ujung jarum yang dikelilingi lembah dalam)

Bayangkan peta kontur di mana sumbu-sumbunya adalah dua parameter strategi dan warnanya mewakili PnL. Dua pola mudah dibedakan secara visual:

Plateau (optimum yang robust):

  • Area luas dengan warna yang sama
  • Transisi halus antar level PnL
  • Garis isoline berjauhan
  • Pergeseran dari optimum sebesar +/-20% mengubah PnL tidak lebih dari 10%

Bayangkan sebuah heatmap: di tengah — persegi panjang kuning cerah yang kira-kira sepertiga dari ukuran seluruh peta. Warnanya berangsur beralih ke oranye, kemudian merah ke arah tepi. Optimum bukan sebuah titik, melainkan sebuah wilayah.

Puncak tajam (overfitting):

  • Titik terang sempit yang dikelilingi warna dingin
  • Transisi tiba-tiba: keruntuhan tepat di sebelah optimum
  • Garis isoline terkompresi dalam cincin-cincin ketat
  • Pergeseran sebesar +/-5% menurunkan PnL sebesar 50% atau lebih

Bayangkan heatmap yang sama, tetapi di tengah — titik kuning kecil yang segera dikelilingi biru dan ungu. Satu kombinasi parameter yang "benar".

Analisis Sensitivitas Parameter

Plot irisan sensitivitas parameter Plot irisan yang menunjukkan bagaimana PnL bergantung pada nilai parameter individual — band lebar menunjukkan robustness, cluster sempit menunjukkan kerapuhan

Analisis Satu Dimensi: PnL vs Satu Parameter

Pendekatan paling sederhana — perbaiki semua parameter kecuali satu dan lihat bagaimana PnL bergantung pada nilainya. Optuna menyediakan plot_slice untuk ini:

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()

Yang perlu dicari pada plot irisan:

  • Parameter robust: awan titik membentuk band horizontal lebar dekat optimum. Trial terbaik tersebar di rentang nilai parameter yang luas.
  • Parameter rapuh: trial terbaik terkonsentrasi dalam rentang sempit. Menggeser parameter satu atau dua langkah — dan profitabilitas runtuh.

Analisis Dua Dimensi: Contour Plot (Heatmap)

Contour plot menunjukkan interaksi dua parameter secara bersamaan. Ini adalah alat utama untuk analisis plateau, karena parameter jarang bertindak secara independen — ambang masuk dan keluar, timeframe, dan ukuran posisi saling terkait.

from optuna.visualization import plot_contour

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

Contour plot untuk pasangan parameter yang robust terlihat seperti peta topografi dataran berbukit: isoline yang halus dan lebar, area besar dengan warna yang sama. Contour plot untuk pasangan yang rapuh — seperti peta kerucut vulkanik: cincin konsentris yang ketat di sekitar satu titik.

Untuk strategi dengan 12 parameter separasi, ini menghasilkan (122)=66\binom{12}{2} = 66 contour plot berpasangan. Anda tidak harus mempelajari semuanya — mulailah dengan parameter yang dinilai paling penting oleh Optuna.

Analisis Multidimensi: Peringkat Kepentingan Parameter

Optuna dapat memperkirakan kontribusi setiap parameter terhadap fungsi objektif:

from optuna.visualization import plot_param_importances

fig = plot_param_importances(study)
fig.show()

Grafik kepentingan parameter adalah histogram horizontal. Parameter diurutkan berdasarkan kontribusinya terhadap variansi PnL secara menurun. 3-4 parameter teratas biasanya menjelaskan 70-80% dari variansi.

Aturan: jika sebuah parameter menjelaskan kurang dari 2% variansi PnL, nilainya secara praktis tidak relevan terhadap hasil — ia robust secara definisi. Fokuskan analisis plateau pada 5 parameter terpenting.

Alat Visualisasi Optuna

Contour plot dan visualisasi kepentingan parameter Optuna Heatmap kontur yang menunjukkan lanskap interaksi parameter bersama peringkat kepentingan

plot_slice — Irisan Satu Dimensi

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()

Hasilnya — grid scatter plot. Setiap subplot menunjukkan nilai fungsi objektif (PnL, sumbu-Y) terhadap nilai parameter tunggal (sumbu-X). Titik-titik adalah trial individual. Untuk parameter yang robust, titik terbaik (PnL tertinggi) terdistribusi di rentang X yang luas. Untuk yang rapuh — berkelompok dalam kolom sempit.

plot_contour — Kontur Dua Dimensi

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()

Setiap contour plot adalah heatmap dengan dua parameter pada sumbunya. Warna mengkodekan rata-rata PnL di wilayah tertentu dalam ruang parameter. Kuning/hijau — PnL tinggi, biru/ungu — rendah. Garis isoline menghubungkan titik-titik dengan PnL yang sama.

plot_param_importances — Kontribusi Parameter

from optuna.visualization import plot_param_importances

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

fANOVA (functional ANOVA) menguraikan variansi fungsi objektif di seluruh parameter dan interaksinya. Ini lebih kuat dari korelasi sederhana karena memperhitungkan efek non-linear.

Metrik Kuantitatif Plateau

Visualisasi metrik robustness kuantitatif Sensitivity ratio, lebar plateau, dan robustness score — tiga metrik yang memformalkan kualitas plateau

Penilaian visual bersifat subjektif. Kita membutuhkan angka. Berikut tiga metrik yang memformalkan konsep "plateau."

Sensitivity Ratio

Rasio perubahan PnL terhadap perubahan parameter:

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

di mana ΔPnL\Delta \text{PnL} adalah penurunan PnL ketika parameter pip_i menyimpang dari optimum sebesar Δpi\Delta p_i.

Interpretasi:

  • Si<0.5S_i < 0.5 — parameter robust: pergeseran 10% menyebabkan penurunan PnL kurang dari 5%
  • 0.5Si<2.00.5 \leq S_i < 2.0 — sensitivitas sedang
  • Si2.0S_i \geq 2.0 — parameter rapuh: pergeseran 10% menghancurkan PnL sebesar 20%+

Lebar Plateau

Lebar wilayah parameter di mana PnL tetap berada dalam X%X\% dari optimum:

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}

Lebar plateau relatif:

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

di mana penyebut adalah rentang pencarian penuh dari parameter.

Interpretasi:

  • Wirel(10%)>0.3W_i^{rel}(10\%) > 0.3 — plateau mencakup lebih dari 30% rentang pada ambang 10%. Parameter robust.
  • Wirel(10%)<0.05W_i^{rel}(10\%) < 0.05 — plateau lebih sempit dari 5% rentang. Tanda bahaya.

Robustness Score

Metrik gabungan di seluruh parameter:

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

di mana wiw_i adalah kepentingan parameter ii yang dinormalisasi dari fANOVA (wi=1\sum w_i = 1).

Produk dari lebar tertimbang adalah metrik yang ketat: jika bahkan satu parameter penting memiliki plateau sempit, RR akan rendah. Parameter yang tidak penting (dengan wiw_i kecil) hampir tidak berpengaruh.

Interpretasi:

  • R>0.1R > 0.1 — strategi robust
  • 0.01<R0.10.01 < R \leq 0.1 — validasi tambahan diperlukan (walk-forward)
  • R0.01R \leq 0.01 — overfitting sangat mungkin terjadi

Kode Python untuk Deteksi Plateau Otomatis

Pipeline deteksi plateau otomatis Sistem otomatis yang memindai lanskap parameter untuk mengidentifikasi plateau yang robust dan puncak-puncak yang rapuh

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:
    """
    Compute sensitivity ratio for a single parameter.

    Fixes all parameters at their best values, varies param_name,
    estimates PnL drop through trial interpolation.
    """
    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]:
    """
    Compute absolute and relative plateau width.

    Returns:
        (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:
    """
    Compute combined robustness score.

    Returns:
        dict with per-parameter metrics and the final score
    """
    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"
        ),
    }

Penggunaan

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()

Contoh output:

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%

Contoh Praktis dengan Strategi Separasi

Tiga strategi dibandingkan berdasarkan profil robustness Membandingkan Strategi A (plateau lebar, robust), Strategi B (sedang), dan Strategi C (puncak tajam, overfitting)

Mari kita periksa tiga strategi dengan 12 parameter separasi. Setiap strategi menjalani optimasi Optuna dengan 500 trial.

Strategi A (~55% PnL, ~500 transaksi, ~15% waktu)

Parameter Strategi A membentuk plateau yang lebar. Ambil parameter utama htf_entry_sell:

  • Nilai optimal: 0.020
  • PnL pada 0.015: +51% (penurunan 7%)
  • PnL pada 0.025: +49% (penurunan 11%)
  • PnL pada 0.010: +43% (penurunan 22%)
  • PnL pada 0.030: +41% (penurunan 25%)

Jika Anda membayangkan ini sebagai plot satu dimensi (sumbu-X — nilai htf_entry_sell, sumbu-Y — PnL), Anda akan melihat parabola yang landai dengan puncak datar. Rentang 0.010-0.030 adalah plateau, di mana PnL tetap dalam +/-25% dari optimum.

Sensitivity ratio: S=0.110.25=0.44S = \frac{0.11}{0.25} = 0.44 — robust.

Lebar plateau pada ambang 10%: dari 0.013 hingga 0.027, Wrel=0.0140.04=35%W^{rel} = \frac{0.014}{0.04} = 35\%.

Strategi B (~25% PnL, ~40 transaksi, ~5% waktu)

Strategi B dioptimalkan pada jumlah transaksi yang kecil. Parameter htf_entry_sell:

  • Nilai optimal: 0.018
  • PnL pada 0.015: +24% (penurunan 4%)
  • PnL pada 0.025: +9% (penurunan 64%)
  • PnL pada 0.012: +11% (penurunan 56%)

Pada plot — kurva asimetris dan curam. Plateau hanya ada dalam rentang sempit 0.015-0.020. Di sebelah kanan optimum — tebing.

Sensitivity ratio: S=0.640.39=1.64S = \frac{0.64}{0.39} = 1.64 — sensitivitas sedang, tetapi dengan 40 transaksi ini adalah tanda bahaya. Sampel kecil + plateau sempit = probabilitas tinggi overfitting.

Lebar plateau pada ambang 10%: dari 0.016 hingga 0.020, Wrel=0.0040.04=10%W^{rel} = \frac{0.004}{0.04} = 10\%.

Strategi C (~300% PnL, ~400 transaksi, ~45% waktu)

Strategi C menunjukkan PnL yang menakjubkan, tetapi analisis plateau mengungkap masalah:

  • Nilai optimal htf_entry_sell: 0.022
  • PnL pada 0.020: +295% (penurunan 2%)
  • PnL pada 0.025: +142% (penurunan 53%)
  • PnL pada 0.019: +128% (penurunan 57%)

Pada plot — "jarum" yang khas: puncak sangat tinggi pada 0.022, penurunan tajam ke segala arah. Contour plot akan menunjukkan titik terang yang segera dikelilingi warna dingin.

Sensitivity ratio: S=0.530.14=3.79S = \frac{0.53}{0.14} = 3.79rapuh. Meskipun ada 400 transaksi, strategi ini terlalu bergantung pada nilai tepat dari satu parameter.

Lebar plateau pada ambang 10%: dari 0.021 hingga 0.023, Wrel=0.0020.04=5%W^{rel} = \frac{0.002}{0.04} = 5\%.

Tabel Ringkasan

Strategi PnL Transaksi Sensitivitas Lebar plateau Robustness score Kesimpulan
Strategi A +55% ~500 0.44 35% 0.148 Robust
Strategi B +25% ~40 1.64 10% 0.032 Periksa (sampel kecil)
Strategi C +300% ~400 3.79 5% 0.008 Overfitting

Paradoks: Strategi C dengan PnL +300% memiliki robustness score terburuk. Strategi A dengan +55% yang "sederhana" adalah yang paling robust. Ini adalah hasil tipikal analisis plateau: angka yang mengesankan sering menyembunyikan kerapuhan.

Interval kepercayaan untuk setiap strategi dapat diverifikasi lebih lanjut melalui Monte Carlo bootstrap — ini akan menunjukkan sebaran PnL saat resampling transaksi.

Visualisasi 3D dan Heatmap

Permukaan lanskap parameter 3D dengan proyeksi kontur Plot permukaan 3D dari PnL terhadap dua parameter dengan garis kontur yang diproyeksikan ke bidang lantai

Untuk pasangan parameter terpenting, berguna untuk membangun permukaan 3D dan heatmap. Ini memberikan pemahaman intuitif tentang bentuk lanskap.

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,
):
    """
    Build a 3D surface plot and heatmap for a pair of parameters.
    """
    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()

Plot permukaan 3D untuk strategi yang robust menyerupai gunung meja — puncak datar dengan lereng yang landai. Untuk strategi yang rapuh — puncak tajam, seperti Matterhorn. Heatmap melengkapi tampilan 3D, menampilkan informasi yang sama dalam proyeksi atas-bawah dengan garis isoline.

Tanda Bahaya: Ketika Hasil Optimasi Mencurigakan

Dashboard tanda bahaya untuk hasil optimasi Indikator peringatan yang menandakan potensi overfitting dalam hasil optimasi

Delapan tanda bahwa optimasi menemukan overfitting daripada pola nyata:

1. Sensitivity Ratio > 2 untuk Parameter Utama

Jika PnL turun lebih dari 20% dengan pergeseran parameter 10% — optimum tersebut rapuh.

2. Lebar Plateau < 10% dari Rentang Pencarian

Jika wilayah "baik" menempati kurang dari 10% rentang yang dieksplorasi — optimizer kemungkinan besar menemukan artefak.

3. Trial Terbaik Menghasilkan PnL 2-3x di Atas Median

Jika trial terbaik adalah outlier terhadap sisanya daripada "puncak bukit" — itu bukan plateau.

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"WARNING: Top trials are {outlier_ratio:.1f}x above median — possible overfitting")

4. Jumlah Transaksi Rendah (< 50) dengan PnL Tinggi

Sampel kecil + PnL tinggi = variansi tinggi dalam estimasi. Analisis plateau pada 40 transaksi sendiri sudah tidak dapat diandalkan. Untuk strategi seperti itu, Monte Carlo bootstrap sangat penting.

5. Satu Kombinasi Parameter "Ajaib"

Jika contour plot menunjukkan satu titik terang di tengah bidang abu-abu — ini bukan strategi, ini adalah kombinasi yang disesuaikan dengan data.

6. Terlalu Banyak Parameter

Untuk 12 parameter dengan 10 nilai masing-masing, ruang pencarian mengandung 101210^{12} kombinasi. Optuna mengeksplorasi ~500. Probabilitas menemukan artefak "baik" di ruang seperti itu tinggi. Semakin banyak parameter, semakin ketat analisis plateau harus dilakukan.

7. PnL Turun Tajam di Luar Sampel

Jika PnL in-sample adalah +87% dan walk-forward menunjukkan +12% — optimasi menyesuaikan parameter dengan periode pelatihan. Lebih lanjut tentang ini dalam artikel Optimasi Walk-Forward.

8. Parameter "Tertempel" pada Batas Rentang

Jika nilai optimal bertepatan dengan batas grid pencarian — optimum mungkin berada di luar rentang. Perluas rentang dan jalankan kembali optimasi.

Laporan Analisis Plateau Otomatis

Menyatukan semuanya dalam satu laporan yang dihasilkan setelah setiap optimasi:

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:
    """
    Generate a complete plateau analysis report.
    """
    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))

Contoh output:

{
  "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
    }
  }
}

Hubungan dengan Validasi Walk-Forward

Validasi walk-forward yang melengkapi analisis plateau Robustness parametrik (analisis plateau) dan robustness temporal (walk-forward) sebagai dua sistem validasi yang saling melengkapi

Analisis plateau dan validasi walk-forward (WFO) adalah metode yang saling melengkapi:

  • Analisis plateau menjawab pertanyaan: "Seberapa stabil optimum terhadap pergeseran parameter kecil?" Ini adalah pemeriksaan robustness parametrik.
  • Walk-forward menjawab pertanyaan: "Apakah parameter bekerja pada data yang belum dilihat optimizer?" Ini adalah pemeriksaan robustness temporal.

Sebuah strategi dapat lulus analisis plateau (plateau lebar) tetapi gagal walk-forward (rezim pasar berubah). Dan sebaliknya — dapat lulus walk-forward pada parameter tetap tetapi memiliki optimum yang rapuh.

Rekomendasi: selalu gunakan kedua metode. Jika sebuah strategi lulus analisis plateau (R>0.1R > 0.1) dan walk-forward (PnLOOS>50%×PnLIS\text{PnL}_{OOS} > 50\% \times \text{PnL}_{IS}) — ini adalah sinyal kuat robustness. Detail lebih lanjut dalam artikel Optimasi Walk-Forward.

Untuk menilai interval kepercayaan PnL di setiap tahap, terapkan Monte Carlo bootstrap. Dan untuk membandingkan strategi dengan waktu aktif yang berbeda secara benar, gunakan metrik PnL per waktu aktif.

Rekomendasi

Sebelum Optimasi

  1. Batasi jumlah parameter. Semakin sedikit parameter — semakin andal plateau. 5-7 parameter adalah maksimum yang masuk akal. 12 sudah memerlukan kehati-hatian yang lebih tinggi.

  2. Tetapkan rentang yang bermakna. Jangan tetapkan htf_entry_sell dari 0.001 hingga 1.0 jika rentang realistisnya adalah 0.005 hingga 0.05. Rentang yang tidak perlu lebar menciptakan ilusi plateau.

  3. Gunakan trial yang cukup. Untuk 12 parameter, minimal 300-500 trial. Untuk analisis plateau yang andal — 1000+.

Selama Optimasi

  1. Pantau konvergensi. Jika Optuna terus menemukan solusi yang jauh lebih baik setelah 400 trial — proses belum konvergen, dan analisis plateau akan tidak dapat diandalkan.

  2. Gunakan pruning dengan hati-hati. Pruning agresif (MedianPruner) dapat memotong trial yang tampak buruk pada langkah awal tetapi penting untuk membangun gambaran lanskap yang lengkap.

Setelah Optimasi

  1. Hasilkan laporan plateau secara otomatis. Integrasikan generate_plateau_report() ke dalam pipeline optimasi. Jangan mengandalkan penilaian visual — gunakan angka.

  2. Periksa 5 parameter teratas. Jika fANOVA menunjukkan bahwa 3 parameter menjelaskan 80% variansi — 9 sisanya dapat diperiksa kurang menyeluruh.

  3. Bandingkan dengan strategi baseline. Jika strategi dengan parameter default (tanpa optimasi) menunjukkan +30%, dan yang dioptimalkan +55% — perbedaannya hanya 25 pp, dan plateau kemungkinan lebar. Jika default menunjukkan 0%, dan yang dioptimalkan +300% — semua profitabilitas bergantung pada penyesuaian parameter yang tepat.

  4. Pemeriksaan akhir — walk-forward. Analisis plateau adalah kondisi yang diperlukan tetapi tidak cukup untuk robustness. Selalu validasi out-of-sample.

Kesimpulan

Optimasi parameter adalah alat yang kuat, tetapi tanpa analisis plateau ini adalah permainan roulette. Anda tidak tahu apakah Anda telah menemukan pola yang stabil atau menyesuaikan model terhadap noise.

Tiga aturan analisis plateau:

  1. Hitung robustness score. Produk dari lebar plateau tertimbang memberikan satu angka yang merangkum robustness semua parameter. R>0.1R > 0.1 — lampu hijau.

  2. Sensitivity ratio < 1 untuk parameter utama. Jika pergeseran parameter 10% menyebabkan penurunan PnL kurang dari 10% — parameter tersebut robust. Jika lebih — berhati-hatilah.

  3. Visualisasikan contour plot. Tidak ada metrik yang dapat menggantikan pemahaman tentang bentuk lanskap. Gunung meja datar — bagus. Jarum tajam — buruk.

Analisis plateau membutuhkan 5 menit setelah optimasi dan dapat menghemat berminggu-minggu live trading yang tidak menguntungkan. Ini adalah langkah wajib antara study.optimize() dan peluncuran bot.


Tautan Berguna

  1. Dokumentasi Optuna — Visualisasi
  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)

Kutipan

@article{soloviov2026plateauanalysis,
  author = {Soloviov, Eugen},
  title = {Plateau Analysis: How to Distinguish a Robust Optimum from Overfitting},
  year = {2026},
  url = {https://marketmaker.cc/id/blog/post/plateau-analysis-overfitting},
  version = {0.1.0},
  description = {Mengapa menemukan parameter strategi terbaik hanyalah separuh dari pekerjaan. Cara membedakan plateau yang stabil dari puncak yang rapuh secara visual dan kuantitatif, serta mengapa contour plot Optuna adalah langkah wajib sebelum meluncurkan strategi yang dioptimalkan ke produksi.}
}
Penafian: Informasi yang disediakan dalam artikel ini hanya untuk tujuan edukasi dan informasi serta tidak merupakan nasihat keuangan, investasi, atau trading. Trading mata uang kripto mengandung risiko kerugian yang signifikan.

Penulis

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

Selangkah Lebih Maju dari Pasar

Berlangganan newsletter kami untuk wawasan AI trading eksklusif, analisis pasar, dan pembaruan platform.

Kami menghormati privasi Anda. Berhenti berlangganan kapan saja.