هفته پیش نوتبوک را برایم فرستادی: همان سیگنال، همان مجموعه داراییها، همان 14 ماه داده BTCUSDT perp. فقط اجرا فرق کرده بود. دیگر spread را رد نمیکردی و بهجایش bidهایت را یک tick داخلتر میگذاشتی؛ Sharpe از 0.42 به 2.14 رسیده بود. پرسیدی آیا این نتیجه واقعی است یا چیزی را خراب کردهای.
یک چیزی را خراب کردهای. میخواهم دقیقاً نشانت بدهم کجا، چون باگی که وجود دارد یک خط است و درسی که پشتش هست خیلی بزرگتر از آن خط.
این هم قانون پرشدنت، عیناً از موتور اجرا:
if bar.low <= limit_price: fill(limit_price)
این میگوید: اگر طی این دقیقه معاملهای در بازار به قیمت من یا پایینتر انجام شد، سفارش من هم به قیمت خودم پر شده است. اما چیزی که واقعاً کد کردهای این است که قیمت به سطح تو رسیده. رسیدن به قیمت با پرشدن سفارش یکی نیست. بین این دو یک صف وجود دارد و تو صف را خالی فرض کردهای.
چه چیزی جلوی تو ایستاده
وقتی bid را روی 84,120.0 در BTCUSDT perp میگذاری، ته صف سفارشهایی قرار میگیری که از قبل آنجا منتظرند. در سطح اول دفتر سفارش این قرارداد، بسته به ساعت روز، معمولاً چیزی بین 4 تا 30 BTC سفارش وجود دارد؛ میانه در بازه نمونهات حدود 12 BTC است. سفارش تو 0.4 BTC است. برای اینکه معاملهای انجام دهی، فروشندهها باید با حجم تجمیعی کافی به آن سطح بزنند تا همه کسانی را که قبل از تو آمدهاند پشت سر بگذارند؛ آن هم پیش از آنکه سفارشها لغو شوند و سطح از زیر پایت خالی شود یا قیمت بازار از آن بالا برود.
پس سؤالی که بکتستت باید بپرسد «آیا قیمت به 84,120.0 رسید؟» نیست، بلکه این است: «آیا وقتی سفارشم آنجا بود، دستکم 12.4 BTC فروش بازار در قیمت 84,120.0 انجام شد؟» این دو رویداد تفاوت فاحشی دارند. در دادههایت، تفاوتشان حدوداً ضریب 3 است.
3 قانون پرشدن، 3 استراتژی متفاوت
سیگنالت را با همان ورودیها و 3 مدل اجرا دوباره اجرا کردم. همان آلفا، همان کارمزدها، همان funding. فقط منطق پرشدن تغییر کرد.
| قانون پرشدن | تعداد پرشدنها | میانگین edge طی 60s پس از پرشدن | Sharpe |
|---|---|---|---|
رسیدن قیمت: low <= limit | 4,180 | +2.6 bp | 2.14 |
عبور کامل: low < limit - 1 tick | 1,712 | +0.4 bp | 0.61 |
| شبیهسازی صف بر پایه حجم در سطح | 1,306 | +0.9 bp | 0.77 |
ردیف وسط همان راهحل سرهمبندیشدهای است که همه اول سراغش میروند: فقط وقتی پرشدن را حساب کن که بازار واقعاً از قیمتت عبور کند، با این استدلال که اگر قیمت از آن طرف گذشته، حتماً سفارش تو را هم مصرف کرده است. جهت کلی این فرض درست است و بیشتر خیالپردازیها را حذف میکند. اما خودش هم سوگیری بدی دارد که کمی بعد سراغش میروم.
ردیف پایین همان مدلی است که باید بسازی. به داده L3 و بازسازی سفارشبهسفارش نیاز ندارد. بیشتر اجزایش را همین حالا داری.
شبیهسازی صفی که میتوانی با aggTrades بسازی
بهجای klines، جریان معاملات تجمیعی را بگیر. هر معامله قیمت، مقدار، timestamp و پرچم سمت maker را دارد که میگوید سفارش تهاجمی خرید بوده یا فروش. برای شبیهسازی قابلقبول سفارش غیرفعال خودت همینها کافی است:
- وقتی سفارشت ثبت میشود، حجم سفارشهای منتظر در قیمت خودت را ثبت کن. اگر فقط فید دفتر سفارش 100ms داری، از آخرین snapshot استفاده کن؛ خطایش در مقایسه با خطایی که داری رفع میکنی ناچیز است.
- مقدار
queue_ahead = resting_sizeرا تنظیم کن. بدبین باش و فرض کن آخری. مگر اینکه خودت سطح را ایجاد کرده باشی، همینطور است. - جریان معاملات را جلو ببر. هر معامله با سفارش تهاجمی فروش در قیمت تو یا پایینتر، مقدار خودش را از
queue_aheadکم میکند. وقتی منفی شد، سفارشت در همان timestamp و با قیمت خودت پر شده است. - اگر قیمت یک tick از سطح فاصله گرفت، صف را صفر نکن؛ حجمش را کاهش بده. بعضی از سفارشهای جلوی تو وقتی سطح قدیمی میشود لغو میشوند و بعضی نمیشوند. کاهش 30-40% بهازای هر ثانیه کامل دور از سطح، در بازسازیهای من بهتر از هر دو فرض افراطی جواب داد.
- اگر استراتژیات سفارش را لغو و دوباره ثبت میکند، سفارش تازه را سفارشی جدید در انتهای صف سطح تازه مدل کن. این همان مرحلهای است که خیلیها جا میاندازند و خیالپردازی باقیمانده همانجا پنهان میشود.
میدانم سر مرحله 2 میخواهی با من بحث کنی. بله، گاهی نزدیک ابتدای صف هستی، چون درست وقتی سطح شکل گرفته سفارش گذاشتهای. قبول؛ این را اندازه بگیر، فرض نکن. حجم سفارشهای منتظر هنگام ثبت را ثبت کن و بگذار دادهها نشان دهند چه سهمی از سفارشها واقعاً زود رسیدهاند. در استراتژی تو این سهم 11% بود، چون سیگنالت بعد از حرکت بازار فعال میشود؛ یعنی سطحی که به آن میپیوندی از قبل شکل گرفته و جمعیت خودش را دارد.
پرشدنهایی که نصیبت میشوند همانهاییاند که کاش نمیشدند
حالا میرسیم به بخشی که واقعاً اهمیت دارد و دلیل سوگیری قانون عبور کامل.
به این فکر کن که چه وقت bid تو کاملاً مصرف میشود. وقتی این اتفاق میافتد که فشار فروش آنقدر زیاد باشد که کل سطح را بخورد. یعنی بنا به تعریف، درست همان لحظهای که بازار از قیمت تو رو به پایین عبور میکند. مطمئنترین پرشدنها همانهایی هستند که بلافاصله تو را در موقعیت نامطلوب قرار میدهند.
پرشدنهایت را بر اساس نحوه رخدادنشان دستهبندی کن و mark-out را در 60 ثانیه اندازه بگیر:
| نوع پرشدن | سهم از پرشدنها | Mark-out در 60s |
|---|---|---|
| معامله در سطح انجام شد، قیمت برگشت بالا | 38% | +3.1 bp |
| معامله در سطح انجام شد، قیمت ثابت ماند | 21% | +0.2 bp |
| قیمت با فاصله 2+ tick از سطح عبور کرد | 41% | −2.4 bp |
بکتست سادهانگارانهات هر 3 دسته را به تو داد و همه را رایگان حساب کرد. قانون عبور کامل تقریباً فقط دسته سوم را به تو میدهد و به همین دلیل edge آن بیشتر از شبیهسازی صف سقوط کرد. هیچکدام درست نیستند. شبیهسازی صف ترکیبی واقعبینانه میدهد و کل ماجرا همین ترکیب است: اجرای غیرفعال spread را نصیبت میکند و در عوض هزینه گزینش نامطلوب را به تو تحمیل میکند؛ نسبت این دو است که استراتژی واقعی تو را میسازد.
چارچوب فکری قدیمی میز سهام هنوز هم پابرجاست: سفارش maker اختیار معامله رایگانی است که به بازار فروختهای. طرف مقابل وقتی از آن استفاده میکند که استفادهاش بهصرفه باشد. بکتستت حقبیمه را میگرفت و فراموش میکرد که اختیار معامله بخش پرداختی هم دارد.
معاملههایی که نگرفتی، خود استراتژی را تغییر میدهند، نه فقط هزینهاش را
این همان نکتهای است که بیش از همه میخواهم دربارهاش فکر کنی. وقتی اجرای taker را بد مدل میکنی، معاملههای درست را با قیمت اشتباه میگیری و اصلاح کارمزد بیشتر مشکل را برطرف میکند. اما وقتی اجرای maker را بد مدل میکنی، از اساس مجموعه اشتباهی از معاملهها را میگیری. حدود 2,900 مورد از 4,180 ورودی تو اصلاً انجام نشدهاند. بعضی از آنها بهترین سیگنالهایت بودند؛ روی کندلهایی که جهش کردند و برگشتند، دقیقاً همان الگویی که بازار بدون تو از آن گذشت.
پس شاخه پرنشدن سفارش باید منطق واقعی داشته باشد. وقتی سیگنال منقضی میشود و ورودی هنوز پر نشده، استراتژی چه میکند؟ با سفارش taker قیمت را دنبال میکند و spread بهعلاوه اثر بازار را میپردازد؟ سفارش را پایینتر دوباره میگذارد و مبنای ورودی متفاوتی میپذیرد؟ معامله را رد میکند و بدون موقعیت میماند؟ هر انتخاب منحنی سرمایه متفاوتی میسازد و هیچکدام «فرض کن سفارشت پر شده» نیست. در اجراهای ما، افزودن یک قانون صادقانه برای دنبالکردن قیمت (اگر پس از 20 ثانیه پر نشد، عبور از spread با سقف 3 bp لغزش) حدود یکسوم معاملههای ازدسترفته و تقریباً نیمی از فاصله بین Sharpe سادهانگارانه و Sharpe شبیهسازی صف را جبران کرد. نتیجه واقعاً جالبی است و فقط وقتی دیده میشود که مدل پرشدن آنقدر واقعی باشد که این سؤال معنا پیدا کند.
یک بررسی سریع و ساده، 10 دقیقهای: گزارش paper trading زندهات را با بکتست در همان بازه مقایسه کن. نرخ پرشدن را بسنج، نه PnL را. اگر بکتست 100% سفارشهای منتظر را پر میکند ولی paper فقط 34% را، مقایسه استراتژی انجام نمیدهی؛ باگ مدل پرشدن داری. پیش از نگاهکردن به حتی یک عدد بازده، آن را درست کن.
حالا که صحبتش شد، 2 نکته کوچکتر هم هست
رد سفارشهای post-only. اگر برای تضمین سطح کارمزد maker از post-only استفاده میکنی و دفتر سفارش بین تصمیم تو و تأیید صرافی جابهجا شود، سفارش بهجای آنکه در صف بماند رد میشود. در گزارشهای paper ما، طی ساعات عادی 3-6% تلاشها روی BTCUSDT رد میشوند و در دقیقه پس از اعلام CPI آمریکا این عدد از 15% هم بالاتر میرود. سفارش ردشده نه سفارش پرشده است و نه سفارش منتظری که پر نشده باشد؛ معاملهای است که هیچوقت وجود نداشته. اگر بکتستت چنین حالتی ندارد، تعداد معاملهها دقیقاً در رژیمهایی باد کرده که بیش از همه برایت مهماند.
پیشگیری از معامله با خودت و اثر سفارشهای خودت. با 0.4 BTC قیمت BTCUSDT را جابهجا نمیکنی، پس از آن صرفنظر کن. اما گفتی میخواهی این را روی قرارداد آتی یک آلتکوین با ارزش بازار متوسط اجرا کنی که ارزش اسمی دفتر سفارش سطح اولش اغلب کمتر از $15k است. آنجا سفارش تو واقعاً سهم معناداری از صف است و عدد سفارشهای جلوی تو که ثبت میکنی، اثر حضور سفارش قبلی خودت را هم در بر میگیرد. وقتی حجم تو از حدود 10% سطح سفارشهای منتظر بیشتر شود، شبیهسازی باید واکنش بقیه معاملهگران به حضورت را در نظر بگیرد؛ راستش در آن مرحله، paper trading را به هر شبیهسازیای که من یا تو بتوانیم بنویسیم ترجیح میدهم.
آن را با شبیهسازی صف دوباره اجرا کن و جدول mark-out را به تفکیک نوع پرشدن برایم بفرست. اگر دسته برگشت هنوز بیشتر edge را میسازد و دسته عبور کامل همهاش را نمیبلعد، شاید چیزی داشته باشی که ارزش بررسی در paper را دارد. اگر کل ماجرا همان 2,900 پرشدنی بود که هرگز نصیبت نمیشد، بهتر است همین حالا بفهمی تا اینکه 4 هفته حساب paper را تماشا کنی و ببینی کاری را که نوتبوک وعده داده بود انجام نمیدهد.
← همهٔ مطالب


