Backtest — اعتبارسنجی استراتژی
اصول، خطاهای رایج، و خوانش نتایج.
بکتست — اعتبارسنجی استراتژی
هدف یادگیری
پس از مطالعه این درس قادر خواهید بود:
- یک بکتست کامل و معتبر از ابتدا تا انتها طراحی کنید
- خطاهای آماری و ساختاری رایج را شناسایی و حذف کنید
- متریکهای کمی را با فرمولهای دقیق محاسبه و تفسیر کنید
- از روشهای Walk-Forward و Monte Carlo برای اعتبارسنجی واقعی استفاده کنید
---
۱. بکتست چیست و چرا اهمیت دارد؟
بکتست فرآیند اعمال یک استراتژی معاملاتی بر روی دادههای تاریخی بهمنظور ارزیابی عملکرد آن پیش از ورود به بازار واقعی است. اما در سطح پیشرفته، بکتست صرفاً «اجرای استراتژی روی چارت گذشته» نیست؛ بلکه یک فرآیند آماری دقیق است که باید شرایط بازار واقعی را با حداکثر وفاداری شبیهسازی کند.
اجزای اصلی یک موتور بکتست حرفهای
- Data Engine: مدیریت دادههای OHLCV با دقت tick یا bar
- Order Management System (OMS): شبیهسازی انواع سفارش (Market, Limit, Stop-Limit)
- Position Tracker: نگهداری وضعیت پرتفوی و exposure در هر لحظه
- Transaction Cost Model: اسپرد، کارمزد، و slippage واقعی
- Risk Engine: مدیریت سایز پوزیشن، استاپها، و محدودیتهای مارجین
نکته کلیدی: بکتست مبتنی بر bar (کندل) نمیتواند دقیقتر از تایمفریم آن bar باشد. اگر با دادههای روزانه بکتست میکنید، سیگنالهایی که به دقت ساعتی نیاز دارند ذاتاً دچار خطا خواهند بود.
---
۲. انواع بایاسهای مخرب
مهمترین بخش یک بکتست معتبر، شناخت و حذف بایاسهاست. این بایاسها نتایج را بهطور مصنوعی مثبتتر از واقعیت نشان میدهند.
۲.۱ Look-Ahead Bias
خطرناکترین خطا در بکتست. این اتفاق زمانی میافتد که استراتژی به اطلاعاتی دسترسی دارد که در لحظه اجرای واقعی سیگنال هنوز وجود نداشته است.
مثال عملی:
فرض کنید سیگنال خرید را بر اساس بستن کندل روزانه محاسبه میکنید، اما ورود را در همان قیمت بستن ثبت میکنید. در واقعیت، قیمت بستن در لحظه اجرای سفارش هنوز نهایی نشده است:
کندل روزانه بسته میشود → سیگنال محاسبه میشود → کندل بعدی باز میشود → ورود
کد با خطا:
python
خطا: استفاده از close[-1] برای ورود همان کندل
if rsi[-1] < 30:
entry_price = close[-1] # look-ahead bias
نسخه صحیح:
python
صحیح: سیگنال از کندل قبلی، ورود در باز شدن کندل جاری
if rsi[-2] < 30:
entry_price = open[-1]
۲.۲ Survivorship Bias
اگر بکتست روی یونیورسی از داراییها انجام میدهید و تنها داراییهایی که هنوز وجود دارند را در نظر میگیرید، بهطور ناخودآگاه پروژههایی که به صفر رفتند یا از رده خارج شدند را حذف کردهاید. در کریپتو، این مشکل بسیار جدی است: صدها توکن که در دوره بکتست شما فعال بودند، امروز دیگر وجود ندارند. نتیجه: اریب مثبت تصنعی در بازدهی.
۲.۳ Overfitting / Curve Fitting
وقتی پارامترهای زیادی را بهمنظور حداکثرسازی بازدهی تاریخی تنظیم میکنید، استراتژی در واقع نویز دادههای گذشته را یاد میگیرد، نه الگوی واقعی بازار.
قانون سرانگشتی کمی: هر پارامتر آزاد به حداقل ۳۰ معامله مستقل برای اعتبارسنجی آماری نیاز دارد. استراتژی با ۵ پارامتر آزاد حداقل به ۱۵۰ معامله در دوره In-Sample نیاز دارد تا نتایج از نظر آماری معنادار باشند.
---
۳. متریکهای کمی و فرمولهای دقیق
۳.۱ Sharpe Ratio
$$\text{Sharpe} = \frac{R_p - R_f}{\sigma_p}$$
که در آن $R_p$ میانگین بازدهی سالانهشده استراتژی، $R_f$ نرخ بدون ریسک، و $\sigma_p$ انحراف معیار سالانهشده بازدهیهاست.
مثال محاسباتی:
فرض کنید یک استراتژی با دادههای ماهانه:
- میانگین بازدهی ماهانه: ۲.۵٪
- انحراف معیار ماهانه: ۶٪
- نرخ بدون ریسک: ۰٪ (معمول در کریپتو)
سالانهسازی:
- بازدهی: ۲.۵٪ × ۱۲ = ۳۰٪
- انحراف معیار: ۶٪ × √۱۲ ≈ ۲۰.۸٪
$$\text{Sharpe} = \frac{30\%}{20.8\%} \approx 1.44$$
شارپ بالای ۱.۰ قابل قبول، بالای ۱.۵ خوب، و بالای ۲.۰ عالی تلقی میشود.
۳.۲ Sortino Ratio
تفاوت اصلی با Sharpe: فقط نوسانات منفی (Downside Deviation) در مخرج قرار میگیرد:
$$\text{Sortino} = \frac{R_p - R_f}{\sigma_d}$$
که در آن $\sigma_d$ انحراف معیار فقط بازدهیهای زیر آستانه هدف (معمولاً صفر) است. این متریک برای استراتژیهایی با توزیع نامتقارن — مثلاً ضررهای کوچک متعدد و سودهای بزرگ کم — دقیقتر از Sharpe عمل میکند.
۳.۳ Maximum Drawdown و Calmar Ratio
$$\text{MDD} = \frac{\text{Trough} - \text{Peak}}{\text{Peak}} \times 100$$
مثال:
پرتفوی از ۱۰,۰۰۰$ به ۱۵,۰۰۰$ رشد میکند، سپس به ۹,۰۰۰$ کاهش مییابد:
$$\text{MDD} = \frac{9000 - 15000}{15000} = -40\%$$
Calmar Ratio نسبت بازدهی سالانه به قدرمطلق MDD است:
$$\text{Calmar} = \frac{R_{annual}}{|\text{MDD}|}$$
اگر بازدهی سالانه ۵۰٪ و MDD برابر ۴۰٪ باشد: Calmar = 1.25
۳.۴ Expectancy — انتظار ریاضی
برای هر واحد ریسکشده:
$$E = (W \times \bar{W}) - (L \times \bar{L})$$
که در آن $W$ نرخ برد، $\bar{W}$ میانگین سود به ازای هر برد، $L$ نرخ باخت، و $\bar{L}$ میانگین ضرر به ازای هر باخت است.
مثال:
- Win Rate: ۴۵٪
- میانگین سود: ۳R
- میانگین ضرر: ۱R
$$E = (0.45 \times 3) - (0.55 \times 1) = 1.35 - 0.55 = +0.80R$$
یعنی به ازای هر معامله، انتظار ریاضی ۰.۸ برابر ریسک سود وجود دارد. هر عدد مثبت نشانه سودآوری بلندمدت است — مشروط به اینکه بایاسی در بکتست وجود نداشته باشد.
---
۴. Walk-Forward Analysis
Walk-Forward تنها روش مطمئن برای شبیهسازی عملکرد واقعی استراتژی در طول زمان است. ایده اصلی: پارامترها را روی یک بازه بهینه میکنید، سپس روی بازه بعدی که در بهینهسازی دیده نشده، تست میکنید.
ساختار Sliding Window
|--- IS ---|--- OOS ---|
|--- IS ---|--- OOS ---|
|--- IS ---|--- OOS ---|
- IS (In-Sample): دوره بهینهسازی پارامترها
- OOS (Out-of-Sample): دوره تست واقعی بدون دست زدن به پارامترها
| پارامتر | مقدار پیشنهادی |
|---------|---------------|
| نسبت IS به OOS | ۷۰/۳۰ یا ۸۰/۲۰ |
| تعداد Windows | حداقل ۱۰ |
| Anchored vs Rolling | Rolling برای بازارهای پرنوسان |
شاخص Walk-Forward Efficiency
$$WFE = \frac{\text{OOS Performance}}{\text{IS Performance}}$$
اگر WFE بالای ۰.۵ باشد، استراتژی به خوبی به دادههای دیدهنشده تعمیم مییابد. زیر ۰.۳ نشانه Overfitting جدی است.
---
۵. Monte Carlo Simulation
حتی با بکتست کامل، ترتیب تاریخی معاملات گذشته یک مسیر از میان بینهایت مسیر ممکن است. شبیهسازی مونتکارلو با تغییر تصادفی ترتیب معاملات، توزیع واقعیتری از نتایج ممکن را نشان میدهد.
فرآیند گامبهگام:
۱. لیست P&L تمام معاملات بکتست را استخراج کنید
۲. ترتیب آنها را بهصورت تصادفی بچینید
۳. این کار را ۱۰,۰۰۰ بار تکرار کنید
۴. توزیع MDD و بازدهی نهایی را در تمام سناریوها تحلیل کنید
python
import numpy as np
trades = [0.02, -0.01, 0.03, -0.015, 0.025, -0.01] # P&L هر معامله
n_simulations = 10000
max_drawdowns = []
for _ in range(n_simulations):
shuffled = np.random.permutation(trades)
equity = np.cumprod(1 + shuffled)
rolling_max = np.maximum.accumulate(equity)
drawdowns = (equity - rolling_max) / rolling_max
max_drawdowns.append(drawdowns.min())
dd_95 = np.percentile(max_drawdowns, 5)
print(f"MDD در سطح اطمینان ۹۵٪: {dd_95:.2%}")
چه اطلاعاتی به دست میآید:
اگر بکتست ۲۵٪ MDD نشان دهد، مونتکارلو ممکن است بگوید در ۹۵٪ سناریوها MDD از ۴۵٪ تجاوز نخواهد کرد. این عدد برای تعیین سایز واقعی سرمایه بسیار مهمتر از MDD تاریخی تکین است.
---
۶. Robustness Testing — آزمون مقاومت
یک استراتژی واقعی باید نسبت به تغییرات جزئی پارامترها حساس نباشد. اگر استراتژی تنها با MA(20) سودده است و با MA(19) یا MA(21) ضررده میشود، به احتمال زیاد Overfitting دارد.
Parameter Surface Analysis
پارامترهای اصلی را در یک بازه تغییر دهید و ماتریس Sharpe نتایج را بررسی کنید:
| Fast MA / Slow MA | 50 | 100 | 200 |
|-------------------|-----|-----|-----|
| 10 | ۱.۲ | ۰.۹ | ۰.۴ |
| 20 | ۱.۴ | ۱.۶ | ۱.۱ |
| 30 | ۱.۱ | ۱.۵ | ۰.۸ |
ناحیه Fast=20, Slow=100 با Sharpe=1.6 مرکز یک plateau (فلات) است، نه یک peak تیز. این ساختار نشانه قابل اعتماد بودن استراتژی است — اگر بازار کمی تغییر کند، عملکرد بهشدت افت نخواهد کرد.
Noise Injection — تزریق نویز
مقدار کمی نویز تصادفی به قیمتهای ورود و خروج اضافه کنید تا slippage واقعی را شبیهسازی کنید:
python
noise_factor = 0.001 # ۰.۱٪ نویز
entry_price = signal_price * (1 + np.random.uniform(-noise_factor, noise_factor))
اگر با تنها ۰.۱٪ نویز، بازدهی بهشدت افت کند، استراتژی در بازار واقعی که slippage و اسپرد دارد، پایدار نخواهد بود.
---
نتیجهگیری
بکتست در سطح پیشرفته یک تمرین آماری جدی است، نه صرفاً اجرای استراتژی روی دادههای گذشته. اعتبار نتایج مستقیماً به کیفیت طراحی فرآیند بستگی دارد. بکتست خوب نمیگوید استراتژی حتماً سودده خواهد بود؛ میگوید دلیل قویتری برای ادامه تحقیق وجود دارد.
خلاصه نقطهای:
- Look-ahead bias با ثبت ورود در کندل بعد از سیگنال حذف میشود، نه در همان کندل
- Survivorship bias با استفاده از یونیورس تاریخی کامل شامل داراییهای حذفشده کنترل میشود
- Overfitting با محدودکردن پارامترها و اعمال Walk-Forward شناسایی میشود
- Sharpe Ratio بالای ۱.۵ با حداقل ۱۰۰ معامله نشانه اعتبار اولیه است، نه تضمین سودآوری
- Walk-Forward Efficiency بالای ۰.۵ شرط لازم برای اعتماد به نتایج OOS است
- Monte Carlo بدترین MDD محتمل را با توزیع آماری نشان میدهد — این عدد برای sizing واقعیتر از MDD تاریخی است
- Sensitivity Analysis مقاومت استراتژی را میسنجد؛ plateau در parameter surface بهتر از peak تیز است
- هیچ بکتستی، حتی کاملترین آن، آینده را تضمین نمیکند؛ بازار رژیم تغییر میدهد و استراتژی باید بهصورت پیوسته پایش شود