معماری سیستم‌های ترکیبی کوانتی: یکپارچگی Risk Management، Position Sizing و Execution در یک فریم‌ورک
مقاله حسین نریمانی ۱۴۰۵/۰۴/۰۱ Quant System Design

معماری سیستم‌های ترکیبی کوانتی: یکپارچگی Risk Management، Position Sizing و Execution در یک فریم‌ورک

اکثر سیستم‌های کوانتی که در production شکست می‌خورند، مشکل سیگنال ندارند. مشکل معماری دارند. سیگنال‌شان کار می‌کند. بک‌تست‌شان قانع‌کننده است. اما وقتی به محیط live می‌رسند، ساختار درونی‌شان تحمل فشار واقعی را ندارد...

اکثر سیستم‌های کوانتی که در production شکست می‌خورند، مشکل سیگنال ندارند. مشکل معماری دارند.

سیگنال‌شان کار می‌کند. بک‌تست‌شان قانع‌کننده است. اما وقتی به محیط live می‌رسند، ساختار درونی‌شان تحمل فشار واقعی را ندارد — چون Risk Management، Position Sizing و Execution به‌عنوان سه موجود مجزا طراحی شده‌اند که فقط در انتها به هم وصل می‌شوند.

این مقاله درباره‌ی نحوه‌ی طراحی یک فریم‌ورک یکپارچه است — جایی که این سه لایه از ابتدا به‌عنوان یک سیستم واحد طراحی می‌شوند، نه سه پروژه‌ی موازی.


چرا معماری جداگانه در سیستم‌های کوانتی شکست می‌خورد

تصور رایج این است: یک مدل سیگنال بساز، یک لایه‌ی risk روش آن بگذار، یک execution engine به خروجی‌اش وصل کن. کار تمام است.

این تصور اشتباه است — نه از نظر منطق، بلکه از نظر زمان‌بندی اطلاعات. هر لایه تصمیماتی می‌گیرد که وابسته به state لایه‌های دیگر است. وقتی این لایه‌ها جدا باشند، هر کدام با یک تصویر ناقص کار می‌کنند.

مشکل State Fragmentation

وقتی Risk Engine نمی‌داند که Execution Engine در حال اجرای یک سفارش بزرگ است، محاسبه‌ی exposure اشتباه می‌شود. وقتی Position Sizing نمی‌داند که چه مقدار از سفارش قبلی fill شده، اندازه‌ی موقعیت جدید اشتباه حساب می‌شود. نتیجه: slippage واقعی از slippage مدل‌شده فاصله می‌گیرد و drawdown در زمان‌هایی رخ می‌دهد که سیستم فکر می‌کرد محافظت شده است.

مشکل Feedback Loop معکوس

در معماری جداگانه، هیچ‌کدام از لایه‌ها feedback واقعی از لایه‌های دیگر نمی‌گیرند. Risk Engine فقط از position های نهایی باخبر می‌شود، نه از فرآیند execution. این یعنی سیستم تا زمانی که یک موقعیت کامل fill شده باشد، risk واقعی را نمی‌داند — و این تأخیر در بازارهای پرنوسان می‌تواند کشنده باشد.


فریم‌ورک یکپارچه: سه لایه، یک State

معماری یکپارچه بر یک اصل ساده استوار است: هر سه لایه باید از یک منبع واحد از حقیقت (Single Source of Truth) تغذیه شوند و خروجی هر لایه بلافاصله در دسترس دو لایه‌ی دیگر باشد.

این یعنی یک State Manager مرکزی که نه فقط موقعیت‌های open، بلکه سفارش‌های در حال اجرا، exposure های partial، و وضعیت real-time هر instrument را نگه می‌دارد.

لایه اول: Risk Engine به‌عنوان Gate نه Filter

اشتباه رایج این است که Risk Management را به‌عنوان یک فیلتر انتهایی در نظر بگیریم — جایی که سیگنال‌ها وارد می‌شوند و اگر "خطرناک" بودند، رد می‌شوند. این رویکرد دیر است.

در معماری یکپارچه، Risk Engine یک Gate است. قبل از اینکه Position Sizing حتی محاسبه شود، Risk Engine باید پاسخ دهد:

  • آیا exposure فعلی پرتفولیو در این instrument اجازه‌ی موقعیت جدید می‌دهد؟
  • آیا correlation این سیگنال با موقعیت‌های موجود، concentration risk ایجاد می‌کند؟
  • آیا volatility regime فعلی با پارامترهای risk طراحی‌شده‌ی این استراتژی همخوانی دارد؟
  • آیا drawdown فعلی از یک threshold مشخص گذشته که باید position sizing را کاهش دهیم؟

اگر پاسخ به هر کدام "خیر" است، سیگنال اصلاً به مرحله‌ی sizing نمی‌رسد. این باعث می‌شود که Position Sizing همیشه با یک فضای risk تمیز کار کند.

لایه دوم: Position Sizing به‌عنوان یک تابع پویا نه یک عدد ثابت

Position Sizing در اکثر سیستم‌های entry-level یا fixed است (مثلاً ۲٪ از سرمایه) یا بر اساس یک فرمول ساده مثل Kelly Criterion محاسبه می‌شود. هر دو رویکرد در isolation کار می‌کنند.

در یک سیستم یکپارچه، sizing باید تابعی از چند متغیر همزمان باشد:

متغیر ورودی تأثیر بر Position Size منبع داده
Volatility فعلی instrument معکوس — volatility بالا، size کمتر Risk Engine
Current Drawdown معکوس — drawdown بالا، size کمتر State Manager
Signal Confidence (Sharpe یا probability) مستقیم — confidence بالا، size بزرگ‌تر Signal Engine
Portfolio Correlation معکوس — correlation بالا، size کمتر Risk Engine
Execution Capacity (Liquidity) محدودکننده — نمی‌توان بزرگ‌تر از بازار بود Execution Engine

این یعنی Position Sizing نه فقط از Signal Engine، بلکه از Risk Engine و Execution Engine هم تغذیه می‌شود. هیچ‌کدام از این لایه‌ها در isolation تصمیم نمی‌گیرند.

لایه سوم: Execution Engine به‌عنوان یک بازیگر آگاه

Execution Engine در معماری‌های ضعیف یک "اجراکننده‌ی کور" است — عددی می‌گیرد و سعی می‌کند آن را در بازار اجرا کند. در معماری یکپارچه، Execution Engine باید به Risk Engine و State Manager متصل باشد و در حین اجرا تصمیم بگیرد.

سه قابلیت حیاتی:

  1. Partial Fill Reporting: هر بخشی از سفارش که fill می‌شود، بلافاصله به State Manager گزارش می‌شود تا Risk Engine exposure واقعی را بداند.
  2. Market Impact Awareness: اگر اندازه‌ی سفارش از یک threshold نسبت به volume بازار عبور کند، Execution Engine باید از Risk Engine تأیید بگیرد که آیا می‌توان به اجرا ادامه داد یا باید size را کاهش داد.
  3. Abort Capability: اگر در حین execution، Risk Engine یک breach شناسایی کند (مثلاً correlation یک instrument دیگر ناگهان بالا رفت)، باید بتواند سفارش در حال اجرا را متوقف یا تعدیل کند.

State Manager: قلب معماری یکپارچه

بدون یک State Manager مرکزی، هیچ‌کدام از قابلیت‌های بالا ممکن نیست. State Manager نباید فقط یک دیتابیس ساده باشد — باید یک سیستم real-time با latency پایین باشد که همه‌ی لایه‌ها می‌توانند به‌صورت همزمان از آن بخوانند و به آن بنویسند.

ساختار State Manager

State Manager باید حداقل این موجودیت‌ها را track کند:

  • Open Positions: با اندازه، قیمت ورود، unrealized PnL و exposure به‌روز
  • Pending Orders: سفارش‌هایی که submitted شده‌اند اما هنوز fill نشده‌اند — این بخش اغلب نادیده گرفته می‌شود
  • Partial Fills: بخش‌هایی از سفارش که fill شده‌اند — برای محاسبه‌ی exposure دقیق
  • Portfolio Metrics: شامل total exposure، drawdown فعلی، volatility واقعی (نه historical) و correlation matrix
  • Risk Limits: محدودیت‌های تعریف‌شده به تفکیک instrument، sector و پرتفولیو کلی

انتخاب تکنولوژی برای State Manager

انتخاب اشتباه تکنولوژی می‌تواند کل معماری را از بین ببرد. این یک مقایسه‌ی کاربردی است:

تکنولوژی مزیت محدودیت مناسب برای
Redis latency بسیار پایین، در دسترس برای همه‌ی لایه‌ها persistence محدود، نیاز به schema دقیق سیستم‌های high-frequency تا mid-frequency
PostgreSQL با LISTEN/NOTIFY persistence قوی، ACID compliance latency بالاتر از Redis سیستم‌های low-frequency با نیاز به audit trail
Apache Kafka event streaming قوی، scalable پیچیدگی عملیاتی بالا سیستم‌های multi-strategy با حجم بالا
In-Memory (Python dict) ساده‌ترین، سریع‌ترین فقط برای single-process، بدون fault tolerance prototype و backtesting

توصیه‌ی عملی: برای اکثر سیستم‌های systematic trading در مقیاس متوسط، Redis به‌عنوان real-time state cache در کنار PostgreSQL برای persistence کافی است. Kafka وقتی منطقی است که چندین استراتژی مستقل از یک infrastructure مشترک استفاده کنند.


Risk Management یکپارچه: فراتر از Stop-Loss

وقتی از Risk Management در سیستم‌های یکپارچه صحبت می‌کنیم، منظورمان فقط stop-loss نیست. Stop-loss یک ابزار reactive است — بعد از اینکه ضرر اتفاق افتاده کار می‌کند.

Risk Management واقعی در یک سیستم یکپارچه پنج لایه دارد:

۱. Pre-Trade Risk Checks

قبل از هر سفارش، سیستم باید چند چک اتوماتیک انجام دهد:

  • آیا notional value این سفارش از حداکثر مجاز برای این instrument عبور می‌کند؟
  • آیا اضافه‌کردن این موقعیت، total portfolio exposure از threshold تعریف‌شده بیشتر می‌کند؟
  • آیا در یک ساعت گذشته، تعداد سفارش‌ها از یک حد مشخص بیشتر شده (علامت یک باگ یا loop)؟
  • آیا spread فعلی بازار از یک threshold معقول بیشتر است که اجرای بهینه ممکن نباشد؟

۲. Intra-Trade Risk Monitoring

این بخش اغلب نادیده گرفته می‌شود. بین زمان ارسال سفارش و زمان fill شدن آن، شرایط بازار می‌تواند تغییر کند. سیستم باید در این بازه هم monitoring داشته باشد.

۳. Portfolio-Level Risk

این سطح از risk را نمی‌توان در سطح instrument مدیریت کرد. باید در سطح کل پرتفولیو ببینید:

  • Concentration Risk: آیا بخش بزرگی از exposure در یک sector یا یک asset class است؟
  • Tail Risk: در یک سناریوی worst-case، چقدر از سرمایه در معرض خطر است؟ این را نمی‌توان از جمع ساده‌ی stop-loss های individual بدست آورد.
  • Liquidity Risk: در یک crash ناگهانی، آیا می‌توانید از تمام موقعیت‌هایتان در قیمت معقول خارج شوید؟

۴. Regime-Based Risk Adjustment

این جایی است که سیستم‌های پیشرفته از سیستم‌های ساده جدا می‌شوند. Risk parameters باید با regime بازار تغییر کنند.

یک مثال عملی: در یک regime با volatility پایین و trend قوی، می‌توانید با position size بزرگ‌تر کار کنید. در یک regime با volatility بالا و بازار range-bound، همان strategy باید با size کوچک‌تر و stop-loss محکم‌تر کار کند — حتی اگر سیگنال‌ها از نظر strength یکسان باشند.

برای تشخیص regime می‌توانید از روش‌های ساده‌ای مثل مقایسه‌ی rolling volatility با historical percentile استفاده کنید، یا از مدل‌های پیچیده‌تر مثل Hidden Markov Model (HMM).

۵. Circuit Breakers

Circuit Breakers آخرین خط دفاعی هستند — نه اول. اگر سیستم به circuit breaker برسد، یعنی چهار لایه‌ی قبلی شکست خورده‌اند. با این حال، بدون آن‌ها نباشید:

  • اگر daily drawdown از X٪ عبور کرد، تمام trading متوقف شود
  • اگر یک instrument در کمتر از Y دقیقه Z٪ حرکت کرد، هیچ موقعیت جدیدی باز نشود
  • اگر connection به exchange برای بیش از N ثانیه قطع شد، سیستم به safe mode برود

Position Sizing پیشرفته: Kelly و فراتر از آن

Kelly Criterion یکی از پایه‌ای‌ترین فریم‌ورک‌های Position Sizing است. فرمول اصلی ساده است: f* = (bp - q) / b که در آن b بازده به ازای هر واحد ریسک، p احتمال موفقیت و q احتمال شکست است.

اما Kelly Criterion در دنیای واقعی مشکلات جدی دارد.

مشکلات Kelly Criterion در عمل

اول: Kelly فرض می‌کند که می‌دانید p و b را. در واقعیت، این دو پارامتر خودشان تخمین‌های نویزدار هستند. اگر تخمین‌تان از p اشتباه باشد، Kelly می‌تواند شما را به over-sizing برساند.

دوم: Kelly برای حداکثر کردن growth طراحی شده، نه برای کنترل drawdown. Full Kelly می‌تواند drawdown های بسیار بزرگ ایجاد کند که از نظر روانشناختی و از نظر operational غیرقابل تحمل هستند.

سوم: Kelly فرض می‌کند بازی‌ها مستقل هستند. در trading، trades با هم correlation دارند — به‌خصوص در بازارهای crisis.

Fractional Kelly و Volatility Targeting

راه‌حل رایج، استفاده از Fractional Kelly است — معمولاً Half-Kelly یا Quarter-Kelly. این رویکرد drawdown را به‌طور قابل‌توجهی کاهش می‌دهد در حالی که بخش بزرگی از edge را حفظ می‌کند.

اما در یک سیستم یکپارچه، Volatility Targeting اغلب عملی‌تر است. در این رویکرد، هدف این است که position size طوری تنظیم شود که volatility contribution هر trade به پرتفولیو ثابت بماند. مثلاً:

اگر هدف ما این است که هر trade حداکثر ۰.۵٪ از volatility پرتفولیو را اضافه کند، و volatility فعلی یک instrument ۲۰٪ سالانه است، position size به‌طور اتوماتیک محاسبه می‌شود — نه از روی یک عدد ثابت.

این رویکرد یک مزیت بزرگ دارد: در دوره‌های high volatility به‌صورت اتوماتیک size را کاهش می‌دهد و در دوره‌های low volatility size را افزایش می‌دهد — بدون اینکه نیاز به دخالت دستی باشد.


Execution Engine: طراحی برای واقعیت بازار

Execution Engine بخشی است که بیشترین فاصله را بین backtesting و live trading ایجاد می‌کند. در backtesting همه چیز ساده است: قیمت close را می‌گیریم، سفارش fill می‌شود. در live trading این سادگی وجود ندارد.

Slippage و Market Impact

Slippage واقعی در سیستم‌های کوانتی از چند منبع می‌آید:

  • Bid-Ask Spread: ساده‌ترین و مشخص‌ترین هزینه
  • Market Impact: تأثیر خود سفارش بر قیمت — وقتی size شما نسبت به volume بازار بزرگ است
  • Timing Slippage: تفاوت بین قیمت تصمیم و قیمت اجرا — که در بازارهای سریع می‌تواند قابل‌توجه باشد
  • Queue Position: در order book، جایگاه شما در صف می‌تواند fill شدن یا نشدن را تعیین کند

Order Type Selection به‌عنوان یک تصمیم استراتژیک

اکثر سیستم‌های ساده از Market Order استفاده می‌کنند. این اشتباه است — نه همیشه، اما اغلب.

یک درخت تصمیم ساده برای انتخاب order type:

  • اگر urgency بالاست و edge شما time-sensitive است → Market Order با پذیرش slippage
  • اگر instrument liquid است و size شما کوچک‌تر از ۱٪ average daily volume است → Limit Order نزدیک به mid-price
  • اگر size بزرگ است → TWAP (Time-Weighted Average Price) یا VWAP برای کاهش market impact
  • اگر می‌خواهید در یک قیمت مشخص وارد شوید → Limit Order با تعریف دقیق timeout

Execution Monitoring و Feedback

بعد از اجرای یک سفارش، سیستم باید بین Expected Execution و Actual Execution مقایسه کند. این تفاوت — که به آن Implementation Shortfall می‌گویند — باید به‌طور مستمر track شود.

اگر Implementation Shortfall در طول زمان بدتر می‌شود، این یک signal است که یا بازار تغییر کرده، یا strategy شما آنقدر بزرگ شده که market impact آن قابل توجه است، یا execution logic نیاز به بهینه‌سازی دارد.


اشتباهات رایج در پیاده‌سازی این معماری

۱. Over-Engineering از ابتدا

یکی از شایع‌ترین اشتباهات این است که از روز اول بخواهید همه‌ی این لایه‌ها را به‌صورت کامل و perfect پیاده کنید. نتیجه: سیستمی که هرگز به production نمی‌رسد.

رویکرد بهتر: با یک State Manager ساده و لایه‌های پایه شروع کنید. فقط وقتی که سیستم در live کار کرد و edge را تأیید کردید، لایه‌های پیچیده‌تر را اضافه کنید.

۲. Backtesting با فرض‌های غیرواقعی

اگر در backtesting فرض کنید که همیشه در قیمت close fill می‌شوید، slippage وجود ندارد و order size محدودیتی ندارد، نتایج بک‌تست‌تان هیچ ارتباطی به live performance نخواهد داشت.

حداقل‌ها برای یک backtesting realistic:

  • شبیه‌سازی bid-ask spread بر اساس داده‌های تاریخی
  • محاسبه‌ی market impact بر اساس نسبت size به volume
  • اعمال latency واقع‌بینانه بین signal و execution
  • در نظر گرفتن هزینه‌های کمیسیون و funding

۳. نادیده گرفتن Correlation در بحران

آماده‌ای این ایده را روی محصول خودت اجرا کنی؟ جلسه راهبردی رزرو کن و نقشه مسیر اسپرینت بعدی را دقیق کن.

نظرات (0)

اولین نفری باشید که نظر می‌دهد.

برای ثبت نظر باید وارد حساب کاربری خود شوید.

ورود / ثبت‌نام