← Макалаларга кайтуу
March 12, 2026
5 мүн окуу

Плато анализи: туруктуу оптимумду overfitting-тен кантип айырмалоо керек

Плато анализи: туруктуу оптимумду overfitting-тен кантип айырмалоо керек
#algotrading
#backtest
#optimization
#overfitting
#plateau analysis
#parameter stability
🎯
Part 4 of 9 · Collection
Backtesting Without Fooling Yourself

"Иллюзиясыз бэктесттер" сериясынын 6-макаласы

📄 Бул макала изилдөө макаласына айланды. Метрикалар формалдаштырылган жана кайра жаратылуучу симуляциялар менен стресс-текшерилген, Probability of Backtest Overfitting жана Deflated Sharpe Ratio менен салыштырылган. Макаланы онлайн окуңуз (интерактивдүү версия + PDF) plateau.marketmaker.cc сайтында, код жана маалыматтар github.com/suenot/plateau-robustness даректе.

Сиз study.optimize() иштеттиңиз, Optuna PnL +87% болгон параметрлер топтомун тапты. Сиз кубанып, стратегияны продакшенге даярдап жатасыз. Эки жумалык live-соода өткөндөн кийин, PnL нөлгө жакын. Эмне болду?

Оптимизатор параметрлер мейкиндигинен ийненин учун тапты. Параметрлер соода-сатыктын тарыхый тизмегине өтө так дал келет — бирок рыноктук шарттардын эң кичине четтөөсү бүт түзүлүштү талкалайт. Бул классикалык overfitting, жана аны иштетүүдөн мурун аныктоого болмок.

Мурунку макалада биз coordinate descent-ти байестик оптимизация менен салыштырдык жана Optuna оптимумду эмне үчүн натыйжалуу табарын көрсөттүк. Бүгүн — кийинки кадам: табылган оптимумдун чуулга ыңгайланган натыйжа эмес, туруктуу экенин кантип текшерүү керек.

"Эң мыкты" параметрлерди табуу эмне үчүн иштин жарымы гана

Көп өлчөмдүү параметрлер мейкиндигинде издөө Оптимизатор чыныгы оптимумду издеп, кенен көп өлчөмдүү параметрлер ландшафтын кыдырат

Стратегия параметрлерин оптималдаштыруу — көп өлчөмдүү мейкиндикте максимумду издөө. Көйгөй — максимумдар эки түрдө болот:

  1. Плато — параметрлер өзгөрсө да PnL туруктуу жогору бойдон калган кенен түз аймак. Рыноктук шарттар эффективдүү параметрлерди 10-20%-га жылдырса да, стратегия пайда алуучулугун улантат.

  2. Курч чоку — PnL параметрдин так маанисинде гана жогору болгон тар чоку. Бир кадамга жылдыруу пайдалуулукту кыйратат. Бул дээрлик анык overfitting: оптимизатор туруктуу мыйзам ченемди эмес, тарыхый маалыматтардын артефактысын тапты.

Альпинисттик метафора: плато — коопсуз басууга болгон тоолуу үстүрт. Курч чоку — тепе-теңдикти гана сактай турган ийненин учу.

Курч чоку vs түз плато — визуалдык интуиция

Курч чоку менен түз платонун салыштыруусу Солдо: туруктуу плато (жумшак кыялары бар кенен үстөл тоо). Оңдо: начар курч чоку (терең өрөөндөр менен курчалган ийненин учу)

Октор эки стратегия параметри болгон, ал эми түс PnL-ди билдирген контурлук картаны элестетиңиз. Эки үлгүнү визуалдык жактан оңой айырмалоого болот:

Плато (туруктуу оптимум):

  • Бир түстүн кенен аймактары
  • PnL деңгээлдеринин ортосундагы жумшак өтүүлөр
  • Бири-биринен алыс жайгашкан изосызыктар
  • Оптимумдан +/-20%-га четтөө PnL-ди 10%-дан ашык өзгөртпөйт

Жылуулук картасын элестетиңиз: борборунда — бүт картанын болжол менен үчтөн бир бөлүгүндөй жаркыраган сары тик бурчтук. Түс четтерге карай акырындык менен кызгылт сарыга, андан кийин кызылга өтөт. Оптимум — чекит эмес, аймак.

Курч чоку (overfitting):

  • Муздак түстөр менен курчалган тар жаркыраган так
  • Кескин өтүүлөр: оптимумдун жанында түздөн-түз кулоо
  • Тар шакектерге кысылган изосызыктар
  • +/-5%-га четтөө PnL-ди 50% же андан көбүрөөк төмөндөтөт

Ошол эле жылуулук картасын элестетиңиз, бирок борборунда — көк жана кызгылт күлгүн түс менен дароо курчалган кичинекей сары чекит. Бир гана "туура" параметрлер айкалышы.

Параметрлердин сезгичтик анализи

Параметр сезгичтигинин slice plot-тору PnL жеке параметр маанилерине кантип көз каранды экенин көрсөткөн slice plot-тор — кенен тилкелер туруктуулукту, тар кластерлер начарлыкты билдирет

Бир өлчөмдүү анализ: PnL жана бир параметр

Эң жөнөкөй ыкма — бир параметрден башканын баарын бекитип, PnL анын маанисине кантип көз каранды экенин көрүү. Optuna бул үчүн plot_slice сунуштайт:

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

Slice plot-то эмнеге көңүл буруу керек:

  • Туруктуу параметр: чекиттер булуту оптимумдун жанында кенен горизонталдык тилке түзөт. Мыкты сынактар параметр маанилеринин кенен диапазонуна таралган.
  • Начар параметр: мыкты сынактар тар диапазондо топтолгон. Параметрди бир-эки кадамга жылдыруу — жана пайдалуулук кулайт.

Эки өлчөмдүү анализ: контурлук сызмалар (heatmap-тар)

Контурлук сызма эки параметрдин өз ара аракеттенүүсүн бир учурда көрсөтөт. Бул плато анализинин негизги куралы, анткени параметрлер сейрек көз карандысыз аракеттенет — кирүү жана чыгуу чектери, таймфреймдер жана позиция көлөмдөрү өз ара байланышкан.

from optuna.visualization import plot_contour

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

Туруктуу параметрлер жубу үчүн контурлук сызма дөңсөө түздүктүн топографиялык картасына окшош: жумшак кенен изосызыктар, бир түстүн чоң аймактары. Начар жуп үчүн контурлук сызма — жанар тоо конусунун картасына окшош: бир чекиттин тегерегиндеги тар концентрдик шакектер.

12 бөлүү параметри бар стратегия үчүн бул (122)=66\binom{12}{2} = 66 жуптук контурлук сызманы берет. Баарын изилдөөнүн кереги жок — Optuna эң маанилүү деп баалаган параметрлерден баштаңыз.

Көп өлчөмдүү анализ: параметрлердин маанилүүлүк рейтинги

Optuna ар бир параметрдин максат функциясына кошкон салымын баалай алат:

from optuna.visualization import plot_param_importances

fig = plot_param_importances(study)
fig.show()

Параметрлердин маанилүүлүк сызмасы горизонталдык гистограмма. Параметрлер PnL дисперсиясына кошкон салымы боюнча кемүү тартибинде иреттелет. Эң жогорку 3-4 параметр адатта дисперсиянын 70-80%-ын түшүндүрөт.

Эреже: эгер параметр PnL дисперсиясынын 2%-дан азын түшүндүрсө, анын мааниси натыйжа үчүн иш жүзүндө маанисиз — ал аныктама боюнча туруктуу. Плато анализин эң маанилүү 5 параметрге топтоңуз.

Optuna визуализация куралдары

Optuna контурлук сызмалары жана параметр маанилүүлүгүнүн визуализациясы Маанилүүлүк рейтингтери менен катар параметрлердин өз ара аракеттенүү ландшафтын көрсөткөн контурлук heatmap-тар

plot_slice — бир өлчөмдүү кесимдер

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

Натыйжа — чачыроо сызмаларынын тору. Ар бир субсызма максат функциясынын маанисин (PnL, Y огу) бир параметр маанисине (X огу) карата көрсөтөт. Чекиттер — өзүнчө сынактар. Туруктуу параметр үчүн мыкты чекиттер (эң жогорку PnL) X-тин кенен диапазонуна таралган. Начар параметр үчүн — тар мамычада топтолгон.

plot_contour — эки өлчөмдүү контурлар

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

Ар бир контурлук сызма октордо эки параметри бар heatmap. Түс параметрлер мейкиндигинин берилген аймагындагы орточо PnL-ди коддойт. Сары/жашыл — жогорку PnL, көк/кызгылт күлгүн — төмөн. Изосызыктар бирдей PnL-и бар чекиттерди туташтырат.

plot_param_importances — параметрлердин салымы

from optuna.visualization import plot_param_importances

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

fANOVA (functional ANOVA) максат функциясынын дисперсиясын параметрлерге жана алардын өз ара аракеттенүүлөрүнө ажыратат. Бул жөнөкөй корреляциядан күчтүү, анткени сызыктуу эмес таасирлерди эске алат.

Плато сандык метрикалары

Сандык туруктуулук метрикаларынын визуализациясы Sensitivity ratio, плато туурасы жана robustness score — плато сапатын формалдаштырган үч метрика

Визуалдык баалоо субъективдүү. Бизге сандар керек. Бул жерде "плато" түшүнүгүн формалдаштырган үч метрика бар.

Sensitivity ratio

PnL өзгөрүшүнүн параметр өзгөрүшүнө болгон катышы:

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

мында ΔPnL\Delta \text{PnL}pip_i параметри оптимумдан Δpi\Delta p_i-ге четтегенде PnL төмөндөшү.

Түшүндүрмө:

  • Si<0.5S_i < 0.5 — параметр туруктуу: 10%-дык четтөө 5%-дан аз PnL төмөндөшүн жаратат
  • 0.5Si<2.00.5 \leq S_i < 2.0 — орточо сезгичтик
  • Si2.0S_i \geq 2.0 — параметр начар: 10%-дык четтөө PnL-ди 20%+-га кулатат

Плато туурасы

PnL оптимумдун X%X\% чегинде калган параметр аймагынын туурасы:

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}

Салыштырмалуу плато туурасы:

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

мында бөлгүч — параметрдин толук издөө диапазону.

Түшүндүрмө:

  • Wirel(10%)>0.3W_i^{rel}(10\%) > 0.3 — плато 10%-дык чекте диапазондун 30%-дан ашыгын камтыйт. Туруктуу параметр.
  • Wirel(10%)<0.05W_i^{rel}(10\%) < 0.05 — плато диапазондун 5%-инен тар. Эскертүү белгиси.

Robustness score

Бардык параметрлер боюнча биригирилген метрика:

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

мында wiw_i — fANOVA-дан алынган ii параметринин нормалдаштырылган маанилүүлүгү (wi=1\sum w_i = 1).

Салмактандырылган тууралардын көбөйтүндүсү — катуу метрика: бир гана маанилүү параметрдин платосу тар болсо, RR төмөн болот. Маанилүү эмес параметрлер (кичине wiw_i бар) дээрлик таасир этпейт.

Түшүндүрмө:

  • R>0.1R > 0.1 — стратегия туруктуу
  • 0.01<R0.10.01 < R \leq 0.1 — кошумча текшерүү керек (walk-forward)
  • R0.01R \leq 0.01 — overfitting өтө ыктымал

Автоматташтырылган плато аныктоо үчүн Python коду

Автоматташтырылган плато аныктоо pipeline-ы Туруктуу платолорду жана начар чокуларды аныктоо үчүн параметрлер ландшафтын сканерлеген автоматташтырылган система

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"
        ),
    }

Колдонуу

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

Мисал натыйжа:

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%

Бөлүү стратегиялары менен практикалык мисалдар

Туруктуулук профили боюнча салыштырылган үч стратегия A стратегиясын (кенен плато, туруктуу), B стратегиясын (орточо) жана C стратегиясын (курч чоку, overfitted) салыштыруу

12 бөлүү параметри бар үч стратегияны карап көрөлү. Ар бир стратегия 500 сынак менен Optuna оптимизациясынан өттү.

A стратегиясы (~55% PnL, ~500 соода, ~15% убакыт)

A стратегиясынын параметрлери кенен платону түзөт. htf_entry_sell негизги параметрин алалы:

  • Оптималдуу мааниси: 0.020
  • 0.015-те PnL: +51% (7% төмөндөө)
  • 0.025-те PnL: +49% (11% төмөндөө)
  • 0.010-до PnL: +43% (22% төмөндөө)
  • 0.030-до PnL: +41% (25% төмөндөө)

Муну бир өлчөмдүү сызма катары элестетсеңиз (X огу — htf_entry_sell мааниси, Y огу — PnL), тегиз чокусу бар жумшак параболаны көрөсүз. 0.010-0.030 диапазону — PnL оптимумдун +/-25% чегинде калган плато.

Sensitivity ratio: S=0.110.25=0.44S = \frac{0.11}{0.25} = 0.44 — туруктуу.

10%-дык чекте плато туурасы: 0.013-төн 0.027-ге чейин, Wrel=0.0140.04=35%W^{rel} = \frac{0.014}{0.04} = 35\%.

B стратегиясы (~25% PnL, ~40 соода, ~5% убакыт)

B стратегиясы аз сандагы сооданын негизинде оптималдаштырылган. htf_entry_sell параметри:

  • Оптималдуу мааниси: 0.018
  • 0.015-те PnL: +24% (4% төмөндөө)
  • 0.025-те PnL: +9% (64% төмөндөө)
  • 0.012-де PnL: +11% (56% төмөндөө)

Сызмада — асимметриялуу жана тик ийри сызык. Плато тек тар 0.015-0.020 диапазонунда бар. Оптимумдун оң жагында — жар.

Sensitivity ratio: S=0.640.39=1.64S = \frac{0.64}{0.39} = 1.64 — орточо сезгичтик, бирок 40 соода менен бул эскертүү белгиси. Кичине үлгү + тар плато = overfitting ыктымалдыгы жогору.

10%-дык чекте плато туурасы: 0.016-дан 0.020-го чейин, Wrel=0.0040.04=10%W^{rel} = \frac{0.004}{0.04} = 10\%.

C стратегиясы (~300% PnL, ~400 соода, ~45% убакыт)

C стратегиясы таң каларлык PnL көрсөтөт, бирок плато анализи көйгөйлөрдү ачат:

  • htf_entry_sell-дин оптималдуу мааниси: 0.022
  • 0.020-до PnL: +295% (2% төмөндөө)
  • 0.025-те PnL: +142% (53% төмөндөө)
  • 0.019-до PnL: +128% (57% төмөндөө)

Сызмада — мүнөздүү "ийне": 0.022-де өтө жогорку чоку, бардык багытта курч кулоо. Контурлук сызма муздак түстөр менен дароо курчалган жаркыраган такты көрсөтмөк.

Sensitivity ratio: S=0.530.14=3.79S = \frac{0.53}{0.14} = 3.79начар. 400 соодага карабастан, стратегия бир параметрдин так маанисине ашыкча көз каранды.

10%-дык чекте плато туурасы: 0.021-ден 0.023-кө чейин, Wrel=0.0020.04=5%W^{rel} = \frac{0.002}{0.04} = 5\%.

Жыйынтык таблица

Стратегия PnL Сооданын саны Sensitivity Плато туурасы Robustness score Жыйынтык
A стратегиясы +55% ~500 0.44 35% 0.148 Туруктуу
B стратегиясы +25% ~40 1.64 10% 0.032 Текшерилсин (кичине үлгү)
C стратегиясы +300% ~400 3.79 5% 0.008 Overfitting

Парадокс: PnL-и +300% болгон C стратегиясынын robustness score-у эң начар. "Жөнөкөй" +55% болгон A стратегиясы эң туруктуу. Бул плато анализинин типтүү натыйжасы: таасирдүү сандар көбүнчө начарлыкты жашырат.

Ар бир стратегиянын ишеним интервалдарын кошумча түрдө Monte Carlo bootstrap аркылуу текшерүүгө болот — ал соодаларды кайра тандап алуу учурундагы PnL таралуусун көрсөтөт.

3D визуализация жана heatmap-тар

Контурлук проекциясы бар 3D параметрлер ландшафтынын бети Пол тегиздигине проекцияланган контурлук сызыктары бар эки параметр боюнча PnL-дин 3D бет сызмасы

Эң маанилүү параметр жуптары үчүн 3D бет жана heatmap куруу пайдалуу. Бул ландшафттын формасын интуитивдүү түшүнүүгө мүмкүндүк берет.

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

Туруктуу стратегия үчүн 3D бет сызмасы үстөл тоого окшош — жумшак кыялары бар тегиз чоку. Начар стратегия үчүн — Маттерхорн сыяктуу курч чоку. Heatmap 3D көрүнүштү толуктап, изосызыктар менен бирге ошол эле маалыматты жогорудан төмөн проекциясында көрсөтөт.

Эскертүү белгилери: оптимизация натыйжалары шектүү болгондо

Оптимизация натыйжалары үчүн эскертүү белгилеринин dashboard-у Оптимизация натыйжаларындагы мүмкүн болуучу overfitting-ти билдирген эскертүү индикаторлору

Оптимизация чыныгы мыйзам ченемдин ордуна overfitting тапканын билдирген сегиз белги:

1. Негизги параметр үчүн sensitivity ratio > 2

Параметрдин 10%-дык четтөөсүндө PnL 20%-дан ашык төмөндөсө — оптимум начар.

2. Издөө диапазонунун 10%-инен аз плато туурасы

"Жакшы" аймак изилденген диапазондун 10%-инен азын ээлесе — оптимизатор көбүнчө артефакт тапкан.

3. Топ-3 сынак медианадан 2-3 эсе жогору PnL берет

Мыкты сынактар "дөбөнүн чокусу" болуунун ордуна калгандарынан бөлүнүп турса — бул плато эмес.

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. Жогорку PnL менен сооданын саны аз (< 50)

Кичине үлгү + жогорку PnL = баалоодогу жогорку дисперсия. 40 соода боюнча плато анализи өзү эле ишенимсиз. Мындай стратегиялар үчүн Monte Carlo bootstrap абдан маанилүү.

5. Бир "сыйкырдуу" параметрлер айкалышы

Контурлук сызма боз талаанын ортосунда бир жаркыраган чекитти көрсөтсө — бул стратегия эмес, маалыматтарга ыңгайланган айкалыш.

6. Өтө көп параметрлер

Ар бирисинин 10 мааниси бар 12 параметр үчүн издөө мейкиндигинде 101210^{12} айкалыш бар. Optuna болжол менен 500-үн изилдейт. Мындай мейкиндиктен "жакшы" артефакт табуу ыктымалдыгы жогору. Параметр канчалык көп болсо, плато анализи ошончолук катуу болушу керек.

7. PnL out-of-sample'де курч төмөндөйт

In-sample PnL +87% болсо жана walk-forward +12% көрсөтсө — оптимизация параметрлерди окутуу мезгилине ыңгайлаштырган. Толугураак Walk-Forward оптимизациясы макаласында.

8. Параметрлер диапазон чектерине "кадалган"

Оптималдуу мааниси издөө торунун чеги менен дал келсе — оптимум диапазондон тышкары жатышы мүмкүн. Диапазонду кеңейтип, оптимизацияны кайра иштетиңиз.

Автоматташтырылган плато анализи отчёту

Баарын ар бир оптимизациядан кийин түзүлгөн бир отчётко бириктирүү:

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

Мисал натыйжа:

{
  "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 текшерүү менен байланышы

Плато анализин толуктаган walk-forward текшерүү Эки бири-бирин толуктаган текшерүү системасы катары параметрдик туруктуулук (плато анализи) жана убакыттык туруктуулук (walk-forward)

Плато анализи менен walk-forward текшерүүсү (WFO) — бири-бирин толуктаган ыкмалар:

  • Плато анализи мындай суроого жооп берет: "Оптимум кичине параметр четтөөлөрүнө канчалык туруктуу?" Бул параметрдик туруктуулукту текшерүү.
  • Walk-forward мындай суроого жооп берет: "Параметрлер оптимизатор көрбөгөн маалыматтарда иштейби?" Бул убакыттык туруктуулукту текшерүү.

Стратегия плато анализинен (кенен плато) өтө алат, бирок walk-forward'до ийгиликсиздикке учурашы мүмкүн (рынок режими өзгөрдү). Тескерисинче да — туруктуу параметрлер менен walk-forward'дон өтүп, бирок начар оптимуму болушу мүмкүн.

Сунуш: ар дайым эки ыкманы тең колдонуңуз. Стратегия плато анализинен (R>0.1R > 0.1) жана walk-forward'дон (PnLOOS>50%×PnLIS\text{PnL}_{OOS} > 50\% \times \text{PnL}_{IS}) өтсө — бул туруктуулуктун күчтүү сигналы. Толугураак Walk-Forward оптимизациясы макаласында.

Ар бир баскычтагы PnL ишеним интервалдарын баалоо үчүн Monte Carlo bootstrap колдонуңуз. Ал эми ар кандай активдүү убакыты бар стратегияларды туура салыштыруу үчүн активдүү убакытка карата PnL метрикасын колдонуңуз.

Сунуштар

Оптимизацияга чейин

  1. Параметрлердин санын чектеңиз. Параметр канчалык аз болсо — плато ошончолук ишенимдүү. 5-7 параметр акылга сыярлык максимум. 12-де азыртан эле жогорку этияттык керек.

  2. Маанилүү диапазондорду коюңуз. Чыныгы диапазон 0.005-тен 0.05-ке чейин болсо, htf_entry_sell-ды 0.001-ден 1.0-гө чейин коюп чыкпаңыз. Кереги жок кенен диапазондор плато иллюзиясын жаратат.

  3. Жетиштүү сынактарды колдонуңуз. 12 параметр үчүн жок дегенде 300-500 сынак. Ишенимдүү плато анализи үчүн — 1000+.

Оптимизация учурунда

  1. Конвергенцияны байкаңыз. Optuna 400 сынактан кийин да олуттуу жакшыраак чечимдерди тапса — процесс жыйналган эмес, жана плато анализи ишенимсиз болот.

  2. Pruning'ди этияттык менен колдонуңуз. Агрессивдүү pruning (MedianPruner) алгачкы кадамдарда начар көрүнгөн, бирок ландшафттын толук сүрөтүн куруу үчүн маанилүү сынактарды кесип салышы мүмкүн.

Оптимизациядан кийин

  1. Плато отчётун автоматтык түрдө түзүңүз. generate_plateau_report()-ду оптимизация pipeline'ына киргизиңиз. Визуалдык баалоого ишенбеңиз — сандарды колдонуңуз.

  2. Топ-5 параметрди текшериңиз. Эгер fANOVA 3 параметр дисперсиянын 80%-ын түшүндүрөрүн көрсөтсө — калган 9-ду азыраак кылдаттык менен текшерүүгө болот.

  3. Baseline стратегия менен салыштырыңыз. Демейки параметрлери бар стратегия (оптимизациясыз) +30% көрсөтсө, ал эми оптималдаштырылганы +55% көрсөтсө — айырма болгону 25 пайыздык пункт, жана плато кенен болушу ыктымал. Демейки 0% көрсөтсө, ал эми оптималдаштырылганы +300% көрсөтсө — бүт пайдалуулук параметрлердин так ыңгайлашуусуна көз каранды.

  4. Акыркы текшерүү — walk-forward. Плато анализи туруктуулук үчүн зарыл, бирок жетишсиз шарт. Ар дайым out-of-sample текшериңиз.

Корутунду

Параметрлерди оптималдаштыруу — күчтүү курал, бирок плато анализисиз бул рулетка оюну. Сиз туруктуу мыйзам ченемди тапканыңызды же моделди чуулга ыңгайлаштырганыңызды билбейсиз.

Плато анализинин үч эрежеси:

  1. Robustness score'ду эсептеңиз. Салмактандырылган плато туураларынын көбөйтүндүсү бардык параметрлердин туруктуулугун жыйынтыктаган бир санды берет. R>0.1R > 0.1 — жашыл жарык.

  2. Негизги параметрлер үчүн sensitivity ratio < 1. Параметрдин 10%-дык четтөөсү 10%-дан аз PnL төмөндөшүн жаратса — параметр туруктуу. Көбүрөөк болсо — этият болуңуз.

  3. Контурлук сызмаларды визуализациялаңыз. Эч бир метрика ландшафттын формасын түшүнүүнүн ордун баса албайт. Тегиз үстөл тоо — жакшы. Курч ийне — жаман.

Плато анализи оптимизациядан кийин 5 мүнөт алат жана пайдасыз live-соода жумаларын үнөмдөй алат. Бул study.optimize() менен ботту иштетүүнүн ортосундагы милдеттүү кадам.


Пайдалуу шилтемелер

  1. Optuna Documentation — Visualization
  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)

Цитата

@article{soloviov2026plateauanalysis,
  author = {Soloviov, Eugen},
  title = {Plateau Analysis: How to Distinguish a Robust Optimum from Overfitting},
  year = {2026},
  url = {https://marketmaker.cc/en/blog/post/plateau-analysis-overfitting},
  version = {0.1.0},
  description = {Why finding the best strategy parameters is only half the work. How to visually and quantitatively distinguish a stable plateau from a fragile peak, and why Optuna contour plots are a mandatory step before launching an optimized strategy into production.}
}
blog.disclaimer

Authors

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

Рынктан бир кадам алдыда болуңуз

AI соода аналитикасы, рынок талдоолору жана платформа жаңылыктары үчүн биздин жаңылыктар бюллетенине жазылыңыз.

Биз сиздин купуялыгыңызды урматтайбыз. Каалаган убакта жазылымдан чыга аласыз.