مسیر Quant Developer · بخش ۱: مبانی بازار و Market Making · درس ۳
Order Book و Matching Engine
سفارش Limit و Market چه فرقی دارند، صرافی سفارشها را با چه قاعدهای به هم میرساند، و چرا جایگاه در صف روی پر شدن سفارش اثر میگذارد.
۱. دفتر سفارش دوباره، این بار از نزدیک
در درس اول دیدیم دفتر سفارش (Order Book) فهرست سفارشهایی است که هنوز پر نشدهاند. در این درس همان دفتر سفارش BTC/USDT را مبنا میگیریم:
هر ردیف یک سطح قیمت است و حجم آن، جمع همه سفارشهایی است که در همان قیمت منتظرند. پشت عدد 0.4 در قیمت 99,990 ممکن است یک سفارش باشد یا ده سفارش از ده نفر مختلف. این نکته در بخش ۴ مهم میشود.
به مجموع حجم سطوح مختلف عمق بازار (Market Depth) میگویند. Best Bid و Best Ask فقط بالای دفتر را نشان میدهند؛ عمق نشان میدهد اگر سفارش بزرگتری بیاید، قیمت تا کجا جابهجا میشود.
۲. سفارش Limit و سفارش Market
- سفارش Limit: «حداکثر این قیمت را برای خرید میپردازم» یا «حداقل این قیمت را برای فروش میخواهم». اگر طرف مقابلی با این قیمت یا بهتر وجود نداشته باشد، سفارش در دفتر مینشیند و منتظر میماند.
- سفارش Market: «همین الان با هر قیمتی که هست معامله کن». این سفارش در دفتر نمینشیند؛ سطح به سطح سفارشهای طرف مقابل را مصرف میکند تا حجمش کامل شود.
سفارش Limit کنترل قیمت را به تو میدهد ولی پر شدنش تضمینی نیست. سفارش Market پر شدن را تقریباً تضمین میکند ولی قیمتش نه.
یک سفارش Market بزرگ چه میکند؟
فرض کن کسی سفارش خرید Market به حجم 1 BTC ثبت کند. موتور معاملات از پایینترین Ask شروع میکند:
| سطح قیمت | حجم مصرفشده | ارزش (USDT) |
|---|---|---|
| 100,010 | 0.3 | 30,003 |
| 100,020 | 0.5 | 50,010 |
| 100,030 | 0.2 از 0.9 | 20,006 |
| جمع | 1.0 | 100,019 |
خریدار انتظار داشت با Best Ask یعنی 100,010 بخرد، اما میانگین قیمتش 100,019 شد. این ۹ دلار اختلاف به ازای هر بیتکوین، اسلیپیج (Slippage) است و دلیلش این بود که بهترین سطح فقط 0.3 BTC حجم داشت. اسلیپیج را در درس بعد دقیقتر بررسی میکنیم.
بعد از این معامله، دو سطح اول Ask خالی شدهاند و Best Ask به 100,030 با حجم 0.7 رسیده است. یعنی Spread از ۲۰ به ۴۰ دلار رسید؛ سفارش بزرگ، شکل بازار را هم تغییر میدهد.
سفارش Limit که فوراً معامله میشود
سفارش Limit همیشه منتظر نمیماند. اگر کسی سفارش خرید Limit با قیمت 100,020 و حجم 1 BTC ثبت کند:
- 0.3 BTC در 100,010 فوراً معامله میشود (قیمت بهتر از سقف خریدار).
- 0.5 BTC در 100,020 فوراً معامله میشود.
- سطح بعدی 100,030 است که از سقف خریدار بالاتر است؛ پس معامله متوقف میشود.
- 0.2 BTC باقیمانده با قیمت 100,020 در دفتر مینشیند و حالا Best Bid جدید است.
به این حالت پر شدن جزئی (Partial Fill) میگویند: بخشی از سفارش اجرا شده و بقیه در دفتر منتظر است. سیستم معاملاتی باید هر دو بخش را جداگانه پیگیری کند.
۳. Maker و Taker
صرافیها دو طرف هر معامله را از هم جدا میکنند:
- Maker: سفارشی که در دفتر نشسته بود و نقدشوندگی فراهم کرده بود.
- Taker: سفارشی که آمد و آن نقدشوندگی را برداشت.
سفارش Market همیشه Taker است. سفارش Limit بسته به قیمتش میتواند هر کدام باشد؛ در مثال قبل، 0.8 BTC اول Taker بود و 0.2 BTC باقیمانده اگر بعداً پر شود، Maker حساب میشود.
این تفاوت مهم است چون کارمزد Maker و Taker معمولاً فرق دارد و گاهی صرافی به Maker ریبیت هم میدهد. بازارساز تقریباً همیشه میخواهد Maker باشد. به همین دلیل بیشتر صرافیها گزینههایی مثل این دارند:
| نوع سفارش | رفتار |
|---|---|
| Post-Only | اگر سفارش فوراً معامله شود، صرافی آن را رد یا لغو میکند؛ تضمین میکند Maker بمانی |
| IOC (Immediate or Cancel) | هر چه فوراً پر شد پر شود، باقیمانده لغو شود |
| FOK (Fill or Kill) | یا کل حجم فوراً پر شود یا هیچچیز |
جزئیات کارمزد و اثرش روی سود و زیان را در درس ۷ بررسی میکنیم.
۴. اولویت قیمت و زمان
وقتی یک سفارش Taker میرسد، موتور معاملات (Matching Engine) باید تصمیم بگیرد با کدام سفارشهای منتظر معامله کند. رایجترین قاعده Price-Time Priority است که به آن FIFO هم میگویند:
- اول قیمت: سفارشی که قیمت بهتری داده جلوتر است (برای خرید: قیمت بالاتر، برای فروش: قیمت پایینتر).
- بعد زمان: بین سفارشهای همقیمت، هر کس زودتر آمده جلوتر است.
یک مثال از صف
پشت سطح 99,990 با حجم 0.4 این سه سفارش منتظرند:
| ترتیب در صف | سفارش | حجم | زمان ثبت |
|---|---|---|---|
| ۱ | A | 0.1 | 09:00:01 |
| ۲ | B | 0.2 | 09:00:03 |
| ۳ | C | 0.1 | 09:00:05 |
تو در 09:00:07 یک سفارش خرید 0.1 BTC در همین قیمت ثبت میکنی. سفارشت چهارم صف است و 0.4 BTC جلوی تو قرار دارد.
- یک فروش Market به حجم 0.3 میآید: A و B کامل پر میشوند. تو هنوز منتظری.
- یک فروش Market دیگر به حجم 0.25 میآید: اول C (0.1)، بعد تو (0.1)، و 0.05 باقیمانده به سطح 99,980 میرود.
سفارش تو فقط وقتی پر شد که ۰.۵۵ BTC فروش Market به این قیمت رسید. اگر جریان فروش زودتر قطع میشد، هرگز پر نمیشدی؛ با اینکه قیمتت همان Best Bid بود.
راه میانبر: بهتر کردن قیمت
اگر بهجای 99,990 در 99,991 خرید ثبت کنی، Best Bid جدید و نفر اول صف خودت هستی. اما بهایش این است که Spread تو یک دلار کوچکتر میشود و اگر همه همین کار را بکنند، Spread بازار به حداقل خودش (یک Tick) میرسد. Tick Size کوچکترین گام مجاز قیمت در هر بازار است و تعیین میکند این رقابت چقدر میتواند پیش برود.
همه صرافیها FIFO نیستند. بعضی بازارهای مشتقات از Pro-Rata استفاده میکنند: حجم Taker به نسبت اندازه سفارشها بین همه سفارشهای همقیمت تقسیم میشود. در آن بازارها، اندازه سفارش از سرعت مهمتر است.
۵. Matching Engine به زبان کد
هسته یک موتور معاملات ساده در چند ده خط جا میشود. نسخه زیر فقط برای آموزش است و قاعده Price-Time Priority را پیاده میکند:
from collections import deque
from dataclasses import dataclass
@dataclass
class Order:
id: str
side: str # "buy" or "sell"
price: int # in ticks, e.g. 99990
qty: int # in lots, e.g. 1 lot = 0.001 BTC
class OrderBook:
def __init__(self):
self.bids: dict[int, deque[Order]] = {} # price -> queue of resting buys
self.asks: dict[int, deque[Order]] = {} # price -> queue of resting sells
def submit(self, order: Order) -> list[tuple[str, int, int]]:
"""Match a limit order against the book; any remainder rests in the book."""
fills = []
if order.side == "buy":
opposite, own = self.asks, self.bids
crosses = lambda p: p <= order.price
best_price = min
else:
opposite, own = self.bids, self.asks
crosses = lambda p: p >= order.price
best_price = max
while order.qty > 0 and opposite:
best = best_price(opposite)
if not crosses(best):
break
queue = opposite[best]
resting = queue[0] # oldest order at the best price
traded = min(order.qty, resting.qty)
fills.append((resting.id, best, traded)) # trades at the resting price
order.qty -= traded
resting.qty -= traded
if resting.qty == 0:
queue.popleft()
if not queue:
del opposite[best]
if order.qty > 0:
own.setdefault(order.price, deque()).append(order)
return fills
چند نکته که در همین کد کوتاه پنهان است:
- قیمت و حجم عدد صحیحاند. صرافیهای واقعی قیمت را بر حسب Tick و حجم را بر حسب Lot نگه میدارند. با اعداد اعشاری، 0.1 + 0.2 دقیقاً 0.3 نمیشود و سفارشی با حجم 0.00000000001 در دفتر جا میماند.
- قیمت معامله، قیمت سفارش منتظر است (
best)، نه قیمت سفارش تازهوارد. - سفارش Market را میتوان یک سفارش Limit با قیمت بینهایت تهاجمی دید که باقیماندهاش بهجای نشستن در دفتر لغو میشود.
- کارایی: پیدا کردن بهترین قیمت با
minروی همه سطوح کند است. موتورهای واقعی از ساختارهای مرتب (درخت یا آرایه بر حسب Tick) استفاده میکنند. لغو سفارش وسط صف هم ساختار جداگانهای لازم دارد که اینجا نیامده.
در درس ۱۶ همین منطق را برای بازسازی دفتر سفارش از داده بازار به کار میبریم، و در درس ۳۰ هسته Backtester ما میشود.
۶. تمرین این درس
گزینه مناسب هر سؤال را انتخاب کن و در پایان نتیجه را ببین. دفتر سفارش همان دفتر بخش ۱ است.
درس بعدی
در درس چهارم از دفتر سفارش ایستا فاصله میگیریم و به Market Microstructure میرسیم: جریان سفارشها در طول زمان چه شکلی دارد، اسلیپیج و Market Impact را چطور اندازه بگیریم، و چه عواملی کیفیت اجرای یک معامله را تعیین میکنند.
این مطلب صرفاً آموزشی است و توصیه خرید، فروش یا سرمایهگذاری نیست.