از 84,120 وارد موقعیت خرید شدهاید. حد ضرر روی 84,036 و حد سود روی 84,271 است. کندل دقیقه بعد میرسد: بازشدن 84,118، سقف 84,290، کف 84,010، بستهشدن 84,240.
هر دو سطح داخل این کندلاند. قیمت هم به حد ضرر رسیده و هم به حد سود، اما 4 عدد OHLC دقیقاً هیچ اطلاعاتی درباره اینکه کدام زودتر رخ داده نمیدهند. بااینحال، بکتست شما یک عدد برگردانده است. جایی در حلقه، یک خط کد تصمیم گرفته؛ احتمالاً خطی که هنگام نوشتنش به چشمتان یک فرض مدلسازی نمیآمد.
این بزرگترین منبع عملکردِ ساختگیای است که در استراتژیهای کوتاهمدت میبینم؛ حتی بیشتر از کارمزد و لغزش قیمت، چون اصلاً شبیه یک فرض به نظر نمیرسد. شبیه لولهکشی کد است.
راه اشتباه اول: بگذارید زنجیره if تصمیم بگیرد
شکل معمولش این است:
if bar.high >= target:
exit(target, "tp")
elif bar.low <= stop:
exit(stop, "sl")
هیچکس انتخاب نکرده که «حد سود زودتر از حد ضرر تعیین تکلیف شود». بررسی حد سود فقط زودتر نوشته شده، چون مسیر موفقیتآمیز همین است و همان چیزی است که به آن فکر میکردید. ترتیب دو شاخه را عوض کنید تا منحنی سرمایه تغییر کند؛ همین بهتنهایی باید نشان دهد که بخشی از سود و زیان استراتژیتان محصول ویرایشگر کدتان است.
یک اسکالپر بازگشت به میانگین کاملاً معمولی را روی قرارداد دائمی BTCUSDT اجرا کردم: 3 ماه کندلهای 1 دقیقهای، 4,812 معامله، حد ضرر 0.10% و حد سود 0.18% از قیمت ورود. سیگنالها و کارمزدها یکسان بودند و فقط قاعده حل تساوی تغییر کرد.
یکهشتم معاملات کل نتیجه را رقم میزنند. این حسابوکتابِ یک جفت حد ضرر و حد سود نزدیک به هم است: معاملات مبهم همانهایی هستند که قیمت در هر دو جهت حرکت کرده، و بیشتر موقعیتهای جالب همینها هستند؛ هرکدام بسته به شیر یا خط، بهاندازه تمام فاصله حد ضرر تا حد سود ارزش دارد. 12.6% معاملات × فاصله 0.28% یعنی 3.5% از گردش اسمی ناخالص بهازای هر واحد نمونه؛ رقمی که از مزیت واقعی استراتژی بسیار بیشتر است.
این نرخ تابع اندازه کندل نسبت به فاصله سطوح شماست و با بلندتر شدن بازه کندلها بهسرعت بدتر میشود. همان حد ضرر و حد سود، همان سیگنالها، با بازنمونهگیری:
| بازه کندل | معاملاتی که هر دو سطح در یک کندل داخلشان بود | Sharpe (ابتدا حد سود) |
|---|---|---|
| 1s | 0.3% | 0.91 |
| 1m | 12.6% | 2.31 |
| 5m | 34% | 3.60 |
| 15m | 49% | 4.42 |
| 1h | 71% | 5.88 |
ببینید این جدول واقعاً چه میگوید. کندلهای درشتتر بکتست را بهتر کردهاند. غریزه هر پژوهشگری میگوید کندلهای ساعتی انتخاب محافظهکارانهتری هستند: نویز کمتر و بیشبرازش کمتر نسبت به ریزساختار بازار. اما وقتی قاعده درونکندلی به نفع شما نتیجه را تعیین میکند، کندل درشتتر فقط جعبه بزرگتری است که داخلش میتوانید فرض کنید خوششانس بودهاید. در کندلهای 1 ساعته، 7 معامله از هر 10 معامله صرفاً تابع قرارداد شماست. این بکتست استراتژی را نمیآزماید؛ ترتیب دو دستور `if` را 3,400 بار میآزماید.
راه اشتباه دوم: فرض کنید حد ضرر دقیقاً با قیمت حد ضرر پر شده است
فرض کنیم ترتیب را اصلاح کردهاید. حد ضرر زودتر تعیین تکلیف میشود، زیانی دقیقاً برابر با 0.10% بهاضافه کارمزد taker ثبت میکنید و احساس میکنید کارتان دقیق بوده است. اما هنوز 2 ایراد جداگانه وجود دارد.
اول اینکه حد ضرر ماشه فعالسازی است، نه قیمت اجرا. در Binance USDⓈ-M، سفارش STOP_MARKET بهمحض برقرارشدن شرط ماشه به سفارش بازار تبدیل میشود و سپس هرچه در دفتر سفارش باشد برمیدارد. در دقیقهای آرام، لغزش قیمت بهاندازه 1 یا 2 تیک است. اما در همان دقیقهای که واقعاً حد ضررتان را فعال کرده—کندلی با بدنه 40 واحدی و آبشار لیکوییدیشن در پی آن—دفتر سفارش دقیقاً در سمتی که دارید از آن عبور میکنید، کمعمق است. در نمونه من، با تطبیق ماشههای حد ضرر با نوار تیک، قیمت اجرای میانه 1.4 bps از ماشه بدتر بود و صدک 95ام به 11 bps رسید. برای حد ضرر 10 bps، این دنباله توزیع یکدهم دیگر از ریسکی را به شما تحمیل میکند که فکر میکردید تعریف کردهاید.
دومی ظریفتر است و به قراردادهای دائمی مربوط میشود: کدام قیمت آن را فعال میکند. Binance بهطور پیشفرض سفارشهای حد ضرر را با قیمت مارک فعال میکند؛ قیمت مارک از شاخص بهاضافه مبنای هموارشده ساخته میشود، نه از آخرین معامله در همان صرافی. سری OHLC شما قیمت آخرین معامله است. این دو سری متفاوتاند و دقیقاً هنگام رویدادهایی که حد ضرر را فعال میکنند، بیشترین اختلاف را دارند.
| قیمت آخرین معامله (کندلهای شما) | قیمت مارک (ماشه پیشفرض) | |
|---|---|---|
| منبع | معاملات در این صرافی | شاخص چند صرافی + مبنا |
| رفتار سایه کندل | کل دامنه حرکت | بهشدت هموارشده |
| اختلاف معمول | 1–3 bps در شرایط آرام، 20–35 bps در دقیقه آبشاری | |
| پیامد برای بکتست | حد ضررهایی که فعال شدند ولی نباید میشدند، و برعکس | |
پس سایه 25 bps در نوار قیمت آخرین معامله، شما را در بکتست از معامله خارج میکند، درحالیکه قیمت مارک زنده هیچوقت حتی به فاصله 10 bps از ماشه شما نرسیده است. یا برعکس، روزی که شاخص حرکت میکند و صرافی شما عقب میماند. اگر workingType را روی CONTRACT_PRICE بگذارید، دستکم رفتار اجرای زنده را با دادههایتان همراستا میکنید؛ این معمولاً برای پژوهشگر انتخاب درستی است، چون شبیهسازی صادقانه ماشه قیمت مارک یعنی باید یک سری دوم را در سراسر موتور اجرای سفارشهایتان نگه دارید.
بهترین خاطرهام از چنین موردی این است: یکی از همکارانمان با جابهجاکردن حد سود از 0.18% به 0.21%، استراتژی را «بهبود» داد. Sharpe از 2.3 به 3.1 رسید. هیچ مزیت تازهای پیدا نشده بود. حد سود فقط از بخش پرتراکم توزیع سایه کندلهای 1 دقیقهای بیرون رفته بود و در نتیجه، معاملات کمتری در دسته مبهمی قرار میگرفتند که کد بیسروصدا برد را به آنها میداد. آنها قاعده حل تساوی را بهینه کرده بودند.
راه اشتباه سوم: همیشه بدترین حالت را فرض کنید و اسمش را محافظهکاری بگذارید
واکنش فوری این است که بدبینانه عمل کنید. اگر قیمت به هر دو سطح رسید، حد ضرر را بپذیرید. تمام؛ دیگر خوشبینیای در کار نیست، آماده انتشار است.
من هم قبلاً همین کار را میکردم. از گزینه دیگر بهتر است، اما باز هم اشتباه است؛ به 2 دلیل.
استراتژیهای سالم را از بین میبرد. تعیین تکلیف بدبینانه برای 12.6% معاملات، در مقایسه با مقدار 0.94 که با تیکها به دست آمده، 2.1 واحد Sharpe از این استراتژی کم کرد. اگر مقدار واقعی 0.94 باشد و قاعده شما 0.18 نشان دهد، ایده را کنار میگذارید و سراغ کاری بدتر میروید. محافظهکاریای که 2 واحد Sharpe خطا دارد، محافظهکاری نیست؛ نویزی است که ژست اخلاقی گرفته.
بدتر از آن، بهینهسازی را آلوده میکند. اگر به جاروب پارامترها قاعده حل تساوی بدبینانه بدهید، بهینهساز یاد میگیرد از ابهام دوری کند، چون ابهام حالا جریمهای محض است. بهسمت حد ضررهای بازتر و حد سودهای نزدیکتر میرود، یا کندلهای کندتری را انتخاب میکند که در آنها همزمان قرارگرفتن هر دو سطح نادر است؛ بعد پارامترهایی را به شما نشان میدهد که بر اساس قرارداد اجرای سفارش شما انتخاب شدهاند، نه بر اساس بازار. همان شکستِ نسخه خوشبینانه، با علامت معکوس، و به همان اندازه نامرئی در گزارش عملکرد.
قاعده سرانگشتی ما پیش از هر چیز این است: اگر stop_distance + target_distance از دامنه صدک 75ام بازه کندلتان کوچکتر باشد، فرض درونکندلی در سود و زیان سهمی بیشتر از سیگنال شما دارد. هر دو عدد را محاسبه کنید. 4 خط کد لازم دارد و بیش از هر بررسی دیگری به پایان بررسی استراتژیها منجر شده است.
چه چیزی واقعاً جواب میدهد
مسیر حرکت قیمت داخل کندل، داده است. آن را به دست بیاورید یا دامنه چیزی را که نمیتوانید به دست بیاورید، برآورد کنید.
- با دقیقترین سری زمانی موجود نتیجه را تعیین کنید. برای دقیقههای مربوط، aggTrades در Binance فقط چند صد ردیف است و مسئله را یکسره حل میکند: قیمت ابتدا به کدام سطح رسید و حرکت سریع بازار با چه قیمتی اجرا شد. برای کل بکتست به داده تیک نیاز ندارید؛ فقط برای کندلهای مبهم لازم است. در نمونه من، این تعداد 606 دقیقه از 129,600 دقیقه بود. یک دانلود کوچک است، نه پروژه زیرساختی.
- اگر تیکها در دسترس نیستند، فقط برای تعیین نتیجه، 1 یا 2 سطح زمانی پایینتر بروید. سیگنالها روی 15m و خروجها با کندلهای 1s یا 1m تعیین تکلیف شوند. ابهام از 49% به کسری از یک درصد میرسد و ابهام باقیمانده آنقدر کم است که بتوان صادقانه نادیدهاش گرفت.
- دامنه نتایج را همیشه گزارش کنید. هر بکتست را 2 بار اجرا کنید: یکبار با تعیین نتیجه خوشبینانه و یکبار بدبینانه؛ هر دو مقدار Sharpe را کنار مقدار تعیینشده چاپ کنید. این فاصله، عدمقطعیت درونکندلی شماست و باید در گزارش عملکرد، کنار فاصله اطمینانی که برای خود Sharpe میگذارید، بیاید. وقتی دامنه 0.2–2.3 است، هیچ نتیجهگیریای درون آن واقعی نیست.
- نرخ ابهام را بهعنوان معیاری اصلی دنبال کنید. نرخ ما در بالای کارت هر استراتژی، کنار تعداد معاملات و گردش معاملات قرار دارد. نرخ بالاتر از حدود 5% یعنی منطق خروج، نه منطق ورود، چیزی است که در حال آزمودنش هستید.
- ماشه را جدا از اجرا مدل کنید. ماشه را بر اساس سری قیمتی فعال کنید که صرافی واقعاً استفاده میکند؛ اجرا را با قیمت ماشه بهاضافه لغزش قیمتِ کالیبرهشده از نوار معاملات مدل کنید، نه با خود قیمت ماشه.
سهام هم همین مشکل را دارند، فقط با ظاهری متفاوت. حد ضرر روی 62.00 برای سهمی که یکشبه با شکاف قیمتی به 58.40 میرسد، در قیمت 62.00 اجرا نمیشود؛ اجرا جایی پایینتر از قیمت بازگشایی خواهد بود. بکتست مبتنی بر کندل روزانه که برای عبور شکافی قیمت، لغزش 0.00 دلار ثبت میکند، با خیال راحت به شما میگوید افزودن حد ضرر، افت سرمایه را بهتر کرده است. چنین اتفاقی نیفتاده؛ فقط در روزهای مهم، استراتژی آزموده نشده است. توقف معاملات هم همین کار را میکند: اجرای سفارش حد ضرر واقعاً در حراج بازگشایی انجام میشود، با قیمتی که کف کندل نشانش نمیدهد.
هیچکدام از اینها عجیبوغریب نیست. فقط باید بپذیریم که کندل خلاصهای از دادههاست و استراتژی مبتنی بر حد ضرر و حد سود، روی ترتیب رویدادهایی شرط میبندد که آن خلاصه حذفشان کرده است. وقتی موتور معامله کاغذی بالاخره استراتژی را با نوار معاملات زنده اجرا کند، نوار معاملات درباره آن ترتیب نظر خودش را دارد و هیچوقت برایش مهم نبوده شما کدام شاخه از دستور if را زودتر نوشتهاید.
← همهٔ مطالب

