مسیر Quant Developer · بخش ۸: پروژه عملی و آمادگی استخدام · درس ۳۱
تحلیل عملکرد: Markout، تفکیک PnL و اختلاف بکتست با اجرای واقعی
کالبدشکافی خروجی Backtester: تفکیک PnL، منحنی Markout، آزمودن تصمیمهای طراحی، جاروب پارامتر و خطر Overfitting، و فاصله بکتست تا اجرای واقعی.
۱. یک عدد کافی نیست
Backtester درس ۳۰ گفت طراحی درس ۲۹ در یک ساعت بازار مصنوعی 9.12 دلار سود خالص داشته است. این عدد بهتنهایی تقریباً هیچچیز نمیگوید. سؤالهای واقعی اینها هستند:
- سود از کجا آمده و کجا از دست رفته؟
- چقدر از پر شدنها سمی بودهاند؟
- تصمیمهای طراحی (مثلاً Microprice) واقعاً کمک کردهاند؟
- اگر پارامترها را تنظیم کنیم، بهبود واقعی است یا شانس؟
- نتیجه اجرای واقعی چقدر از این عدد فاصله خواهد داشت؟
همه کدهای این درس را در یک فایل یا نوتبوک Jupyter کنار backtest.py و mm_quote.py بگذار و تکهها را به ترتیب اجرا کن. هر تکه از متغیرهای تکه قبلی استفاده میکند. همه اعداد این درس خروجی همین کدها روی همان داده مصنوعیاند.
۲. تفکیک PnL بر اساس نوع پر شدن
در درس ۳۰ هر پر شدن را با یکی از دو برچسب ثبت کردیم: trade (یک سفارش Market به سفارش ما رسید) و cross (قیمت بازار از روی سفارش ما رد شد). اول ببینیم هر گروه چه سهمی از Spread Capture دارد:
import itertools
import numpy as np
import pandas as pd
import backtest as bt
from mm_quote import Params
df, eq, mid_path, res = bt.run() # the lesson-30 baseline
sign = np.where(df.side == "buy", 1, -1)
df["edge"] = sign * (df.mid - df.px) * df.qty # spread capture of each fill, USDT
print(df.groupby("how").agg(fills=("qty", "size"), btc=("qty", "sum"), edge=("edge", "sum")).round(2))
fills btc edge
how
cross 84 0.84 -13.24
trade 1248 11.56 60.05
| نوع | تعداد | حجم (BTC) | Spread Capture (دلار) | به ازای هر BTC |
|---|---|---|---|---|
| trade | 1,248 | 11.56 | 60.05 سود | 5.19 سود |
| cross | 84 | 0.84 | 13.24 زیان | 15.76 زیان |
پر شدنهای cross فقط ۶.۳٪ تعداد پر شدنها هستند، ولی ۲۲٪ از Spread Capture گروه دیگر را پاک کردهاند. هر کدام بهطور متوسط ۳ برابر بیشتر از آنچه یک پر شدن معمولی میسازد، از بین میبرد. اینها لحظههاییاند که قیمت جهش کرده و سفارش ما هنوز سر جایش بوده؛ اولین هدف بهبود، کم کردن همین گروه است (مثلاً با عقب کشیدن سفارشها وقتی σ ناگهان بالا میرود).
۳. منحنی Markout
در درس ۸ تعریف کردیم: Markout یعنی سود هر پر شدن، اگر آن را با Mid چند لحظه بعد ارزشگذاری کنیم. اگر معاملهگرهای طرف ما آگاه باشند، Markout با گذشت زمان افت میکند:
def markout(h_ms):
"""Average PnL per BTC if every fill were valued at the mid h_ms later."""
idx = np.minimum(df.t // bt.STEP_MS + h_ms // bt.STEP_MS, len(mid_path) - 1)
pnl = sign * (mid_path[idx] - df.px) * df.qty
return pnl.sum() / df.qty.sum()
for h in (0, 1_000, 5_000, 30_000):
print(f"markout {h / 1000:>4.0f}s: {markout(h):6.2f} USDT/BTC")
markout 0s: 3.77 USDT/BTC
markout 1s: 3.40 USDT/BTC
markout 5s: 2.34 USDT/BTC
markout 30s: 2.70 USDT/BTC
خواندن این منحنی:
- لحظه صفر (3.77): همان Spread Capture تقسیم بر حجم: 46.81 / 12.405.
- از صفر تا ۵ ثانیه: افت 1.43 دلار به ازای هر BTC. این هزینه Adverse Selection است: در دنیای ما معاملهگرهای آگاه حرکت دو ثانیه بعد را میدانند و قیمت بعد از معامله با ما، علیه ما حرکت میکند.
- بعد از ۵ ثانیه: منحنی دیگر پایین نمیرود. تغییر کوچک ۵ تا ۳۰ ثانیه در حد نویز است (چقدر نویز؟ چند خط پایینتر میبینیم).
حالا حدس درس ۳۰ را میسنجیم. هزینه Adverse Selection در کل حجم:
این از کل زیان موجودی (13.00 دلار) هم بیشتر است. پس حدس درس ۳۰ با داده سازگار است: زیان موجودی عمدتاً از معامله با افراد آگاه آمده، نه از روند ۶۸۰ دلاری قیمت. («سازگار است» و نه «ثابت شد»؛ چند خط پایینتر میبینیم این اعداد چقدر عدمقطعیت دارند.) حالا یک مقایسه مهمتر: کارمزد به ازای هر BTC حدود 1.99 دلار است (24.70 / 12.405). Markout پنجثانیهای منهای کارمزد:
لبه واقعی این استراتژی، بعد از Adverse Selection و کارمزد، حدود ۳۵ سنت برای هر بیتکوین است. هر چیزی که این عدد را جابهجا کند، کل نتیجه را جابهجا میکند.
هر عدد یک خطای استاندارد لازم دارد
این ۳۵ سنت چقدر قابلاعتماد است؟ پر شدنهایی که به هم نزدیکاند، به هم وابستهاند (همه در یک حرکت قیمت شریکاند)، پس فرمول ساده خطای استاندارد که استقلال را فرض میکند، عدمقطعیت را کم نشان میدهد. بهجایش از Block Bootstrap استفاده میکنیم: دقیقههای کامل را با جایگذاری دوباره نمونه میگیریم و Markout را صدها بار از نو حساب میکنیم (درس ۹):
def markout_se(h_ms, n_boot=500):
"""Block bootstrap: resample whole minutes, because nearby fills are correlated."""
idx = np.minimum(df.t // bt.STEP_MS + h_ms // bt.STEP_MS, len(mid_path) - 1)
pnl = (sign * (mid_path[idx] - df.px) * df.qty).to_numpy()
qty, minute = df.qty.to_numpy(), (df.t // 60_000).to_numpy()
rng = np.random.default_rng(0)
minutes = np.unique(minute)
stats = []
for _ in range(n_boot):
pick = rng.choice(minutes, len(minutes)) # minutes drawn with replacement
rows = np.concatenate([np.flatnonzero(minute == m) for m in pick])
stats.append(pnl[rows].sum() / qty[rows].sum())
return np.std(stats)
for h in (0, 5_000):
print(f"markout {h / 1000:>2.0f}s: {markout(h):5.2f} +/- {markout_se(h):4.2f} USDT/BTC")
markout 0s: 3.77 +/- 0.20 USDT/BTC
markout 5s: 2.34 +/- 0.71 USDT/BTC
خطای استاندارد Markout پنجثانیهای 0.71 دلار است؛ دو برابر بزرگتر از کل لبه ۳۵ سنتی. یعنی با یک ساعت داده نمیتوانیم بگوییم این استراتژی بعد از کارمزد لبه مثبت دارد. برای اینکه لبه ۰.۳۵ دلاری دو خطای استاندارد از صفر فاصله بگیرد، خطا باید به حدود ۰.۱۷ برسد. خطا با جذر حجم داده کم میشود، پس به حدود (0.71 / 0.175)² ≈ 16 برابر داده، یعنی حدود ۱۶ ساعت، نیاز داریم؛ آن هم با این فرض که ساعتها شبیه هماند. نقطه دقیق خم شدن منحنی هم با این خطاها از روی یک ساعت داده قابل تشخیص نیست.
۴. آزمودن یک تصمیم طراحی: Microprice یا Mid؟
در درس ۲۹ Microprice را انتخاب کردیم و نوشتیم «این فرض قابل آزمایش است». حالا وقتش است. کلید use_micro را خاموش میکنیم و همهچیز دیگر را ثابت نگه میداریم. به این کار Ablation میگویند: یک جزء را برمیداری تا سهمش را ببینی.
def decompose(**kw):
"""Run one backtest and split its PnL into the lesson-7 pieces."""
df, eq, mid_path, res = bt.run(**kw)
s = np.where(df.side == "buy", 1, -1)
capture = (s * (df.mid - df.px) * df.qty).sum()
return pd.Series(dict(fills=len(df), btc=df.qty.sum(), capture=capture,
inventory=res["pnl"] + res["fees"] - capture,
fees=-res["fees"], net=res["pnl"]))
mid_only = Params(use_micro=False)
print(pd.DataFrame(dict(micro=decompose(), mid=decompose(p=mid_only))).round(2))
seeds = (7, 11, 12, 13, 14, 15)
for name, p in (("micro", Params()), ("mid", mid_only)):
print(f"{name:>5}:", " ".join(f"{bt.run(p, seed=s)[3]['pnl']:6.1f}" for s in seeds))
micro mid
fills 1332.00 1160.00
btc 12.40 10.80
capture 46.81 65.61
inventory -13.00 -31.81
fees -24.70 -21.49
net 9.12 12.30
micro: 9.1 12.5 10.2 11.6 -4.1 19.5
mid: 12.3 15.8 14.7 11.5 9.3 25.1
نتیجه بر خلاف انتظار ماست: Mid ساده در ۵ ساعت از ۶ ساعت بهتر بوده و میانگین سودش 14.8 دلار است، در برابر 9.8 دلار برای Microprice. دو نکته در این جدول هست که ارزش دارد با دقت ببینی:
تفکیک به مرجع وابسته است. با Microprice، Spread Capture کمتر و زیان موجودی هم کمتر است. دلیلش این است که Spread Capture را نسبت به Mid اندازه میگیریم. وقتی سفارشها را حول Microprice میچینی، از دید Mid «لبه کمتری» میگیری، ولی چون Microprice کمی از آینده خبر دارد، موجودیات کمتر ضرر میدهد. سهم هر تکه به انتخاب مرجع بستگی دارد؛ فقط سود خالص مستقل از مرجع است.
اطلاعات داشتن با سودمند بودن فرق دارد. در درس ۳۰ دیدیم Microprice واقعاً اطلاعات دارد. ولی فرمول درس ۵ عدمتعادل حجم را با وزن کامل وارد قیمت میکند: با Spread بیستدلاری و عدمتعادل شدید، Fair Value تا ۹ دلار از Mid فاصله میگیرد، در حالی که سیگنال واقعی در دنیای ما ضعیف است. نتیجه یک تخمین پرنویز است. راهحل منطقی، کوچک کردن این اثر است:
که β را با یک رگرسیون ساده (درس ۱۰) روی حرکت بعدی Mid تخمین میزنی. این را بهعنوان تمرین برای خودت نگه دار.
۵. جاروب پارامتر و خطر Overfitting
وسوسه بعدی، تنظیم پارامترهاست. یک شبکه ۳ در ۳ از min_half و skew را روی همان یک ساعت اجرا میکنیم. ولی این بار هر ترکیب را روی پنج ساعت دیگر هم که در تنظیم نقشی نداشتهاند (Seed 11 تا 15) میسنجیم. اجرای این تکه حدود یک دقیقه طول میکشد:
rows = []
for min_half, skew in itertools.product((6.0, 10.0, 14.0), (0.0, 1.0, 2.0)):
p = Params(min_half=min_half, skew=skew)
pnl_in = bt.run(p, seed=7)[3]["pnl"] # the hour we tune on
pnl_out = [bt.run(p, seed=s)[3]["pnl"] for s in (11, 12, 13, 14, 15)] # five unseen hours
rows.append((min_half, skew, pnl_in, np.mean(pnl_out), min(pnl_out)))
sweep = pd.DataFrame(rows, columns=["min_half", "skew", "in_sample", "out_mean", "out_worst"])
print(sweep.round(1).sort_values("in_sample", ascending=False).to_string(index=False))
min_half skew in_sample out_mean out_worst
14.0 0.0 23.0 9.8 -18.4
6.0 0.0 22.8 14.5 -5.1
10.0 0.0 16.3 22.5 6.1
10.0 1.0 14.3 12.2 3.5
10.0 2.0 10.4 11.4 5.5
6.0 1.0 9.1 9.9 -4.1
14.0 1.0 8.2 4.6 2.2
6.0 2.0 6.3 9.2 -0.4
14.0 2.0 1.3 5.0 1.8
جدول بر اساس نتیجه داخل نمونه (In-Sample) مرتب شده است. حالا ببین چه اتفاقی میافتد:
| ترکیب | داخل نمونه | میانگین خارج از نمونه | بدترین ساعت خارج از نمونه |
|---|---|---|---|
| 14 و 0 (برنده داخل نمونه) | 23.0 | 9.8 | 18.4 زیان |
| 10 و 0 | 16.3 | 22.5 | 6.1 سود |
| 6 و 1 (پیشفرض درس ۲۹) | 9.1 | 9.9 | 4.1 زیان |
برنده داخل نمونه، بدترین ساعت کل جدول را دارد. اگر فقط به یک ساعت نگاه میکردیم، min_half=14, skew=0 را انتخاب میکردیم و در اولین ساعت بد ۱۸ دلار از دست میدادیم؛ تقریباً دو برابر کل سود پایه.
چند درس از همین جدول کوچک:
- رتبهبندی داخل نمونه نویز است. فاصله ۲۳.۰ و ۲۲.۸ بیمعناست. تفاوت واقعی
min_half=6وmin_half=10هم کمتر از آن است که به نظر میرسد: σ تخمینی در این ساعت هیچوقت زیر 6 دلار نمیرود (میانهاش حدود 8.3 دلار است)، پس کف 6 اصلاً فعال نمیشود و فرق واقعی دو تنظیم بهطور متوسط حدود ۱.۵ دلار در نیماسپرد است. - دنبال فلات بگرد، نه قله. ستون
min_half=10تنها جایی است که هر سه مقدارskewدر همه ساعتهای خارج از نمونه سودده بودهاند. یک ناحیه پایدار از یک نقطه درخشان قابلاعتمادتر است. - Skew را با میانگین قضاوت نکن. در این جدول
skew=0میانگین بهتری دارد، چون دنیای مصنوعی ما رژیم خطرناکی ندارد و سقف 0.05 BTC بیشتر ریسک را مهار میکند. ولی بدترین ساعت کل جدول هم مالskew=0است. Skew بیمه است؛ ارزشش در دمِ توزیع دیده میشود، نه در میانگین پنج ساعت. اگر بخواهیم انتخاب کنیم،min_half=10, skew=1را برمیداریم و دلیلش را در سند مینویسیم. - هر آزمایش اضافه، شانس خطا را بالا میبرد. ما ۹ ترکیب را امتحان کردیم. با ۹۰۰ ترکیب، بهترین نتیجه داخل نمونه تقریباً حتماً شانسی است. هر چه بیشتر جستوجو کنی، نتیجه خارج از نمونه باید سختگیرانهتر و طولانیتر باشد. در عمل از Walk-Forward استفاده میشود: روی یک بازه تنظیم کن، روی بازه بعدی بسنج، پنجره را جلو ببر و تکرار کن (درس ۲۰).
۶. حساسیت به فرضها
حالا سه فرض اصلی Backtester را تکان میدهیم: تأخیر، کارمزد و مدل صف. نسخه بدبینانه صف فرض میکند همیشه یک رقیب زودتر از ما رسیده: دو برابر حجم نمایشدادهشده جلوی ماست، و حتی وقتی قیمت را بهتر میکنیم، 0.05 BTC جلوتر از ما نشسته است.
for lat in (0, 50, 100, 200):
print(f"latency {lat:>3} ms: net {decompose(latency_ms=lat)['net']:7.2f}")
for fee in (0.0, 0.2, 0.5):
print(f"maker fee {fee} bp: net {decompose(fee_bps=fee)['net']:7.2f}")
def pessimistic_queue(side, px, book):
"""A competitor always got there first: 0.05 BTC ahead even when we improve the price."""
bid, ask, bid_sz, ask_sz = book
if side == "buy":
return 0.05 if px > bid else 2 * bid_sz
return 0.05 if px < ask else 2 * ask_sz
optimistic_queue = bt.queue_on_arrival
bt.queue_on_arrival = pessimistic_queue
print(f"pessimistic queue: net {decompose()['net']:7.2f}")
latency 0 ms: net 18.36
latency 50 ms: net 9.12
latency 100 ms: net -8.33
latency 200 ms: net -31.18
maker fee 0.0 bp: net 33.82
maker fee 0.2 bp: net 9.12
maker fee 0.5 bp: net -27.92
pessimistic queue: net -3.20
| فرض | خوشبینانه | پایه | بدبینانه |
|---|---|---|---|
| تأخیر | 0 ms: سود 18.36 | 50 ms: سود 9.12 | 100 ms: زیان 8.33 ؛ 200 ms: زیان 31.18 |
| کارمزد Maker (فرضی) | صفر: سود 33.82 | 0.2 bp: سود 9.12 | 0.5 bp: زیان 27.92 |
| مدل صف | مدل درس ۳۰ (همان پایه) | سود 9.12 | زیان 3.20 |
هر کدام از این سه فرض بهتنهایی میتواند علامت نتیجه را عوض کند:
- تأخیر: دو برابر شدن تأخیر از ۵۰ به ۱۰۰ میلیثانیه سود را به زیان تبدیل میکند. با ۱۰۰ میلیثانیه، پیام جایگزینی درست همزمان با داده بعدی میرسد و سفارش کهنه یک گام کامل در دفتر میماند؛ تعداد پر شدنهای
crossاز ۸۴ به ۲۳۸ میرسد. - کارمزد: تست درس ۳۰ تأیید شد: با کارمزد صفر، سود دقیقاً 9.12 + 24.70 = 33.82 است. کارمزد 0.5 bp، یعنی حدود ۵ دلار برای هر BTC، از کل Markout پنجثانیهای (2.34 دلار به ازای هر BTC) هم بیشتر است.
- صف: با فرض بدبینانه، تعداد پر شدنها از ۱,۳۳۲ به ۵۸۳ میرسد و نتیجه منفی میشود.
جملهای که باید در گزارش پروژه بنویسی، «این استراتژی سودده است» نیست. این است: «در این دنیای مصنوعی، این استراتژی فقط با تأخیری جایی بین ۵۰ تا ۱۰۰ میلیثانیه یا کمتر، کارمزد Maker زیر حدود 0.27 bp و جایگاه صف نزدیک به مدل خوشبینانه سودده است.» آستانه کارمزد را میشود مستقیم حساب کرد، چون کارمزد روی تصمیمها اثری ندارد و سود با آن خطی کم میشود: هر 0.1 bp حدود ۱۲.۳۵ دلار، پس سود در 33.82 / 123.5 ≈ 0.27 bp صفر میشود. آستانه تأخیر را با جاروب ریزتر میشود پیدا کرد.
۷. چرا اجرای واقعی از بکتست بدتر است؟
تقریباً هر کسی که یک استراتژی بازارسازی را از Backtest به اجرای واقعی برده، این تجربه را دارد که نتیجه واقعی بدتر بوده است. دلیلها معمولاً همینهاست:
| منبع شکاف | در Backtest ما | در اجرای واقعی |
|---|---|---|
| جایگاه صف (درس ۱۸) | تخمین از حجم نمایشدادهشده | رقبا سریعترند و صف پنهان دارد |
| تأخیر (درس ۱۹) | عدد ثابت 50 ms | توزیع با دم سنگین؛ p99 شاید ۱۰ برابر میانه |
| اثر خود ما | هیچ؛ رقبا به ما واکنش نشان نمیدهند | رقبا قیمت ما را میبینند و جلو میزنند |
| داده | تمیز و کامل | قطعی، پیام گمشده، ترتیب بههمریخته (درس ۱۶) |
| خطاهای عملیاتی (درس ۲۶) | وجود ندارد | Reject، Rate Limit، قطع اتصال |
| کارمزد | یک نرخ ثابت | پلهای بر اساس حجم ماهانه |
| انتخاب پارامتر | روی همین داده تنظیم شده | داده تازه، رژیم تازه |
نکته مهم: تقریباً همه این ردیفها در یک جهت اثر میگذارند. بکتستی که هیچکدام را مدل نکرده، بهطور سیستماتیک خوشبین است، نه فقط پرنویز.
۸. شکاف را اندازه بگیر، نه حدس بزن
راه درست مقایسه، مقایسه تکهبهتکه است، نه فقط مقایسه سود نهایی. برای نشان دادن روش، اجرای «بدبینانه» (تأخیر ۱۰۰ و صف بدبینانه) را جای «اجرای واقعی» میگذاریم و هر دو را با یک تابع تفکیک میکنیم:
bt.queue_on_arrival = optimistic_queue
backtest = decompose()
bt.queue_on_arrival = pessimistic_queue # a stand-in for "live"
live = decompose(latency_ms=100)
bt.queue_on_arrival = optimistic_queue
print(pd.DataFrame(dict(backtest=backtest, live=live, gap=live - backtest)).round(2))
backtest live gap
fills 1332.00 932.00 -400.00
btc 12.40 8.57 -3.83
capture 46.81 8.22 -38.59
inventory -13.00 -0.05 12.95
fees -24.70 -17.07 7.63
net 9.12 -8.89 -18.01
شکاف ۱۸.۰۱ دلاری سود خالص، اینطور تقسیم میشود: Spread Capture ۳۸.۵۹ دلار کم شده، و در مقابل زیان موجودی ۱۲.۹۵ دلار و کارمزد ۷.۶۳ دلار کمتر شدهاند (چون حجم کمتری معامله شده است). حجم ۳۱٪ کم شده (از 12.40 به 8.57 BTC)، ولی Spread Capture ۸۲٪. یعنی پر شدنهایی که از دست رفتهاند، خوبها بودهاند. وقتی تفکیک بر اساس نوع پر شدن را برای این اجرا تکرار کنی (تکه بخش ۲ با همین تنظیمات)، سهم پر شدنهای cross از ۶.۳٪ به ۲۵٪ (۲۳۴ از ۹۳۲) میرسد: صف بدبینانه پر شدنهای بیخطر جلوی صف را میگیرد و تأخیر بیشتر پر شدنهای سمی را زیاد میکند.
در اجرای واقعی، همین روش با داده واقعی این شکل را دارد:
- بازپخش همدوره: داده بازار ثبتشده در همان بازه اجرای واقعی را (با Pipeline درس ۱۶) به Backtester بده، با همان پارامترها. حالا دو اجرای قابلمقایسه داری.
- تفکیک هر دو با یک تابع: همان
decompose، روی پر شدنهای واقعی و شبیهسازیشده. این دلیل اصلی است که در درس ۲۹ Mid لحظه پر شدن را لاگ کردیم. - کالیبره کردن نرخ پر شدن: برای سفارشهای واقعی، نرخ پر شدن را بر اساس جایگاه صف تخمینی دستهبندی کن و با پیشبینی مدل صف مقایسه کن (درس ۱۸).
- مقایسه منحنی Markout: اگر Markout واقعی سریعتر از Backtest افت کند، یا تأخیر واقعی بیشتر است یا طرف معاملهات آگاهتر از مدل.
- تأخیر واقعی را به مدل برگردان: بهجای عدد ثابت، از توزیع اندازهگیریشده با ابزار درس ۲۸ نمونه بگیر.
هدف این حلقه این نیست که شکاف صفر شود. هدف این است که Backtester آنقدر به واقعیت نزدیک شود که تصمیمها (کدام پارامتر بهتر است، کدام تغییر ارزش دارد) در هر دو یکسان باشند.
۹. گزارش پروژه
اگر بخواهی سه درس آخر را در یک صفحه خلاصه کنی، چیزی شبیه این مینویسی:
- طراحی: بازارساز تکبازاره با Microprice، نیماسپرد وابسته به نوسان، Skew خطی و سقف موجودی 0.05 BTC.
- نتیجه پایه: ۹.۱۲ دلار در یک ساعت مصنوعی؛ Spread Capture برابر ۴۶.۸۱، زیان موجودی ۱۳.۰۰، کارمزد ۲۴.۷۰.
- لبه واقعی: Markout پنجثانیهای منهای کارمزد، حدود ۰.۳۵ دلار به ازای هر BTC، با خطای استاندارد ۰.۷۱؛ یک ساعت داده برای اثبات وجود لبه کافی نیست.
- یافتهها: Microprice با وزن کامل در این دنیا از Mid بدتر است. پر شدنهای
crossفقط ۶.۳٪ تعدادند ولی معادل ۲۲٪ از لبه بقیه پر شدنها را از بین میبرند. برنده تنظیم داخل نمونه، بدترین ساعت خارج از نمونه را دارد. - حساسیت: نتیجه با تأخیر ۱۰۰ میلیثانیه، کارمزد ۰.۵ bp یا صف بدبینانه منفی میشود.
- محدودیتها: داده مصنوعی، صف ساده، بدون اثر روی رقبا، کارمزد ثابت.
این صفحه همان چیزی است که در درس ۳۲ به مصاحبهکننده نشان میدهی. دقت کن که تقریباً نیمی از آن درباره محدودیتها و شکستهاست؛ این ضعف گزارش نیست، نقطه قوتش است.
۱۰. تمرین این درس
گزینه مناسب هر سؤال را انتخاب کن و در پایان نتیجه را ببین.
درس بعدی
درس سیودوم آخرین درس این مسیر است: مصاحبه Quant Developer. سؤالهای فنی رایج درباره ساختار داده دفتر سفارش، تأخیر، همروندی و آمار را با پاسخهای کوتاه مرور میکنیم، یک سؤال طراحی سیستم را قدمبهقدم حل میکنیم، و میبینیم چطور همین پروژه سه درس آخر را طوری ارائه کنی که مصاحبهکننده بفهمد واقعاً میدانی چه ساختهای.
این مطلب صرفاً آموزشی است و توصیه خرید، فروش یا سرمایهگذاری نیست.