Skip to content

فصل ۱۳ — بورس اوراق بهادار (Stock Exchange)

خلاصهٔ تحلیلی — این متن چکیدهٔ مفاهیم فصل به زبان فارسی است، نه ترجمهٔ واژه‌به‌واژه.

گام ۱ — نیازمندی‌ها و برآورد

بورسی کوچک تا متوسط: فقط سهام و فقط limit order (ثبت و لغو)، ساعات معاملات عادی، ده‌ها هزار کاربر همزمان، حداقل ۱۰۰ نماد و میلیاردها سفارش در روز؛ به‌علاوه risk check ساده (سقف معاملهٔ روزانه) و کنترل موجودی کیف پول (فریز وجه سفارش‌های باز).

نیازمندی‌های غیرکارکردی: دسترس‌پذیری ۹۹٫۹۹٪ (چند ثانیه downtime هم به اعتبار ضربه می‌زند)، تحمل خطا با بازیابی سریع، تأخیر round-trip در حد میلی‌ثانیه با تمرکز بر صدک ۹۹، و امنیت (KYC، دفاع DDoS). برآورد: ~۱ میلیارد سفارش در ۶٫۵ ساعت ≈ ۴۳هزار QPS؛ پیک (ابتدا/انتهای بازار) ~۲۱۵هزار QPS.

دانش پایهٔ کسب‌وکار

  • Broker: واسط کاربران خرد؛ مشتریان نهادی با نرم‌افزار تخصصی و نیازهای متفاوت (صندوق بازنشستگی: order splitting برای کاهش market impact؛ market maker: تأخیر بسیار پایین).
  • Limit order قیمت ثابت دارد و شاید فوراً یا کامل match نشود؛ market order قیمت ندارد و فوراً به قیمت روز اجرا می‌شود.
  • سطوح دادهٔ بازار: L1 (بهترین bid/ask و حجم)، L2 (چند سطح قیمتی)، L3 (سطوح قیمتی + صف سفارش‌ها در هر سطح).
  • Candlestick: قیمت open/close/high/low یک بازهٔ زمانی. FIX: پروتکل استاندارد و vendor-neutral تبادل اطلاعات معاملات اوراق.

گام ۲ — طراحی سطح بالا

سه جریان با الزامات تأخیر متفاوت:

  1. جریان معامله (مسیر بحرانی): کلاینت ← broker ← client gateway (اعتبارسنجی، احراز هویت، rate limiting — باید سبک بماند) ← order manager (risk check + کنترل موجودی کیف پول) ← sequencermatching engine ← بازگشت executions به کلاینت.
  2. جریان دادهٔ بازار: matching engine ← market data publisher (ساخت order book و candlestick) ← data service ← broker ها.
  3. جریان گزارش: reporter ویژگی‌های سفارش‌ها و executions را ادغام و برای مالیات، تسویه و compliance در دیتابیس می‌نویسد — دقت مهم‌تر از تأخیر.

اجزای کلیدی:

  • Matching engine: نگهداری order book هر نماد، تطبیق خرید/فروش (هر match دو execution یا fill می‌سازد) و انتشار جریان executions. باید قطعی (deterministic) باشد: بازپخش همان ترتیب سفارش‌ها باید همان ترتیب executions را بدهد — پایهٔ دسترس‌پذیری بالا.
  • Sequencer: به هر سفارش ورودی و هر execution خروجی شمارهٔ ترتیبی می‌زند (نمونهٔ inbound و outbound جدا). دلایل: انصاف و زمان‌بندی، بازیابی/بازپخش سریع، تضمین exactly-once. در عمل نقش صف پیام و event store را هم دارد (شبیه دو استریم Kafka، اگر تأخیر Kafka قابل‌قبول بود).
  • Client gateway های متفاوت برای خرد/نهادی؛ حالت افراطی: colocation — سرور broker در دیتاسنتر بورس که تأخیرش عملاً طول کابل است.

API و مدل داده

REST برای broker ها: POST /v1/order، GET /v1/execution، GET /v1/marketdata/orderBook/L2 و GET /v1/marketdata/candles (نهادی‌ها پروتکل تخصصی می‌خواهند). سه نوع داده:

  • Product / Order / Execution: product ایستا و cache-پذیر؛ order دستور ورودی و execution نتیجهٔ match. در مسیر بحرانی، سفارش‌ها در دیتابیس ذخیره نمی‌شوند — در حافظه اجرا و در sequencer (دیسک/حافظهٔ مشترک) ماندگار می‌شوند و بعد از بستن بازار آرشیو؛ reporter جداگانه به دیتابیس می‌نویسد.
  • Order book: فهرست سفارش‌های خرید/فروش به تفکیک سطح قیمت. الزامات: جست‌وجوی O(1)، افزودن/لغو/تطبیق O(1)، دسترسی به بهترین bid/ask و پیمایش سطوح. پیاده‌سازی: نگاشت قیمت←PriceLevel + لیست پیوندی دوطرفه در هر سطح + نگاشت orderId←Order؛ افزودن به انتهای صف، تطبیق از ابتدای صف و لغو با پرش مستقیم از orderMap، همگی O(1) (لیست یک‌طرفه لغو را O(n) می‌کرد).
  • Candlestick chart: کلاس Candlestick به ازای هر بازه؛ حافظه با ring buffer از پیش تخصیص‌یافته و محدود کردن تعداد stick ها (بقیه روی دیسک) کنترل می‌شود. دادهٔ بازار معمولاً در دیتابیس ستونی درون-حافظه‌ای (مثل KDB) برای تحلیل real-time.

گام ۳ — بررسی عمیق

کارایی: همه‌چیز روی یک سرور

تأخیر = مجموع زمان اجرای مسیر بحرانی؛ دو اهرم: حذف کار از مسیر بحرانی (حتی logging حذف می‌شود) و کوتاه کردن هر گام. با اجزای شبکه‌ای، رفت‌وبرگشت شبکه (~۵۰۰ میکروثانیه در هر hop) و دیسک sequencer، تأخیر به ده‌ها میلی‌ثانیه می‌رسد — برای بورس مدرن کافی نیست. راه‌حل تکامل‌یافته: همهٔ اجزای مسیر بحرانی روی یک سرور با ارتباط از طریق mmap روی /dev/shm (فایل‌سیستم مبتنی بر حافظه؛ بدون هیچ دسترسی دیسک/شبکه؛ ارسال پیام زیر یک میکروثانیه) — «microservice های کم‌تأخیر درون یک سرور».

  • Application loop: حلقهٔ while تک‌نخی که فقط وظایف حیاتی را poll و اجرا می‌کند و به یک هستهٔ CPU پین می‌شود: بدون context switch و بدون قفل ← صدک ۹۹ پایین و پیش‌بینی‌پذیر. هزینه: برنامه‌نویسی سخت‌گیرانه تا هیچ task حلقه را طولانی اشغال نکند.

Event sourcing در بورس

مثل فصل کیف پول: به‌جای state فعلی، لاگ تغییرناپذیر رویدادها منبع حقیقت است. gateway پیام FIX را به SBE (کدگذاری باینری فشرده) تبدیل و به‌صورت NewOrderEvent وارد event store می‌کند؛ order manager (که به کتابخانهٔ embedded در هر جزء تبدیل شده تا رفت‌وبرگشت state حذف شود — event sourcing یکسانی state ها را تضمین می‌کند) اعتبارسنجی و به matching core می‌دهد؛ match ← OrderFilledEvent به event store؛ MDP و reporter رویدادها را async مصرف می‌کنند. sequencer اینجا فقط یک کار می‌کند: تک-نویسنده که رویدادها را از ring buffer محلی اجزا برمی‌دارد، شماره می‌زند و به event store می‌نویسد (چند sequencer = جنگ قفل).

دسترس‌پذیری بالا و تحمل خطا

  • Hot-warm: نمونهٔ warm همان رویدادها را پردازش می‌کند اما چیزی منتشر نمی‌کند؛ با سقوط hot فوراً جایگزین می‌شود؛ warm پس از restart از event store بازیابی می‌شود — determinism بازیابی را دقیق می‌کند. تشخیص خرابی با heartbeat.
  • فراتر از یک سرور: replication کل event store به سرورهای warm (مثلاً با reliable UDP، الگوی Aeron) و بین دیتاسنترها.
  • «down بودن» ساده نیست: هشدار کاذب failover بیهوده می‌سازد و باگ مشترک می‌تواند backup ها را هم بیندازد ← ابتدا failover دستی، بعد از کسب تجربه خودکارسازی؛ chaos engineering برای کشف حالات مرزی.
  • انتخاب leader با Raft: اکثریت رأی (⌊n/2⌋+1)، election timeout پس از قطع heartbeat، مفهوم term و حالت split vote. RTO بورس در حد ثانیه (نیازمند failover خودکار و استراتژی degradation اولویت‌بندی‌شده)؛ RPO نزدیک صفر — Raft با نسخه‌های متعدد داده آن را می‌دهد.

تطبیق، determinism و MDP

  • الگوریتم تطبیق FIFO: در هر سطح قیمت، سفارش زودتر زودتر match می‌شود؛ گونه‌های دیگری مثل FIFO با LMM (سهمیهٔ market maker پیشرو) در بازار آتی و dark pool ها رایج‌اند.
  • دو نوع determinism: کارکردی (بازپخش همان ترتیب = همان نتیجه؛ فاصلهٔ زمانی واقعی رویدادها مهم نیست، پس بازپخش می‌تواند فشرده و سریع باشد) و تأخیری (تأخیر تقریباً یکسان برای همهٔ معاملات؛ سنجش با صدک ۹۹/۹۹٫۹۹ و HdrHistogram؛ نوسان‌ها مثل GC توقف-جهان JVM را بررسی کنید).
  • MDP: بازسازی order book و candlestick از executions با ring buffer (پیش‌تخصیص، بدون قفل، با padding خط کش)؛ سطوح سرویس متفاوت (۵ سطح L2 رایگان، بیشتر پولی).

انصاف و امنیت

توزیع دادهٔ بازار باید هم‌زمان باشد وگرنه جای اول صف مزیت معاملاتی می‌شود ← multicast با reliable UDP (و/یا ترتیب تصادفی subscriber ها). colocation انصاف را نمی‌شکند — سرویس VIP پولی است. دفاع DDoS: جداسازی سرویس عمومی از خصوصی، کش تهاجمی، URL های cache-پذیر (/data/recent به‌جای query string)، safelist/blocklist و rate limiting.

نکات کلیدی فصل

  • بورس مدرن ممکن است عملاً روی یک سرور غول‌پیکر اجرا شود: mmap + /dev/shm + تک‌نخی پین‌شده به CPU، برای حذف شبکه، دیسک، context switch و قفل از مسیر بحرانی.
  • determinism (sequencer تک-نویسنده + event sourcing) هم‌زمان پایهٔ بازیابی سریع، hot-warm failover و ممیزی است.
  • order book کارآمد = hash map + doubly-linked list؛ هر سه عملیات place/match/cancel باید O(1) باشد.
  • صدک ۹۹ تأخیر مهم‌تر از میانگین است؛ تأخیرِ پایدار خودش یک نیازمندی کسب‌وکاری است.
  • در سیستم‌های مالی regulated، «انصاف» یک الزام طراحی است: multicast هم‌زمان، نه صف اولویت‌دار.

یادداشت‌های مترجم

  • order book، matching engine، sequencer، colocation، multicast، ring buffer و RTO/RPO لاتین حفظ شده‌اند.