← Kembali ke artikel
March 12, 2026
Bacaan 5 minit

Analisis Dataran: Cara Membezakan Optimum Kukuh daripada Overfitting

Analisis Dataran: Cara Membezakan Optimum Kukuh daripada Overfitting
#algotrading
#backtest
#pengoptimuman
#overfitting
#analisis dataran
#kestabilan parameter

Artikel ke-6 dalam siri "Backtest Tanpa Ilusi"

Anda menjalankan study.optimize(), Optuna menemui set parameter dengan PnL +87%. Anda teruja dan menyediakan strategi untuk pengeluaran. Dua minggu dagangan langsung kemudian, PnL hampir sifar. Apa yang berlaku?

Pengoptimum menemui hujung jarum dalam ruang parameter. Parameter tersebut disesuaikan dengan sempurna kepada urutan dagangan sejarah — tetapi penyimpangan sekecil mana pun dalam keadaan pasaran akan memusnahkan keseluruhan binaan itu. Ini adalah overfitting klasik, dan ia boleh dikesan sebelum pelancaran.

Dalam artikel sebelumnya kami membandingkan coordinate descent dengan pengoptimuman Bayesian dan menunjukkan mengapa Optuna menemui optimum dengan lebih cekap. Hari ini — langkah seterusnya: cara memastikan optimum yang ditemui adalah kukuh, bukan hasil penyesuaian kepada bunyi (noise).

Mengapa Mencari Parameter "Terbaik" Hanyalah Separuh Kerja

Mencari melalui ruang parameter pelbagai dimensi Pengoptimum menavigasi landskap parameter pelbagai dimensi yang luas untuk mencari optimum sebenar

Pengoptimuman parameter strategi adalah pencarian maksimum dalam ruang pelbagai dimensi. Masalahnya ialah maksimum datang dalam dua jenis:

  1. Dataran — kawasan rata yang luas di mana PnL secara konsisten tinggi di seluruh variasi parameter. Walaupun keadaan pasaran mengalih parameter efektif sebanyak 10-20%, strategi akan terus menguntungkan.

  2. Puncak tajam — kemuncak sempit di mana PnL tinggi hanya pada nilai parameter yang tepat. Anjakan satu langkah sahaja meruntuhkan keberuntungan. Ini hampir pasti overfitting: pengoptimum menemui artifak data sejarah, bukan corak stabil.

Metafora pendakian gunung: dataran ialah tanah tinggi gunung di mana anda boleh berjalan dengan selamat. Puncak tajam ialah hujung jarum di mana anda hanya boleh mengimbangi diri.

Puncak Tajam vs Dataran Rata — Intuisi Visual

Perbandingan puncak tajam berbanding dataran rata Kiri: dataran kukuh (gunung meja lebar dengan cerun lembut). Kanan: puncak tajam rapuh (hujung jarum dikelilingi lembah dalam)

Bayangkan peta kontur di mana paksi-paksi ialah dua parameter strategi dan warna mewakili PnL. Dua corak mudah dibezakan secara visual:

Dataran (optimum kukuh):

  • Kawasan luas warna yang sama
  • Peralihan licin antara tahap PnL
  • Kontur berjauhan
  • Anjakan dari optimum sebanyak +/-20% mengubah PnL tidak lebih daripada 10%

Bayangkan heatmap: di tengah — segiempat tepat kuning cerah kira-kira satu pertiga saiz keseluruhan peta. Warna beransur-ansur bertukar kepada oren, kemudian merah ke arah pinggir. Optimum bukan satu titik, tetapi satu kawasan.

Puncak tajam (overfitting):

  • Titik cerah sempit dikelilingi warna sejuk
  • Peralihan mendadak: runtuhan betul-betul bersebelahan optimum
  • Kontur dimampatkan ke dalam gelang ketat
  • Anjakan sebanyak +/-5% menjatuhkan PnL 50% atau lebih

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

Analisis Kepekaan Parameter

Plot kepingan kepekaan parameter Plot kepingan menunjukkan bagaimana PnL bergantung kepada nilai parameter individu — jalur lebar menandakan keteguhan, kelompok sempit menandakan kerapuhan

Analisis Satu Dimensi: PnL vs Satu Parameter

Pendekatan paling mudah — tetapkan semua parameter kecuali satu dan lihat bagaimana PnL bergantung kepada 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()

Apa yang perlu dicari pada plot kepingan:

  • Parameter kukuh: awan titik membentuk jalur mendatar lebar berhampiran optimum. Percubaan terbaik tersebar di seluruh julat nilai parameter yang luas.
  • Parameter rapuh: percubaan terbaik tertumpu dalam julat sempit. Anjakan parameter satu atau dua langkah — dan keberuntungan runtuh.

Analisis Dua Dimensi: Plot Kontur (Heatmap)

Plot kontur menunjukkan interaksi dua parameter secara serentak. Ini adalah alat utama untuk analisis dataran, kerana parameter jarang bertindak secara bebas — ambang masuk dan keluar, jangka masa, dan saiz posisi saling berkaitan.

from optuna.visualization import plot_contour

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

Plot kontur untuk pasangan parameter kukuh kelihatan seperti peta topografi dataran berbukit: kontur lebar licin, kawasan luas warna yang sama. Plot kontur untuk pasangan rapuh — seperti peta kon gunung berapi: gelang konsentrik ketat di sekeliling satu titik.

Untuk strategi dengan 12 parameter pemisahan, ini memberikan (122)=66\binom{12}{2} = 66 plot kontur berpasangan. Anda tidak perlu mempelajari semuanya — mulakan dengan parameter yang dinilai paling penting oleh Optuna.

Analisis Pelbagai Dimensi: Penilaian Kepentingan Parameter

Optuna boleh menganggar sumbangan setiap parameter kepada fungsi objektif:

from optuna.visualization import plot_param_importances

fig = plot_param_importances(study)
fig.show()

Carta kepentingan parameter ialah histogram mendatar. Parameter disusun mengikut sumbangan mereka kepada varians PnL dalam tertib menurun. 3-4 parameter teratas biasanya menjelaskan 70-80% varians.

Peraturan: jika parameter menjelaskan kurang daripada 2% varians PnL, nilainya hampir tidak relevan kepada keputusan — ia kukuh secara definisi. Fokuskan analisis dataran kepada 5 parameter terpenting teratas.

Alat Visualisasi Optuna

Plot kontur Optuna dan visualisasi kepentingan parameter Heatmap kontur menunjukkan landskap interaksi parameter bersama-sama penilaian kepentingan

plot_slice — Kepingan 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 plot serakan. Setiap subplot menunjukkan nilai fungsi objektif (PnL, paksi-Y) berbanding nilai parameter tunggal (paksi-X). Titik adalah percubaan individu. Untuk parameter kukuh, titik terbaik (PnL tertinggi) diedarkan merentasi julat X yang luas. Untuk parameter rapuh — berkelompok dalam lajur 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 plot kontur ialah heatmap dengan dua parameter pada paksi. Warna menyandikan purata PnL dalam kawasan tertentu ruang parameter. Kuning/hijau — PnL tinggi, biru/ungu — rendah. Kontur menghubungkan titik dengan PnL yang sama.

plot_param_importances — Sumbangan Parameter

from optuna.visualization import plot_param_importances

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

fANOVA (functional ANOVA) menguraikan varians fungsi objektif merentasi parameter dan interaksinya. Ini lebih berkuasa daripada korelasi mudah kerana ia mengambil kira kesan bukan linear.

Metrik Dataran Kuantitatif

Visualisasi metrik keteguhan kuantitatif Nisbah kepekaan, lebar dataran, dan skor keteguhan — tiga metrik yang memformalkan kualiti dataran

Penilaian visual adalah subjektif. Kita memerlukan angka. Berikut adalah tiga metrik yang memformalkan konsep "dataran."

Nisbah Kepekaan

Nisbah perubahan PnL kepada 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} ialah penurunan PnL apabila parameter pip_i menyimpang dari optimum sebanyak Δpi\Delta p_i.

Tafsiran:

  • Si<0.5S_i < 0.5 — parameter kukuh: anjakan 10% menyebabkan penurunan PnL kurang daripada 5%
  • 0.5Si<2.00.5 \leq S_i < 2.0 — kepekaan sederhana
  • Si2.0S_i \geq 2.0 — parameter rapuh: anjakan 10% meruntuhkan PnL sebanyak 20%+

Lebar Dataran

Lebar kawasan parameter di mana PnL kekal dalam X%X\% daripada 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 dataran 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 ialah julat carian penuh parameter.

Tafsiran:

  • Wirel(10%)>0.3W_i^{rel}(10\%) > 0.3 — dataran meliputi lebih daripada 30% julat pada ambang 10%. Parameter kukuh.
  • Wirel(10%)<0.05W_i^{rel}(10\%) < 0.05 — dataran lebih sempit daripada 5% julat. Tanda amaran.

Skor Keteguhan

Metrik gabungan merentasi semua parameter:

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

di mana wiw_i ialah kepentingan ternormal parameter ii daripada fANOVA (wi=1\sum w_i = 1).

Hasil darab lebar berwajaran ialah metrik yang ketat: jika bahkan satu parameter penting mempunyai dataran sempit, RR akan rendah. Parameter tidak penting (dengan wiw_i kecil) hampir tidak memberi kesan.

Tafsiran:

  • R>0.1R > 0.1 — strategi kukuh
  • 0.01<R0.10.01 < R \leq 0.1 — pengesahan tambahan diperlukan (walk-forward)
  • R0.01R \leq 0.01 — overfitting sangat berkemungkinan

Kod Python untuk Pengesanan Dataran Automatik

Saluran pengesanan dataran automatik Sistem automatik mengimbas landskap parameter untuk mengenal pasti dataran kukuh dan puncak 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 Praktikal dengan Strategi Pemisahan

Tiga strategi dibandingkan mengikut profil keteguhan Membandingkan Strategi A (dataran lebar, kukuh), Strategi B (sederhana), dan Strategi C (puncak tajam, overfitted)

Mari kita periksa tiga strategi dengan 12 parameter pemisahan. Setiap strategi menjalani pengoptimuman Optuna dengan 500 percubaan.

Strategi A (~55% PnL, ~500 dagangan, ~15% masa)

Parameter Strategi A membentuk dataran lebar. Ambil parameter utama htf_entry_sell:

  • Nilai optimum: 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 (paksi-X — nilai htf_entry_sell, paksi-Y — PnL), anda akan melihat parabola lembut dengan bahagian atas rata. Julat 0.010-0.030 ialah dataran, di mana PnL kekal dalam +/-25% daripada optimum.

Nisbah kepekaan: S=0.110.25=0.44S = \frac{0.11}{0.25} = 0.44 — kukuh.

Lebar dataran 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 dagangan, ~5% masa)

Strategi B dioptimumkan pada bilangan dagangan yang kecil. Parameter htf_entry_sell:

  • Nilai optimum: 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 — lengkung asimetri dan curam. Dataran wujud hanya dalam julat sempit 0.015-0.020. Di sebelah kanan optimum — tebing.

Nisbah kepekaan: S=0.640.39=1.64S = \frac{0.64}{0.39} = 1.64 — kepekaan sederhana, tetapi dengan 40 dagangan ini adalah tanda amaran. Sampel kecil + dataran sempit = kebarangkalian tinggi overfitting.

Lebar dataran 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 dagangan, ~45% masa)

Strategi C menunjukkan PnL yang menakjubkan, tetapi analisis dataran mendedahkan masalah:

  • Nilai optimum 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 mendadak ke semua arah. Plot kontur akan menunjukkan titik cerah yang segera dikelilingi warna sejuk.

Nisbah kepekaan: S=0.530.14=3.79S = \frac{0.53}{0.14} = 3.79rapuh. Walaupun dengan 400 dagangan, strategi bergantung secara berlebihan kepada nilai tepat satu parameter.

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

Jadual Ringkasan

Strategi PnL Dagangan Kepekaan Lebar dataran Skor keteguhan Keputusan
Strategi A +55% ~500 0.44 35% 0.148 Kukuh
Strategi B +25% ~40 1.64 10% 0.032 Semak (sampel kecil)
Strategi C +300% ~400 3.79 5% 0.008 Overfitting

Paradoks: Strategi C dengan PnL +300% mempunyai skor keteguhan terburuk. Strategi A dengan "+55%" yang "sederhana" adalah yang paling kukuh. Ini adalah keputusan analisis dataran yang tipikal: angka yang mengesankan sering menyembunyikan kerapuhan.

Selang keyakinan untuk setiap strategi boleh disahkan secara tambahan melalui bootstrap Monte Carlo — ia akan menunjukkan sebaran PnL semasa pensampelan semula dagangan.

Visualisasi 3D dan Heatmap

Permukaan landskap parameter 3D dengan unjuran kontur Plot permukaan 3D PnL merentasi dua parameter dengan garis kontur diunjurkan ke atas satah lantai

Untuk pasangan parameter terpenting, adalah berguna untuk membina permukaan 3D dan heatmap. Ini memberikan pemahaman intuitif tentang bentuk landskap.

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 kukuh menyerupai gunung meja — bahagian atas rata dengan cerun lembut. Untuk strategi rapuh — puncak tajam, seperti Matterhorn. Heatmap melengkapi paparan 3D, menunjukkan maklumat yang sama dalam unjuran atas-bawah dengan kontur.

Tanda Amaran: Apabila Hasil Pengoptimuman Mencurigakan

Papan pemuka tanda amaran untuk hasil pengoptimuman Penunjuk amaran yang menandakan potensi overfitting dalam hasil pengoptimuman

Lapan tanda bahawa pengoptimuman menemui overfitting dan bukan corak sebenar:

1. Nisbah Kepekaan > 2 untuk Parameter Utama

Jika PnL turun lebih daripada 20% dengan anjakan parameter 10% — optimum adalah rapuh.

2. Lebar Dataran < 10% daripada Julat Carian

Jika kawasan "baik" menduduki kurang daripada 10% julat yang diterokai — pengoptimum kemungkinan besar menemui artifak.

3. 3 Percubaan Teratas Menghasilkan PnL 2-3x di Atas Median

Jika percubaan terbaik adalah nilai terpencil berbanding yang lain dan bukan "puncak bukit" — ia bukan dataran.

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. Bilangan Dagangan Rendah (< 50) dengan PnL Tinggi

Sampel kecil + PnL tinggi = varians tinggi dalam anggaran. Analisis dataran pada 40 dagangan tidak boleh dipercayai secara intrinsiknya. Untuk strategi sedemikian, bootstrap Monte Carlo adalah kritikal.

5. Satu Kombinasi Parameter "Ajaib"

Jika plot kontur menunjukkan satu titik cerah di tengah medan kelabu — ini bukan strategi, ia adalah kombinasi yang disesuaikan kepada data.

6. Terlalu Banyak Parameter

Untuk 12 parameter dengan 10 nilai setiap satu, ruang carian mengandungi 101210^{12} kombinasi. Optuna meneroka ~500. Kebarangkalian menemui artifak "baik" dalam ruang sedemikian adalah tinggi. Semakin banyak parameter, semakin ketat analisis dataran yang perlu dilakukan.

7. PnL Turun Mendadak di Luar Sampel

Jika PnL dalam sampel ialah +87% dan walk-forward menunjukkan +12% — pengoptimuman menyesuaikan parameter kepada tempoh latihan. Lebih lanjut mengenai ini dalam artikel pengoptimuman Walk-Forward.

8. Parameter "Terjepit" ke Sempadan Julat

Jika nilai optimum bertepatan dengan sempadan grid carian — optimum mungkin terletak di luar julat. Perluaskan julat dan jalankan semula pengoptimuman.

Laporan Analisis Dataran Automatik

Menyatukan semuanya ke dalam satu laporan yang dijana selepas setiap pengoptimuman:

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 Pengesahan Walk-Forward

Pengesahan walk-forward melengkapi analisis dataran Keteguhan parametrik (analisis dataran) dan keteguhan temporal (walk-forward) sebagai dua sistem pengesahan yang saling melengkapi

Analisis dataran dan pengesahan walk-forward (WFO) adalah kaedah yang saling melengkapi:

  • Analisis dataran menjawab soalan: "Seberapa stabil optimum terhadap anjakan parameter kecil?" Ini adalah semakan keteguhan parametrik.
  • Walk-forward menjawab soalan: "Adakah parameter berfungsi pada data yang belum dilihat oleh pengoptimum?" Ini adalah semakan keteguhan temporal.

Sebuah strategi boleh lulus analisis dataran (dataran lebar) tetapi gagal walk-forward (rejim pasaran berubah). Dan sebaliknya — boleh lulus walk-forward pada parameter tetap tetapi mempunyai optimum yang rapuh.

Cadangan: sentiasa gunakan kedua-dua kaedah. Jika strategi lulus analisis dataran (R>0.1R > 0.1) dan walk-forward (PnLOOS>50%×PnLIS\text{PnL}_{OOS} > 50\% \times \text{PnL}_{IS}) — ini adalah isyarat keteguhan yang kuat. Butiran lanjut dalam artikel pengoptimuman Walk-Forward.

Untuk menilai selang keyakinan PnL pada setiap peringkat, gunakan bootstrap Monte Carlo. Dan untuk membandingkan strategi dengan masa aktif yang berbeza dengan betul, gunakan metrik PnL per masa aktif.

Cadangan

Sebelum Pengoptimuman

  1. Hadkan bilangan parameter. Semakin sedikit parameter — semakin boleh dipercayai dataran. 5-7 parameter adalah maksimum yang munasabah. 12 sudah memerlukan kehati-hatian yang lebih tinggi.

  2. Tetapkan julat yang bermakna. Jangan tetapkan htf_entry_sell dari 0.001 hingga 1.0 jika julat realistik ialah 0.005 hingga 0.05. Julat yang terlalu lebar mencipta ilusi dataran.

  3. Gunakan percubaan yang mencukupi. Untuk 12 parameter, minimum 300-500 percubaan. Untuk analisis dataran yang boleh dipercayai — 1000+.

Semasa Pengoptimuman

  1. Perhatikan penumpuan. Jika Optuna terus menemui penyelesaian yang jauh lebih baik selepas 400 percubaan — proses belum menumpu, dan analisis dataran tidak akan boleh dipercayai.

  2. Gunakan pemangkasan dengan berhati-hati. Pemangkasan agresif (MedianPruner) boleh memotong percubaan yang kelihatan buruk pada langkah awal tetapi penting untuk membina gambaran landskap yang lengkap.

Selepas Pengoptimuman

  1. Jana laporan dataran secara automatik. Integrasikan generate_plateau_report() ke dalam saluran pengoptimuman. Jangan bergantung pada penilaian visual — gunakan angka.

  2. Semak 5 parameter teratas. Jika fANOVA menunjukkan bahawa 3 parameter menjelaskan 80% varians — baki 9 boleh diperiksa dengan kurang teliti.

  3. Bandingkan dengan strategi garis dasar. Jika strategi dengan parameter lalai (tanpa pengoptimuman) menunjukkan +30%, dan yang dioptimumkan +55% — perbezaannya hanya 25 pp, dan dataran kemungkinan besar lebar. Jika lalai menunjukkan 0%, dan yang dioptimumkan +300% — semua keberuntungan bergantung pada penyesuaian parameter yang tepat.

  4. Semakan akhir — walk-forward. Analisis dataran adalah syarat perlu tetapi tidak mencukupi untuk keteguhan. Sentiasa sahkan di luar sampel.

Kesimpulan

Pengoptimuman parameter adalah alat yang berkuasa, tetapi tanpa analisis dataran ia adalah permainan roulette. Anda tidak tahu sama ada anda telah menemui corak stabil atau menyesuaikan model kepada bunyi (noise).

Tiga peraturan analisis dataran:

  1. Kira skor keteguhan. Hasil darab lebar dataran berwajaran memberikan satu angka yang merumuskan keteguhan semua parameter. R>0.1R > 0.1 — lampu hijau.

  2. Nisbah kepekaan < 1 untuk parameter utama. Jika anjakan parameter 10% menyebabkan penurunan PnL kurang daripada 10% — parameter adalah kukuh. Jika lebih — berhati-hati.

  3. Visualkan plot kontur. Tiada metrik boleh menggantikan pemahaman tentang bentuk landskap. Gunung meja rata — baik. Jarum tajam — buruk.

Analisis dataran mengambil masa 5 minit selepas pengoptimuman dan boleh menyelamatkan minggu dagangan langsung yang tidak menguntungkan. Ia adalah langkah wajib antara study.optimize() dan melancarkan bot.


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

Petikan

@article{soloviov2026plateauanalysis,
  author = {Soloviov, Eugen},
  title = {Plateau Analysis: How to Distinguish a Robust Optimum from Overfitting},
  year = {2026},
  url = {https://marketmaker.cc/ms/blog/post/plateau-analysis-overfitting},
  version = {0.1.0},
  description = {Mengapa mencari parameter strategi terbaik hanyalah separuh daripada kerja. Cara membezakan dataran stabil daripada puncak rapuh secara visual dan kuantitatif, dan mengapa plot kontur Optuna adalah langkah wajib sebelum melancarkan strategi yang telah dioptimumkan ke dalam pengeluaran.}
}
Penafian: Maklumat yang disediakan dalam artikel ini adalah untuk tujuan pendidikan dan maklumat sahaja dan bukan merupakan nasihat kewangan, pelaburan, atau dagangan. Dagangan mata wang kripto melibatkan risiko kerugian yang ketara.

Pengarang

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

Kekal Mendahului Pasaran

Langgan surat berita kami untuk pandangan dagangan AI eksklusif, analisis pasaran, dan kemas kini platform.

Kami menghormati privasi anda. Berhenti melanggan pada bila-bila masa.