← Мақалаларға оралу
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% болатын параметрлер жиынын тапты. Сіз қуанып, стратегияны production-ға дайындап жатырсыз. Екі апталық лайв трейдингтен кейін PnL нөлге жақын. Не болды?

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

Алдыңғы мақалада біз coordinate descent-ті байестік оптимизациямен салыстырдық және Optuna оптимумды неге тиімдірек табатынын көрсеттік. Бүгін — келесі қадам: табылған оптимумның шуылға бейімделудің нәтижесі емес, тұрақты екеніне қалай көз жеткізуге болады.

Ең жақсы параметрлерді табу неге жұмыстың тек жартысы

Көпөлшемді параметрлер кеңістігінде іздеу Оптимизатор шынайы оптимумды іздеп, кең көпөлшемді параметрлер ландшафтын шарлап жатыр

Стратегия параметрлерін оптимизациялау — көпөлшемді кеңістіктегі максимумды іздеу. Мәселе — максимумдар екі түрлі болады:

  1. Плато — параметрлер өзгерсе де PnL тұрақты жоғары болатын кең тегіс аймақ. Нарық жағдайлары тиімді параметрлерді 10-20%-ға ауыстырса да, стратегия пайда әкелуін жалғастырады.

  2. Өткір шың — PnL тек параметрдің дәл мәнінде ғана жоғары болатын тар шоқы. Бір қадамдық ауытқу пайдалылықты құлатады. Бұл дерлік сөзсіз overfitting: оптимизатор тарихи деректердің артефактісін тапты, тұрақты заңдылықты емес.

Альпинистік метафора: плато — сіз қауіпсіз жүре алатын таулы үстірт. Өткір шың — тек тепе-теңдік сақтауға болатын инеше ұшы.

Өткір шың мен тегіс плато — визуалды түйсік

Өткір шың мен тегіс плато салыстыруы Сол жақта: тұрақты плато (жайпақ беткейлері бар кең үстел тау). Оң жақта: нәзік өткір шың (терең аңғарлармен қоршалған инеше ұшы)

Осьтер екі стратегия параметрі болатын, ал түс 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 минут алады және пайдасыз лайв трейдингтің апталарын үнемдей алады. Бұл 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 сауда талдаулары, нарық аналитикасы және платформа жаңалықтары үшін біздің ақпараттық бюллетеньге жазылыңыз.

Біз сіздің жекелігіңізді құрметтейміз. Кез келген уақытта жазылымнан шығуға болады.