آپ نے پچھلے ہفتے notebook بھیجا تھا: وہی signal، وہی universe، BTCUSDT perp data کے وہی 14 مہینے۔ صرف execution بدلا تھا۔ آپ نے spread عبور کرنا چھوڑا اور ایک tick اندر bids رکھنا شروع کیں، تو Sharpe 0.42 سے بڑھ کر 2.14 ہو گیا۔ آپ نے پوچھا کہ کیا یہ واقعی درست ہے یا آپ سے کچھ ٹوٹ گیا ہے۔
آپ سے کچھ ٹوٹ گیا ہے۔ میں آپ کو ٹھیک ٹھیک بتانا چاہتا ہوں کہ خرابی کہاں ہے، کیونکہ bug ایک سطر کا ہے اور اس کے پیچھے سبق اس سطر سے کہیں بڑا ہے۔
یہ رہا آپ کا fill rule، آپ کے engine سے نقل کیا ہوا:
if bar.low <= limit_price: fill(limit_price)
اس کا مطلب ہے: اگر اس ایک منٹ کے دوران market نے میری قیمت پر یا اس سے نیچے trade کیا، تو میرا order میری قیمت پر بھر گیا۔ اصل میں یہ صرف یہ بتاتا ہے کہ قیمت آپ کی سطح کو چھو گئی۔ چھونا fill ہونا نہیں ہے۔ ان دونوں کے بیچ ایک queue ہے، اور آپ نے ماڈل میں queue کو خالی فرض کیا ہے۔
آپ کے آگے کون کھڑا ہے
جب آپ BTCUSDT perp پر 84,120.0 کی bid لگاتے ہیں تو پہلے سے وہاں موجود تمام resting orders کے پیچھے لگتے ہیں۔ اس contract کے top-of-book level پر عموماً وقت کے لحاظ سے 4 سے 30 BTC تک ہوتے ہیں؛ آپ کے sample window میں median تقریباً 12 BTC ہے۔ آپ کا order 0.4 BTC کا ہے۔ آپ کی trade ہونے کے لیے sellers کو اتنی مجموعی مقدار کے ساتھ اس level پر hit کرنا ہوگا کہ آپ سے پہلے آنے والے سب clear ہو جائیں، اور یہ اس سے پہلے ہونا چاہیے کہ level آپ کے نیچے سے cancel ہو جائے یا market اوپر نکل جائے۔
لہٰذا آپ کے backtest کو یہ نہیں پوچھنا چاہیے کہ "کیا قیمت 84,120.0 تک پہنچی؟" بلکہ یہ کہ "کیا میرا order وہاں موجود رہتے ہوئے کم از کم 12.4 BTC کی market sells نے 84,120.0 پر execute کیا؟" یہ دونوں بالکل مختلف واقعات ہیں۔ آپ کے data میں ان کا فرق تقریباً تین گنا ہے۔
تین fill rules، تین مختلف strategies
میں نے آپ کے signal کو انہی entries اور تین execution models کے ساتھ دوبارہ چلایا۔ وہی alpha، وہی fees، وہی funding۔ صرف fill logic بدلی۔
| Fill rule | Fills | fill کے بعد اوسط 60s edge | Sharpe |
|---|---|---|---|
Touch: low <= limit | 4,180 | +2.6 bp | 2.14 |
Strict penetration: low < limit - 1 tick | 1,712 | +0.4 bp | 0.61 |
| Volume-at-level queue sim | 1,306 | +0.9 bp | 0.77 |
درمیانی قطار وہ سادہ حل ہے جس کی طرف سب سے پہلے ہاتھ جاتا ہے: fill تبھی شمار کریں جب market آپ کی قیمت سے سختی سے آگے trade کرے، یہ سوچ کر کہ اگر قیمت آگے نکل گئی تو اس نے لازماً آپ کا order بھی کھا لیا ہوگا۔ سمت کے لحاظ سے یہ درست ہے اور زیادہ تر خیالی نتائج ختم کر دیتا ہے۔ لیکن اس میں بھی ایک بُری قسم کا bias ہے، جس پر آگے بات کروں گا۔
آخری قطار وہ ہے جسے آپ کو بنانا چاہیے۔ اس کے لیے L3 feed یا ہر order کی الگ الگ reconstruction درکار نہیں۔ اس کا بیشتر حصہ آپ کے پاس پہلے سے موجود ہے۔
aggTrades سے بنائی جا سکنے والی queue sim
klines کے بجائے aggregate trade stream حاصل کریں۔ ہر print میں price، quantity، timestamp اور maker-side flag ہوتا ہے، جو بتاتا ہے کہ aggressor خرید رہا تھا یا فروخت۔ آپ کے اپنے passive order کی قابلِ عمل simulation کے لیے یہ کافی ہے:
- جب آپ کا order لگے تو اپنی قیمت پر موجود resting size کا snapshot لیں۔ اگر آپ کے پاس صرف 100ms book feed ہے تو آخری snapshot استعمال کریں؛ جو غلطی ہوگی وہ اس غلطی کے مقابلے میں معمولی ہے جسے آپ درست کر رہے ہیں۔
queue_ahead = resting_sizeمقرر کریں۔ احتیاطاً فرض کریں کہ آپ سب سے آخر میں ہیں۔ آپ یہی ہوں گے، الا یہ کہ level آپ نے خود بنایا ہو۔- Trade stream پر چلیں۔ آپ کی قیمت پر یا اس سے کم ہر sell-aggressor print،
queue_aheadکو اپنی quantity کے برابر کم کرے گا۔ جب یہ منفی ہو جائے تو اسی timestamp پر آپ کا order آپ کی قیمت پر fill ہو جائے گا۔ - اگر قیمت ایک tick دور چلی جائے تو queue کو صفر پر reset نہ کریں۔ اسے بتدریج کم کریں۔ level باسی ہونے پر آپ سے آگے موجود کچھ orders cancel ہو جاتے ہیں، کچھ نہیں۔ چھونے سے دور رہنے کے ہر مکمل second پر 30-40% کمی نے میری reconstructions سے دونوں انتہاؤں کی نسبت بہتر مطابقت دکھائی۔
- اگر آپ کی strategy order cancel کر کے دوبارہ لگاتی ہے تو نئی جگہ پر repost کو نئے level کے آخر میں ایک نیا order سمجھ کر ماڈل کریں۔ یہی وہ قدم ہے جسے لوگ چھوڑ دیتے ہیں، اور اسی جگہ باقی خیالی نتیجہ چھپا ہوتا ہے۔
مرحلہ 2 پر آپ مجھ سے اختلاف کرنا چاہیں گے۔ ہاں، کبھی کبھی آپ queue کے آگے کے قریب ہوتے ہیں، کیونکہ آپ نے level بنتے ہی order لگا دیا۔ ٹھیک ہے — اسے ریکارڈ کریں، فرض نہ کریں۔ order لگاتے وقت resting size log کریں اور data سے معلوم کریں کہ آپ کے کتنے orders واقعی شروع ہی میں پہنچے۔ آپ کی strategy میں یہ 11% تھا، کیونکہ آپ کا signal ایک move کے بعد آتا ہے، یعنی جس level میں آپ شامل ہو رہے ہیں وہ پہلے سے قائم ہے اور وہاں لوگوں کا ہجوم بھی موجود ہے۔
آپ کو جو fills ملتے ہیں، وہی ہیں جن کے نہ ملنے کی آپ خواہش کرتے
اب اصل اہم بات، اور یہ وجہ کہ strict-penetration rule میں bias ہے۔
سوچیں کہ آپ کی bid پوری کب consume ہوتی ہے۔ ایسا تب ہوتا ہے جب sell pressure اتنا زیادہ ہو کہ پورا level کھا جائے۔ یعنی تعریف ہی کے مطابق، اسی وقت market آپ کی قیمت سے نیچے جا رہی ہوتی ہے۔ جن fills کا آپ کو سب سے زیادہ یقین ہوتا ہے، انہی میں آپ فوراً نقصان والی سمت میں ہوتے ہیں۔
اپنے fills کو ان کے وقوعے کے طریقے کے لحاظ سے بانٹیں اور 60-second mark-out ناپیں:
| Fill کی قسم | Fills کا حصہ | 60s پر mark-out |
|---|---|---|
| Level پر trade ہوئی، قیمت اچھل کر اوپر گئی | 38% | +3.1 bp |
| Level پر trade ہوئی، قیمت مستحکم رہی | 21% | +0.2 bp |
| قیمت 2+ ticks آگے trade ہوئی | 41% | −2.4 bp |
آپ کے سادہ backtest نے تینوں اقسام کے fills دیے اور سب کو مفت سمجھا۔ strict-penetration rule تقریباً صرف تیسری قسم کے fills دیتا ہے، اسی لیے اس کا edge queue sim سے بھی زیادہ گر گیا۔ دونوں درست نہیں۔ queue sim آپ کو ایک حقیقت پسندانہ آمیزہ دیتا ہے، اور سارا کھیل اسی آمیزے کا ہے: passive execution آپ کو spread دلاتا ہے اور adverse selection اس کا بدلہ لیتی ہے؛ ان دونوں کا تناسب ہی آپ کی اصل strategy ہے۔
Equities desk کی پرانی تشبیہ آج بھی درست ہے: maker order ایک free option ہے جو آپ market کو لکھ کر دیتے ہیں۔ کوئی اسے تب exercise کرتا ہے جب ایسا کرنا اس کے فائدے میں ہو۔ آپ کا backtest premium تو جمع کر رہا تھا مگر یہ بھول گیا کہ option کی ایک ادائیگی بھی ہوتی ہے۔
جو trades آپ کو نہیں ملیں، وہ صرف لاگت نہیں بلکہ پوری strategy بدل دیتی ہیں
یہ وہ نکتہ ہے جس پر میں چاہتا ہوں کہ آپ سب سے زیادہ غور کریں۔ جب آپ taker execution کو غلط ماڈل کرتے ہیں تو درست trades غلط قیمت پر ملتی ہیں، اور fees کی اصلاح سے مسئلے کا بیشتر حصہ ٹھیک ہو جاتا ہے۔ جب maker execution کو غلط ماڈل کرتے ہیں تو آپ کو بالکل غلط trades ملتی ہیں۔ آپ کی 4,180 entries میں سے تقریباً 2,900 کبھی ہوئیں ہی نہیں۔ ان میں کچھ آپ کے بہترین signals تھے، ایسی candles پر جو تیزی سے اوپر گئیں اور پلٹ آئیں؛ بالکل اسی صورت میں market آپ کے بغیر آگے نکل گئی۔
لہٰذا آپ کی unfilled شاخ میں حقیقی logic درکار ہے۔ جب signal stale ہو جائے اور entry fill نہ ہوئی ہو تو strategy کیا کرے گی؟ spread اور impact ادا کر کے taker order سے پیچھا کرے؟ کم قیمت پر دوبارہ order لگائے اور entry کی مختلف بنیاد قبول کرے؟ trade چھوڑ دے اور flat رہے؟ ہر انتخاب سے equity curve نمایاں طور پر بدلتی ہے، اور ان میں سے کوئی بھی "فرض کر لو کہ fill ہو گیا تھا" نہیں ہے۔ ہماری runs میں، ایک حقیقت پسندانہ chase rule شامل کرنے (20 seconds تک fill نہ ہو تو cross کرنا، slippage کی حد 3 bp) سے چھوٹی ہوئی trades کا تقریباً ایک تہائی واپس ملا اور naive اور queue-sim Sharpe کے فرق کا تقریباً نصف۔ یہ واقعی دلچسپ نتیجہ ہے، اور صرف اسی وقت سامنے آتا ہے جب fill model اتنا حقیقی ہو کہ سوال کے معنی بنیں۔
آسان جانچ، دس منٹ: اپنی live paper-trading log اور اسی مدت کے backtest کا موازنہ کریں۔ PnL نہیں، fill rate دیکھیں۔ اگر backtest resting orders میں سے 100% fill کرتا ہے اور paper trading میں یہ 34% ہے تو آپ strategies کا موازنہ نہیں کر رہے، آپ کے fill model میں bug ہے۔ ایک بھی return number دیکھنے سے پہلے اسے درست کریں۔
جب بات چل رہی ہے تو دو چھوٹی باتیں اور
Post-only rejects۔ اگر maker fee tier یقینی بنانے کے لیے post-only استعمال کر رہے ہیں اور آپ کے فیصلے اور exchange کے ack کے درمیان book بدل جاتی ہے، تو order resting ہونے کے بجائے reject ہو جاتا ہے۔ ہماری paper logs میں معمول کے اوقات میں BTCUSDT پر 3-6% کوششیں رد ہوئیں، جبکہ US CPI کا اعلان آنے کے ایک منٹ بعد یہ شرح 15% سے زیادہ تھی۔ رد شدہ order نہ filled order ہے نہ ایسا unfilled order جو book پر موجود رہا ہو؛ یہ ایسی trade ہے جو کبھی وجود میں ہی نہیں آئی۔ اگر آپ کے backtest میں یہ حالت شامل نہیں تو آپ کے trade count میں عین انہی ادوار کے دوران مبالغہ ہوگا جو آپ کے لیے سب سے اہم ہیں۔
Self-trade prevention اور آپ کے اپنے footprint کا اثر۔ 0.4 BTC کے ساتھ آپ BTCUSDT کی قیمت نہیں ہلا رہے، اس لیے اسے چھوڑ دیں۔ لیکن آپ نے mid-cap alt perp پر اسے چلانے کا ذکر کیا تھا، جہاں top-of-book کی notional قدر اکثر $15k سے کم ہوتی ہے۔ وہاں آپ کا order queue کا ایک بامعنی حصہ ہے، اور snapshot میں آپ کے آگے queue کی مقدار میں آپ کے آخری order کی موجودگی کا اثر بھی شامل ہے۔ جب آپ کا size resting level کے تقریباً 10% سے زیادہ ہو جائے تو sim کو یہ ماننا ہوگا کہ دوسرے شرکا آپ کی وجہ سے اپنا رویہ بدلتے ہیں۔ اور سچ کہوں تو اس مقام پر میں آپ کے یا اپنے لکھے کسی بھی simulation سے زیادہ paper trading پر بھروسا کروں گا۔
queue sim کے ساتھ دوبارہ چلائیں اور fill type کے لحاظ سے تقسیم شدہ mark-out table مجھے بھیجیں۔ اگر bounce bucket اب بھی زیادہ تر edge دے رہی ہے اور through bucket اسے پورا نہیں کھا رہی تو شاید آپ کے پاس paper trading میں آزمانے کے قابل کچھ ہے۔ اگر یہ سارا نتیجہ ان 2,900 fills کی وجہ سے تھا جو آپ کو کبھی مل ہی نہیں سکتے تھے تو بہتر ہے یہ بات ابھی معلوم ہو جائے، اس سے پہلے کہ چار ہفتے paper account کو notebook کے وعدے کے برخلاف چلتا دیکھتے رہیں۔
← تمام پوسٹس


