प्लेटो विश्लेषण: एक मजबूत ऑप्टिमम को ओवरफिटिंग से कैसे अलग करें
"बिना भ्रम के बैकटेस्ट" सीरीज़ का लेख 6
📄 यह लेख एक रिसर्च पेपर में विकसित हुआ। मेट्रिक्स को औपचारिक रूप दिया गया है और रिप्रोड्यूसिबल सिमुलेशन के साथ स्ट्रेस-टेस्ट किया गया है, Probability of Backtest Overfitting और Deflated Sharpe Ratio के मुकाबले बेंचमार्क किया गया है। पेपर ऑनलाइन पढ़ें (इंटरैक्टिव वर्जन + PDF) plateau.marketmaker.cc पर, कोड और डेटा github.com/suenot/plateau-robustness पर।
आपने study.optimize() चलाया, Optuna ने PnL +87% वाला एक पैरामीटर सेट खोज लिया। आप उत्साहित हैं और स्ट्रैटेजी को प्रोडक्शन के लिए तैयार कर रहे हैं। दो हफ्ते के लाइव ट्रेडिंग के बाद, PnL लगभग शून्य है। क्या हुआ?
ऑप्टिमाइज़र ने पैरामीटर स्पेस में सुई की नोक खोज ली। पैरामीटर ट्रेड्स की ऐतिहासिक सीक्वेंस के साथ पूरी तरह से फिट हैं — लेकिन मार्केट कंडीशंस में मामूली-सा विचलन पूरे ढांचे को नष्ट कर देता है। यह क्लासिक ओवरफिटिंग है, और इसे लॉन्च से पहले ही पहचाना जा सकता था।
पिछले लेख में हमने coordinate descent की तुलना बायेसियन ऑप्टिमाइज़ेशन से की थी और दिखाया था कि Optuna ऑप्टिमम को अधिक कुशलता से क्यों खोजता है। आज — अगला कदम: कैसे सुनिश्चित करें कि मिला हुआ ऑप्टिमम मजबूत है, न कि नॉइज़ पर फिटिंग का परिणाम।
"बेस्ट" पैरामीटर खोजना काम का केवल आधा हिस्सा क्यों है
एक ऑप्टिमाइज़र वास्तविक ऑप्टिमम की तलाश में एक विशाल मल्टीडायमेंशनल पैरामीटर लैंडस्केप में नेविगेट कर रहा है
स्ट्रैटेजी पैरामीटर ऑप्टिमाइज़ेशन एक मल्टीडायमेंशनल स्पेस में मैक्सिमम की खोज है। समस्या यह है कि मैक्सिमम दो प्रकार के होते हैं:
-
प्लेटो — एक चौड़ा फ्लैट क्षेत्र जहां पैरामीटर वेरिएशन के साथ PnL लगातार ऊंचा बना रहता है। यहां तक कि अगर मार्केट कंडीशंस इफेक्टिव पैरामीटर को 10-20% शिफ्ट कर दें, तो भी स्ट्रैटेजी मुनाफा देती रहेगी।
-
शार्प पीक — एक संकरा शिखर जहां PnL केवल एक्जैक्ट पैरामीटर वैल्यू पर ऊंचा होता है। एक स्टेप का शिफ्ट प्रॉफिटेबिलिटी को ध्वस्त कर देता है। यह लगभग निश्चित रूप से ओवरफिटिंग है: ऑप्टिमाइज़र को ऐतिहासिक डेटा का एक आर्टिफैक्ट मिला, कोई स्थिर पैटर्न नहीं।
एक पर्वतारोहण रूपक: प्लेटो एक ऊंचा पठार है जहां आप सुरक्षित रूप से चल सकते हैं। शार्प पीक एक सुई की नोक है जहां आप केवल संतुलन बना सकते हैं।
शार्प पीक बनाम फ्लैट प्लेटो — विज़ुअल इंट्यूशन
बाएं: एक मजबूत प्लेटो (हल्की ढलानों वाला चौड़ा टेबल माउंटेन)। दाएं: एक नाजुक शार्प पीक (गहरी घाटियों से घिरी सुई की नोक)
एक कॉन्टूर मैप की कल्पना करें जहां एक्सिस दो स्ट्रैटेजी पैरामीटर हैं और रंग PnL को दर्शाता है। दो पैटर्न को दृश्य रूप से आसानी से पहचाना जा सकता है:
प्लेटो (मजबूत ऑप्टिमम):
- एक ही रंग के चौड़े क्षेत्र
- PnL लेवल्स के बीच स्मूथ ट्रांज़िशन
- दूर-दूर स्थित आइसोलाइंस
- ऑप्टिमम से +/-20% शिफ्ट होने पर PnL 10% से ज्यादा नहीं बदलता
एक हीटमैप की कल्पना करें: केंद्र में — एक चमकीला पीला आयत, जो पूरे मैप के लगभग एक-तिहाई के बराबर है। रंग धीरे-धीरे नारंगी में, फिर किनारों की ओर लाल में बदलता है। ऑप्टिमम एक बिंदु नहीं है, बल्कि एक क्षेत्र है।
शार्प पीक (ओवरफिटिंग):
- ठंडे रंगों से घिरा एक संकरा चमकीला धब्बा
- अचानक ट्रांज़िशन: ऑप्टिमम के ठीक बगल में एक गिरावट
- टाइट रिंग्स में संकुचित आइसोलाइंस
- +/-5% शिफ्ट होने पर PnL 50% या उससे ज्यादा गिर जाता है
वही हीटमैप कल्पना करें, लेकिन केंद्र में — नीले और बैंगनी रंग से तुरंत घिरा एक छोटा-सा पीला बिंदु। एक ही "सही" पैरामीटर कॉम्बिनेशन।
पैरामीटर सेंसिटिविटी विश्लेषण
स्लाइस प्लॉट्स जो दिखाते हैं कि PnL व्यक्तिगत पैरामीटर वैल्यूज़ पर कैसे निर्भर करता है — चौड़े बैंड मजबूती दर्शाते हैं, संकरे क्लस्टर नाजुकता दर्शाते हैं
वन-डायमेंशनल विश्लेषण: 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()
स्लाइस प्लॉट पर क्या देखें:
- मजबूत पैरामीटर: पॉइंट क्लाउड ऑप्टिमम के पास एक चौड़ा हॉरिजॉन्टल बैंड बनाता है। बेस्ट ट्रायल्स पैरामीटर वैल्यूज़ की एक चौड़ी रेंज में फैले होते हैं।
- नाजुक पैरामीटर: बेस्ट ट्रायल्स एक संकरी रेंज में केंद्रित होते हैं। पैरामीटर को एक या दो स्टेप शिफ्ट करने पर — और प्रॉफिटेबिलिटी ध्वस्त हो जाती है।
टू-डायमेंशनल विश्लेषण: कॉन्टूर प्लॉट्स (हीटमैप्स)
एक कॉन्टूर प्लॉट एक साथ दो पैरामीटरों के इंटरैक्शन को दिखाता है। यह प्लेटो विश्लेषण के लिए मुख्य टूल है, क्योंकि पैरामीटर शायद ही कभी स्वतंत्र रूप से काम करते हैं — एंट्री और एग्ज़िट थ्रेशोल्ड, टाइमफ्रेम, और पोज़िशन साइज़ आपस में जुड़े होते हैं।
from optuna.visualization import plot_contour
fig = plot_contour(study, params=["htf_entry_sell", "htf_exit_buy"])
fig.show()
एक मजबूत पैरामीटर जोड़ी के लिए कॉन्टूर प्लॉट एक पहाड़ी मैदान के टोपोग्राफिक मैप जैसा दिखता है: स्मूथ चौड़ी आइसोलाइंस, एक ही रंग के बड़े क्षेत्र। एक नाजुक जोड़ी के लिए कॉन्टूर प्लॉट — एक ज्वालामुखी शंकु के मैप की तरह: एक ही बिंदु के चारों ओर टाइट कॉन्सेंट्रिक रिंग्स।
12 सेपरेशन पैरामीटर वाली स्ट्रैटेजी के लिए, यह पेयरवाइज़ कॉन्टूर प्लॉट्स देता है। आपको उन सबका अध्ययन करने की जरूरत नहीं है — उन पैरामीटरों से शुरू करें जिन्हें 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 विज़ुअलाइज़ेशन टूल्स
कॉन्टूर हीटमैप्स जो इंपॉर्टेंस रैंकिंग के साथ-साथ पैरामीटर इंटरैक्शन लैंडस्केप दिखाते हैं
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()
हर कॉन्टूर प्लॉट एक हीटमैप है जिसमें एक्सिस पर दो पैरामीटर होते हैं। रंग पैरामीटर स्पेस के दिए गए क्षेत्र में औसत 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) ऑब्जेक्टिव फंक्शन के वेरियंस को पैरामीटरों और उनके इंटरैक्शन में विघटित करता है। यह साधारण कोरिलेशन से ज्यादा शक्तिशाली है क्योंकि यह नॉनलीनियर इफेक्ट्स को भी ध्यान में रखता है।
क्वांटिटेटिव प्लेटो मेट्रिक्स
सेंसिटिविटी रेशियो, प्लेटो विड्थ, और रोबस्टनेस स्कोर — तीन मेट्रिक्स जो प्लेटो क्वालिटी को औपचारिक रूप देते हैं
विज़ुअल आकलन व्यक्तिपरक है। हमें संख्याओं की जरूरत है। यहां तीन मेट्रिक्स दी गई हैं जो "प्लेटो" के कॉन्सेप्ट को औपचारिक रूप देती हैं।
सेंसिटिविटी रेशियो
पैरामीटर परिवर्तन के मुकाबले PnL परिवर्तन का अनुपात:
जहां वह PnL गिरावट है जब पैरामीटर ऑप्टिमम से द्वारा विचलित होता है।
व्याख्या:
- — पैरामीटर मजबूत है: 10% शिफ्ट से 5% से कम PnL गिरावट होती है
- — मध्यम सेंसिटिविटी
- — पैरामीटर नाजुक है: 10% शिफ्ट से PnL 20%+ गिर जाता है
प्लेटो विड्थ
पैरामीटर क्षेत्र की वह चौड़ाई जिसके भीतर PnL ऑप्टिमम के के भीतर रहता है:
रिलेटिव प्लेटो विड्थ:
जहां डिनोमिनेटर पैरामीटर की पूरी सर्च रेंज है।
व्याख्या:
- — प्लेटो 10% थ्रेशोल्ड पर रेंज के 30% से ज्यादा को कवर करता है। मजबूत पैरामीटर।
- — प्लेटो रेंज के 5% से भी संकरा है। रेड फ्लैग।
रोबस्टनेस स्कोर
सभी पैरामीटरों पर एक संयुक्त मेट्रिक:
जहां fANOVA से पैरामीटर का नॉर्मलाइज़्ड इंपॉर्टेंस है ()।
वेटेड विड्थ्स का प्रोडक्ट एक सख्त मेट्रिक है: यदि एक भी महत्वपूर्ण पैरामीटर का प्लेटो संकरा है, तो कम होगा। अनइम्पॉर्टेंट पैरामीटर (छोटे वाले) का लगभग कोई प्रभाव नहीं होता।
व्याख्या:
- — स्ट्रैटेजी मजबूत है
- — अतिरिक्त वैलिडेशन आवश्यक (walk-forward)
- — ओवरफिटिंग की बहुत संभावना है
ऑटोमेटेड प्लेटो डिटेक्शन के लिए Python कोड
मजबूत प्लेटो और नाजुक पीक की पहचान करने के लिए पैरामीटर लैंडस्केप को स्कैन करता ऑटोमेटेड सिस्टम
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 (शार्प पीक, ओवरफिटेड) की तुलना
आइए 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% के भीतर रहता है।
सेंसिटिविटी रेशियो: — मजबूत।
10% थ्रेशोल्ड पर प्लेटो विड्थ: 0.013 से 0.027 तक, ।
स्ट्रैटेजी 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 में मौजूद है। ऑप्टिमम के दाईं ओर — एक खड़ी चट्टान।
सेंसिटिविटी रेशियो: — मध्यम सेंसिटिविटी, लेकिन 40 ट्रेड्स के साथ यह एक रेड फ्लैग है। छोटा सैंपल + संकरा प्लेटो = ओवरफिटिंग की उच्च संभावना।
10% थ्रेशोल्ड पर प्लेटो विड्थ: 0.016 से 0.020 तक, ।
स्ट्रैटेजी 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 पर एक बहुत ऊंचा शिखर, सभी दिशाओं में तीव्र गिरावट। कॉन्टूर प्लॉट में ठंडे रंगों से तुरंत घिरा एक चमकीला धब्बा दिखेगा।
सेंसिटिविटी रेशियो: — नाजुक। 400 ट्रेड्स के बावजूद, स्ट्रैटेजी एक ही पैरामीटर की एक्जैक्ट वैल्यू पर अत्यधिक निर्भर है।
10% थ्रेशोल्ड पर प्लेटो विड्थ: 0.021 से 0.023 तक, ।
सारांश तालिका
| स्ट्रैटेजी | PnL | ट्रेड्स | सेंसिटिविटी | प्लेटो विड्थ | रोबस्टनेस स्कोर | निर्णय |
|---|---|---|---|---|---|---|
| स्ट्रैटेजी A | +55% | ~500 | 0.44 | 35% | 0.148 | मजबूत |
| स्ट्रैटेजी B | +25% | ~40 | 1.64 | 10% | 0.032 | जांचें (छोटा सैंपल) |
| स्ट्रैटेजी C | +300% | ~400 | 3.79 | 5% | 0.008 | ओवरफिटिंग |
विरोधाभास: +300% PnL वाली स्ट्रैटेजी C का रोबस्टनेस स्कोर सबसे खराब है। "मामूली" +55% वाली स्ट्रैटेजी A सबसे मजबूत है। यह प्लेटो विश्लेषण का एक विशिष्ट परिणाम है: प्रभावशाली संख्याएं अक्सर नाजुकता को छिपा देती हैं।
प्रत्येक स्ट्रैटेजी के लिए कॉन्फिडेंस इंटरवल को Monte Carlo bootstrap के माध्यम से अतिरिक्त रूप से वेरिफाई किया जा सकता है — यह ट्रेड्स को रीसैंपल करते समय PnL स्प्रेड दिखाएगा।
3D विज़ुअलाइज़ेशन और हीटमैप्स
फ्लोर प्लेन पर प्रोजेक्टेड कॉन्टूर लाइंस के साथ दो पैरामीटरों पर PnL का 3D सरफेस प्लॉट
सबसे महत्वपूर्ण पैरामीटर जोड़ियों के लिए, एक 3D सरफेस और हीटमैप बनाना उपयोगी है। यह लैंडस्केप शेप की सहज समझ प्रदान करता है।
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 सरफेस प्लॉट एक टेबल माउंटेन जैसा दिखता है — हल्की ढलानों वाला फ्लैट टॉप। एक नाजुक स्ट्रैटेजी के लिए — मैटरहॉर्न जैसा एक शार्प पीक। हीटमैप 3D व्यू को पूरक करता है, वही जानकारी आइसोलाइंस के साथ टॉप-डाउन प्रोजेक्शन में दिखाता है।
रेड फ्लैग्स: ऑप्टिमाइज़ेशन परिणाम कब संदिग्ध होते हैं
चेतावनी संकेतक जो ऑप्टिमाइज़ेशन परिणामों में संभावित ओवरफिटिंग का संकेत देते हैं
आठ संकेत जो बताते हैं कि ऑप्टिमाइज़ेशन को वास्तविक पैटर्न के बजाय ओवरफिटिंग मिली है:
1. मुख्य पैरामीटर के लिए सेंसिटिविटी रेशियो > 2
अगर 10% पैरामीटर शिफ्ट पर PnL 20% से ज्यादा गिरता है — ऑप्टिमम नाजुक है।
2. सर्च रेंज का 10% से कम प्लेटो विड्थ
अगर "अच्छा" क्षेत्र खोजे गए रेंज के 10% से कम है — ऑप्टिमाइज़र को सबसे अधिक संभावना है कि एक आर्टिफैक्ट मिला है।
3. टॉप-3 ट्रायल्स मीडियन से 2-3x ज्यादा 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 पैरामीटरों के लिए, सर्च स्पेस में कॉम्बिनेशंस होते हैं। Optuna ~500 का अन्वेषण करता है। ऐसे स्पेस में "अच्छा" आर्टिफैक्ट मिलने की संभावना अधिक होती है। जितने ज्यादा पैरामीटर, उतना ही सख्त प्लेटो विश्लेषण होना चाहिए।
7. PnL आउट-ऑफ-सैंपल में तेज़ी से गिरता है
अगर इन-सैंपल 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 वैलिडेशन (WFO) पूरक तरीके हैं:
- प्लेटो विश्लेषण इस सवाल का जवाब देता है: "छोटे पैरामीटर शिफ्ट्स के प्रति ऑप्टिमम कितना स्थिर है?" यह पैरामीट्रिक रोबस्टनेस की जांच है।
- Walk-forward इस सवाल का जवाब देता है: "क्या पैरामीटर उस डेटा पर काम करते हैं जिसे ऑप्टिमाइज़र ने नहीं देखा?" यह टेम्पोरल रोबस्टनेस की जांच है।
एक स्ट्रैटेजी प्लेटो विश्लेषण पास कर सकती है (चौड़ा प्लेटो) लेकिन walk-forward में फेल हो सकती है (मार्केट रीजीम बदल गया)। और इसके विपरीत — यह फिक्स्ड पैरामीटरों पर walk-forward पास कर सकती है लेकिन नाजुक ऑप्टिमम रख सकती है।
सिफारिश: हमेशा दोनों तरीकों का उपयोग करें। अगर कोई स्ट्रैटेजी प्लेटो विश्लेषण () और walk-forward () दोनों पास करती है — यह रोबस्टनेस का मजबूत संकेत है। ज्यादा जानकारी Walk-Forward ऑप्टिमाइज़ेशन लेख में है।
हर स्टेज पर PnL कॉन्फिडेंस इंटरवल का आकलन करने के लिए, Monte Carlo bootstrap लागू करें। और अलग-अलग एक्टिव टाइम वाली स्ट्रैटेजीज़ की सही तुलना के लिए, PnL per active time मेट्रिक का उपयोग करें।
सिफारिशें
ऑप्टिमाइज़ेशन से पहले
-
पैरामीटरों की संख्या सीमित करें। जितने कम पैरामीटर — प्लेटो उतना ही विश्वसनीय। 5-7 पैरामीटर एक उचित अधिकतम है। 12 पैरामीटर पर पहले से ही ज्यादा सावधानी चाहिए।
-
मीनिंगफुल रेंजेज़ सेट करें।
htf_entry_sellको 0.001 से 1.0 तक सेट न करें अगर यथार्थवादी रेंज 0.005 से 0.05 है। अनावश्यक रूप से चौड़ी रेंजेज़ प्लेटो का भ्रम पैदा करती हैं। -
पर्याप्त ट्रायल्स का उपयोग करें। 12 पैरामीटरों के लिए, कम से कम 300-500 ट्रायल्स। विश्वसनीय प्लेटो विश्लेषण के लिए — 1000+।
ऑप्टिमाइज़ेशन के दौरान
-
कन्वर्जेंस पर नज़र रखें। अगर Optuna 400 ट्रायल्स के बाद भी काफी बेहतर सॉल्यूशंस खोजता रहता है — प्रोसेस कन्वर्ज नहीं हुई है, और प्लेटो विश्लेषण अविश्वसनीय होगा।
-
सावधानी से Pruning का उपयोग करें। एग्रेसिव प्रूनिंग (MedianPruner) उन ट्रायल्स को काट सकती है जो शुरुआती स्टेप्स में खराब दिखते हैं लेकिन पूरी लैंडस्केप तस्वीर बनाने के लिए महत्वपूर्ण होते हैं।
ऑप्टिमाइज़ेशन के बाद
-
प्लेटो रिपोर्ट अपने आप जनरेट करें।
generate_plateau_report()को ऑप्टिमाइज़ेशन पाइपलाइन में इंटीग्रेट करें। विज़ुअल आकलन पर भरोसा न करें — संख्याओं का उपयोग करें। -
टॉप-5 पैरामीटरों की जांच करें। अगर fANOVA दिखाता है कि 3 पैरामीटर 80% वेरियंस समझाते हैं — बाकी 9 को कम गहनता से जांचा जा सकता है।
-
बेसलाइन स्ट्रैटेजी से तुलना करें। अगर डिफॉल्ट पैरामीटरों वाली स्ट्रैटेजी (बिना ऑप्टिमाइज़ेशन के) +30% दिखाती है, और ऑप्टिमाइज़्ड वाली +55% — तो फर्क केवल 25 पर्सेंटेज पॉइंट्स है, और प्लेटो शायद चौड़ा है। अगर डिफॉल्ट 0% दिखाता है, और ऑप्टिमाइज़्ड +300% — तो पूरी प्रॉफिटेबिलिटी सटीक पैरामीटर फिटिंग पर निर्भर है।
-
अंतिम जांच — walk-forward। प्लेटो विश्लेषण रोबस्टनेस के लिए आवश्यक लेकिन पर्याप्त नहीं शर्त है। हमेशा out-of-sample वैलिडेट करें।
निष्कर्ष
पैरामीटर ऑप्टिमाइज़ेशन एक शक्तिशाली टूल है, लेकिन प्लेटो विश्लेषण के बिना यह रूले का खेल है। आपको पता नहीं होता कि आपने एक स्थिर पैटर्न खोजा है या मॉडल को नॉइज़ पर फिट किया है।
प्लेटो विश्लेषण के तीन नियम:
-
रोबस्टनेस स्कोर की गणना करें। वेटेड प्लेटो विड्थ्स का प्रोडक्ट एक ही संख्या देता है जो सभी पैरामीटरों की रोबस्टनेस को समराइज़ करती है। — ग्रीन लाइट।
-
मुख्य पैरामीटरों के लिए सेंसिटिविटी रेशियो < 1। अगर 10% पैरामीटर शिफ्ट से 10% से कम PnL गिरावट होती है — पैरामीटर मजबूत है। अगर ज्यादा — सावधान रहें।
-
कॉन्टूर प्लॉट्स को विज़ुअलाइज़ करें। कोई भी मेट्रिक लैंडस्केप शेप की समझ का विकल्प नहीं बन सकता। एक फ्लैट टेबल माउंटेन — अच्छा। एक शार्प सुई — बुरा।
प्लेटो विश्लेषण में ऑप्टिमाइज़ेशन के बाद 5 मिनट लगते हैं और यह हफ्तों के अनप्रॉफिटेबल लाइव ट्रेडिंग को बचा सकता है। यह study.optimize() और बॉट के लॉन्च के बीच एक अनिवार्य कदम है।
उपयोगी लिंक्स
- Optuna Documentation — Visualization
- 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)
उद्धरण
@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.}
}
Authors
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.