Analisis Dataran: Cara Membezakan Optimum Kukuh daripada Overfitting
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
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:
-
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.
-
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
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 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 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
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
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:
di mana ialah penurunan PnL apabila parameter menyimpang dari optimum sebanyak .
Tafsiran:
- — parameter kukuh: anjakan 10% menyebabkan penurunan PnL kurang daripada 5%
- — kepekaan sederhana
- — parameter rapuh: anjakan 10% meruntuhkan PnL sebanyak 20%+
Lebar Dataran
Lebar kawasan parameter di mana PnL kekal dalam daripada optimum:
Lebar dataran relatif:
di mana penyebut ialah julat carian penuh parameter.
Tafsiran:
- — dataran meliputi lebih daripada 30% julat pada ambang 10%. Parameter kukuh.
- — dataran lebih sempit daripada 5% julat. Tanda amaran.
Skor Keteguhan
Metrik gabungan merentasi semua parameter:
di mana ialah kepentingan ternormal parameter daripada fANOVA ().
Hasil darab lebar berwajaran ialah metrik yang ketat: jika bahkan satu parameter penting mempunyai dataran sempit, akan rendah. Parameter tidak penting (dengan kecil) hampir tidak memberi kesan.
Tafsiran:
- — strategi kukuh
- — pengesahan tambahan diperlukan (walk-forward)
- — overfitting sangat berkemungkinan
Kod Python untuk 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
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: — kukuh.
Lebar dataran pada ambang 10%: dari 0.013 hingga 0.027, .
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: — 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, .
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: — rapuh. Walaupun dengan 400 dagangan, strategi bergantung secara berlebihan kepada nilai tepat satu parameter.
Lebar dataran pada ambang 10%: dari 0.021 hingga 0.023, .
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
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
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 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
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 () dan walk-forward () — 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
-
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.
-
Tetapkan julat yang bermakna. Jangan tetapkan
htf_entry_selldari 0.001 hingga 1.0 jika julat realistik ialah 0.005 hingga 0.05. Julat yang terlalu lebar mencipta ilusi dataran. -
Gunakan percubaan yang mencukupi. Untuk 12 parameter, minimum 300-500 percubaan. Untuk analisis dataran yang boleh dipercayai — 1000+.
Semasa Pengoptimuman
-
Perhatikan penumpuan. Jika Optuna terus menemui penyelesaian yang jauh lebih baik selepas 400 percubaan — proses belum menumpu, dan analisis dataran tidak akan boleh dipercayai.
-
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
-
Jana laporan dataran secara automatik. Integrasikan
generate_plateau_report()ke dalam saluran pengoptimuman. Jangan bergantung pada penilaian visual — gunakan angka. -
Semak 5 parameter teratas. Jika fANOVA menunjukkan bahawa 3 parameter menjelaskan 80% varians — baki 9 boleh diperiksa dengan kurang teliti.
-
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.
-
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:
-
Kira skor keteguhan. Hasil darab lebar dataran berwajaran memberikan satu angka yang merumuskan keteguhan semua parameter. — lampu hijau.
-
Nisbah kepekaan < 1 untuk parameter utama. Jika anjakan parameter 10% menyebabkan penurunan PnL kurang daripada 10% — parameter adalah kukuh. Jika lebih — berhati-hati.
-
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
- Dokumentasi Optuna — Visualisasi
- Hutter, F., Hoos, H., Leyton-Brown, K. — An Efficient Approach for Assessing Hyperparameter Importance (fANOVA, 2014)
- Pardo, R. — The Evaluation and Optimization of Trading Strategies
- Marcos Lopez de Prado — Advances in Financial Machine Learning, Chapter 11: Dangers of Backtesting
- Bailey, D.H. et al. — The Probability of Backtest Overfitting (2015)
- Optuna — optuna.visualization.plot_contour
- Optuna — optuna.importance.FanovaImportanceEvaluator
- 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.}
}
Pengarang
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.