مسیر Quant Developer · بخش ۳: مدلهای ریاضی و Quantitative Finance · درس ۱۲
مدل Guéant–Lehalle–Fernandez-Tapia
مدلی که موجودی بازارساز را بین دو سقف نگه میدارد، مسئله Avellaneda–Stoikov را دقیق حل میکند و فرمولهای تقریبی سادهای میدهد که به افق زمانی وابسته نیستند.
۱. سه مشکلی که از درس قبل ماند
مدل Avellaneda–Stoikov در درس ۱۱ اسکلت درستی داشت، ولی در بخش محدودیتها سه مشکل جدی دیدیم:
- سقفی برای موجودی نبود. با موجودی بزرگ، Skew آنقدر بزرگ میشد که Ask پایینتر از Mid میافتاد.
- افق T مصنوعی بود. Skew و Spread به زمان باقیمانده تا یک پایان فرضی وابسته بودند؛ چیزی که در بازار ۲۴ ساعته معنی روشنی ندارد.
- فرمول Spread تقریبی بود و از بسط بر حسب q به دست آمده بود.
اولیویه گِئان، شارلآلبر لوال و خواکین فرناندستاپیا در مقالهای با عنوان Dealing with the inventory risk: a solution to the market making problem (منتشرشده در سال ۲۰۱۳ در Mathematics and Financial Economics) هر سه را حل کردند. این مدل را معمولاً با حروف اول نام نویسندگان، GLFT، میشناسند.
۲. همان فرضها، بهاضافه یک سقف
فرضهای GLFT همان فرضهای درس قبل است:
- Mid با نوسان σ و بدون روند حرکت میکند (S(t) = S(0) + σW(t)).
- بازارساز مطلوبیت نمایی با ریسکگریزی γ دارد.
- نرخ پر شدن سفارشی به فاصله δ از Mid برابر A·e^(−kδ) است.
- هر Fill یک واحد است.
و یک فرض تازه: موجودی بین Q منفی و Q مثبت میماند. وقتی موجودی به Q میرسد، بازارساز دیگر Bid نمیگذارد؛ و وقتی به Q منفی میرسد، دیگر Ask نمیگذارد.
نمادگذاری مقاله را نگه میداریم: δb فاصله Bid تا Mid و δa فاصله Ask تا Mid است:
q بر حسب تعداد واحد سفارش است. اگر اندازه هر سفارش 0.01 BTC باشد، q برابر 5 یعنی 0.05 BTC موجودی.
۳. حل دقیق: یک ضرب ماتریس
مسئله بهینهسازی به یک معادله دیفرانسیل غیرخطی میرسد (معادله Hamilton–Jacobi–Bellman). نکته کلیدی مقاله این است که با یک تغییر متغیر، این معادله به یک دستگاه خطی تبدیل میشود که جوابش با یک نمای ماتریسی به دست میآید. دو ثابت تعریف کن:
ماتریس M یک ماتریس سهقطری با 2Q + 1 سطر است (یک سطر برای هر موجودی ممکن): روی قطر اصلی α × q² و روی دو قطر کناری η با علامت منفی. بردار v اینطور به دست میآید:
و قیمتهای بهینه از نسبت درایههای همسایه v:
لازم نیست اثبات را دنبال کنی. نکته مهم این است که جواب دقیق مسئله با چند خط numpy حساب میشود و دیگر تقریب درس قبل لازم نیست. در پایان افق (t برابر T) همه درایههای v برابر 1 میشوند و هر دو فاصله به (1/γ)·ln(1 + γ/k) میرسند.
۴. فرمولهای تقریبی حالت ایستا
اگر افق باقیمانده طولانی باشد، قیمتهای بهینه تقریباً به زمان وابسته نیستند؛ فقط در نزدیکی T تغییر میکنند. این همان چیزی است که برای بازار ۲۴ ساعته میخواهیم. مقاله برای این حالت ایستا (وقتی T − t به بینهایت میل میکند) این تقریبها را میدهد:
برای خوانایی، جمله زیر رادیکال را C بنامیم:
با جمع و تفریق دو فرمول، ساختار آشنای درس قبل بیرون میآید:
حالا کنار Avellaneda–Stoikov بگذارشان:
| Avellaneda–Stoikov | GLFT (حالت ایستا) | |
|---|---|---|
| جابهجایی مرکز به ازای هر واحد موجودی | γσ²(T − t) | C |
| Spread | γσ²(T − t) + (2/γ)·ln(1 + γ/k) | C + (2/γ)·ln(1 + γ/k) |
| وابستگی به افق T | دارد | ندارد |
| وابستگی به A | ندارد | دارد |
جمله نقدشوندگی، (2/γ)·ln(1 + γ/k)، در هر دو یکی است. تفاوت در جمله ریسک است. در AS این جمله از ساعت میآید: چقدر تا پایان افق مانده. در GLFT از بازار میآید: C با √A نسبت عکس دارد. شهودش این است که در بازار فعالتر (A بزرگتر) موجودی را سریعتر میتوانی تخلیه کنی، پس لازم نیست برای هر واحد موجودی اینقدر قیمتها را جابهجا کنی.
یک سادهسازی کمکی: وقتی γ در برابر k کوچک است، (1 + γ/k)^(1 + k/γ) تقریباً e (حدود 2.718) میشود و:
یعنی Skew در GLFT با σ خطی رشد میکند، نه با σ² مثل AS.
۵. کد: قیمتها بر حسب موجودی
برای مقایسه با درس قبل، همان پارامترهای مثال Avellaneda–Stoikov را نگه میداریم (σ برابر 2، γ برابر 0.1، k برابر 1.5، A برابر 140، T برابر 1) و سقف Q را 10 میگذاریم. سه چیز حساب میکنیم: جواب دقیق GLFT در لحظه شروع، تقریب ایستای GLFT، و قیمتهای AS در لحظه شروع. همه بهصورت فاصله از Mid:
import numpy as np
# Same units as lesson 11 (Avellaneda-Stoikov example), plus an inventory bound Q
sigma, gamma, k, A = 2.0, 0.1, 1.5, 140.0
Q, T = 10, 1.0
base = np.log(1 + gamma / k) / gamma # (1/γ)·ln(1 + γ/k)
# --- GLFT asymptotic (stationary) approximation ---
c = np.sqrt(sigma**2 * gamma / (2 * k * A) * (1 + gamma / k) ** (1 + k / gamma))
def glft_approx(q):
d_bid = base + (2 * q + 1) / 2 * c
d_ask = base - (2 * q - 1) / 2 * c
return d_bid, d_ask
# --- GLFT exact solution at time t (finite horizon T, bound Q) ---
alpha = k / 2 * gamma * sigma**2
eta = A * (1 + gamma / k) ** (-(1 + k / gamma))
qs = np.arange(Q, -Q - 1, -1) # Q, Q-1, ..., -Q
M = np.diag(alpha * qs**2) - eta * (np.eye(len(qs), k=1) + np.eye(len(qs), k=-1))
def glft_exact(t):
w, V = np.linalg.eigh(M) # M is symmetric
v = V @ (np.exp(-w * (T - t)) * (V.T @ np.ones(len(qs))))
v = dict(zip(qs, v))
d_bid = {q: np.log(v[q] / v[q + 1]) / k + base for q in qs if q < Q}
d_ask = {q: np.log(v[q] / v[q - 1]) / k + base for q in qs if q > -Q}
return d_bid, d_ask
# --- Avellaneda-Stoikov quotes, as distances from mid ---
def as_quotes(q, tau):
half = (gamma * sigma**2 * tau + 2 * np.log(1 + gamma / k) / gamma) / 2
skew = q * gamma * sigma**2 * tau # s - r
return half + skew, half - skew
eb, ea = glft_exact(0.0)
print(" q | GLFT exact bid ask | GLFT approx bid ask | AS(T-t=1) bid ask")
for q in [-10, -9, -5, -2, 0, 2, 5, 9, 10]:
ab, aa = glft_approx(q)
sb, sa = as_quotes(q, 1.0)
b = f"{eb[q]:6.3f}" if q in eb else " - "
a = f"{ea[q]:6.3f}" if q in ea else " - "
print(f"{q:3d} | {b} {a} | {ab:6.3f} {aa:6.3f} | {sb:6.3f} {sa:6.3f}")
ماتریس M متقارن است، پس بهجای نمای ماتریسی از تجزیه مقدار ویژه (eigh) استفاده کردهایم و فقط numpy لازم است. خروجی:
q | GLFT exact bid ask | GLFT approx bid ask | AS(T-t=1) bid ask
-10 | 0.028 - | 0.154 1.188 | -3.155 4.845
-9 | 0.179 1.263 | 0.206 1.137 | -2.755 4.445
-5 | 0.410 0.933 | 0.413 0.930 | -1.155 2.845
-2 | 0.567 0.776 | 0.568 0.775 | 0.045 1.645
0 | 0.672 0.672 | 0.671 0.671 | 0.845 0.845
2 | 0.776 0.567 | 0.775 0.568 | 1.645 0.045
5 | 0.933 0.410 | 0.930 0.413 | 2.845 -1.155
9 | 1.263 0.179 | 1.137 0.206 | 4.445 -2.755
10 | - 0.028 | 1.188 0.154 | 4.845 -3.155
در این پارامترها C حدود 0.052 و (1/γ)·ln(1 + γ/k) حدود 0.645 است. از این جدول چند چیز یاد میگیریم:
- تقریب در میانه بازه عالی است. از q برابر 5 منفی تا 5 مثبت، جواب دقیق و تقریب تا حدود سه هزارم یکیاند. Spread دقیق در q برابر صفر 1.343 است، تقریباً همان 0.645 × 2 + 0.052.
- نزدیک سقف، جواب دقیق محتاطتر است. در q برابر 9، مدل دقیق Bid را 1.263 دور از Mid میگذارد (تقریب: 1.137)؛ یعنی نزدیک سقف، تمایلش به خرید بیشتر خیلی کمتر از پیشبینی خطی است. در q برابر 10 اصلاً Bid نمیگذارد.
- در سقف مقابل، تهاجمی است. در q برابر 10 منفی، Ask حذف شده و Bid فقط 0.028 با Mid فاصله دارد؛ مدل با جدیت میخواهد از سقف دور شود.
- AS با این افق خیلی تهاجمیتر است. در AS با T − t برابر 1، جابهجایی مرکز به ازای هر واحد موجودی γσ²(T − t) = 0.4 است، حدود هشت برابر C. از q برابر 3 به بعد، فاصله Ask منفی میشود؛ یعنی Ask پایینتر از Mid، همان مشکلی که در درس ۱۱ دیدیم.
یک راه شهودی برای دیدن رابطه دو مدل: Skew مدل AS وقتی با C برابر میشود که T − t برابر C / (γσ²) باشد، که اینجا حدود 0.13 است. میتوانی GLFT ایستا را مثل AS با یک «افق مؤثر» ببینی که بهجای ساعت، از سرعت تخلیه موجودی در بازار میآید. این تعبیر فقط برای شهود است و جزو نتایج مقاله نیست.
۶. از فرمول تا سیستم
در عمل، فرمولهای تقریبی بخش ۴ به خاطر سادگی پرکاربردند. پیادهسازی معمولاً این شکل را دارد:
- تخمین ورودیها: σ از بازدههای اخیر Mid (درس ۹)، A و k از برازش نرخ پر شدن بر حسب فاصله از Mid (درس ۱۸)، و Q از محدودیت ریسک و سرمایه (درس ۲۴). γ عملاً یک پارامتر تنظیمی است.
- محاسبه δb و δa با موجودی فعلی، بر حسب واحد سفارش.
- تبدیل به قیمت: bid = s − δb و ask = s + δa، گرد کردن به Tick (Bid رو به پایین، Ask رو به بالا) و ارسال با Post-Only (درس ۳).
- بررسیهای ایمنی: اگر یک طرف از Best طرف مقابل عبور کرد یا موجودی به سقف رسید، آن طرف را نگذار.
۷. محدودیتها
GLFT مشکل سقف موجودی و افق را حل میکند، ولی بقیه فرضهای AS سر جایشاناند:
- σ، A و k ثابتاند. هر سه در بازار واقعی با زمان تغییر میکنند و باید مدام دوباره تخمین زده شوند.
- قیمت مستقل از جریان سفارش است. Adverse Selection (درس ۸) و پیشبینی کوتاهمدت مثل عدمتوازن دفتر سفارش (درس ۱۰) در مدل پایه نیستند.
- شکل نمایی λ(δ) یک فرض است، نه واقعیت؛ نرخ پر شدن واقعی به جایگاه در صف (درس ۳) هم بستگی دارد.
- Fill یکواحدی، بدون کارمزد، Tick و تأخیر.
پژوهشهای بعدی، از جمله کتاب The Financial Mathematics of Market Liquidity از خود گِئان، این چارچوب را به توابع نرخ پر شدن عمومیتر و حالتهای واقعیتر گسترش دادهاند. ولی اسکلت همان است که در این دو درس دیدیم: یک جمله نقدشوندگی، یک جمله ریسک، و Skew متناسب با موجودی.
۸. تمرین این درس
درس بعدی
با این درس بخش سوم تمام میشود. کدهای این بخش اسکریپتهای کوتاهی بودند که فقط یک بار اجرا میشدند. در درس سیزدهم، اولین درس بخش چهارم (داده و زیرساخت تحلیلی)، به Python برای Quant میرسیم: ساختار دادن به کد تحقیقاتی، استفاده درست از NumPy و pandas، مدیریت خطا، و نوشتن تست؛ مثلاً تستی که مطمئن شود تابع قیمتگذاری GLFT در موجودی صفر قیمتهای متقارن میدهد و در سقف موجودی هیچوقت Bid نمیگذارد.
این مطلب صرفاً آموزشی است و توصیه خرید، فروش یا سرمایهگذاری نیست.