پیشرفتهml

Reinforcement Learning — ترید با RL

15 دقیقه
Reinforcement Learning — ترید با RL

Q-Learning، PPO، DQN.

یادگیری تقویتی در ترید — Q-Learning، DQN، PPO

هدف یادگیری

پس از مطالعه این درس خواهید توانست:
- مسئله ترید را در قالب فرمال Markov Decision Process (MDP) تعریف کنید
- معادله بلمن و آپدیت Q-Learning را دستی محاسبه کنید
- تفاوت معماری DQN و Q-Learning جدولی را توضیح دهید
- الگوریتم PPO و مفهوم clipping را با فرمول شرح دهید
- محیط ترید برای RL را طراحی کرده و تابع پاداش مناسب انتخاب کنید

---

۱. ترید به‌عنوان Markov Decision Process

ترید در بازارهای مالی را می‌توان به‌صورت یک MDP با چهارتایی (S, A, P, R) فرمال کرد:

- S (State Space): مجموعه حالت‌های بازار — قیمت، حجم، اندیکاتورها، موقعیت فعلی پرتفولیو
- A (Action Space): فضای اقدام — {خرید، فروش، نگه‌داری} یا برای اقدام پیوسته {−1 تا +1} به‌عنوان وزن موقعیت
- P (Transition Probability): احتمال انتقال از حالت s به s' با اقدام a — در بازار ذاتاً تصادفی است
- R (Reward Function): تابع پاداش — معمولاً تغییر ارزش پرتفولیو منهای هزینه معاملاتی

مثال عددی — تعریف حالت برای BTC/USDT:

s_t = [close_t / close_{t-1} - 1, # بازده قیمتی نرمال‌شده
volume_t / MA(volume, 20), # نسبت حجم به میانگین ۲۰ دوره‌ای
RSI_14 / 100, # RSI نرمال‌شده به [0,1]
position_t] # موقعیت فعلی: {-1, 0, +1}

بازده تجمعی با Discount Factor:

عامل RL هدفش بیشینه کردن بازده تجمعی تخفیف‌یافته است:

G_t = R_{t+1} + γ·R_{t+2} + γ²·R_{t+3} + ...
= Σ_{k=0}^{∞} γ^k · R_{t+k+1}

با γ = 0.99 برای استراتژی swing (افق بلندمدت‌تر) در برابر γ = 0.90 برای scalping، عامل وزن‌دهی پاداش‌های آینده را کنترل می‌کنیم. هر چه γ به ۱ نزدیک‌تر باشد، عامل «صبورتر» است.

---

۲. Q-Learning — معادله بلمن و آپدیت جدولی

Q-Learning یک الگوریتم model-free و off-policy است که تابع ارزش-اقدام Q(s,a) را یاد می‌گیرد — یعنی نیازی به مدل انتقال محیط ندارد.

معادله بلمن برای Q بهینه:

Q(s, a) = E[R_{t+1} + γ · max_{a'} Q(s_{t+1}, a') | s_t=s, a_t=a]

قانون آپدیت Q-Learning:

Q(s_t, a_t) ← Q(s_t, a_t) + α · [R_{t+1} + γ·max_{a'}Q(s_{t+1}, a') - Q(s_t, a_t)]
|_____________TD Target_____________| |__TD Error__|

جایی که α نرخ یادگیری است و TD Error خطای تفاضل زمانی نامیده می‌شود.

مثال محاسباتی گام‌به‌گام:

فرض کنید:
- Q(s_t, Buy) = 0.40
- R_{t+1} = +0.02 (قیمت ۲٪ رشد کرد — پاداش مثبت)
- max Q(s_{t+1}, a') = 0.60
- α = 0.10, γ = 0.95

TD Target = 0.02 + 0.95 × 0.60 = 0.02 + 0.57 = 0.59
TD Error = 0.59 - 0.40 = 0.19

Q_new(s_t, Buy) = 0.40 + 0.10 × 0.19 = 0.419

آپدیت Q برای Buy اقدام را ۰.۰۱۹ واحد افزایش داد — عامل یاد گرفت که در این حالت خرید بهتر از آن چیزی بود که فکر می‌کرد.

محدودیت بنیادی — Curse of Dimensionality:

Q-Learning کلاسیک فضای حالت را در یک جدول ذخیره می‌کند. اگر State یک بردار ۱۰ بعدی پیوسته باشد، جدول Q به‌طور نمایی رشد می‌کند و عملاً غیرممکن می‌شود. این همان مشکلی بود که DQN برای حل آن طراحی شد.

---

۳. DQN — شبکه عصبی به‌جای جدول

Deep Q-Network (DQN) از یک شبکه عصبی به‌عنوان function approximator برای Q استفاده می‌کند:

Q(s, a; θ) ≈ Q*(s, a)

شبکه ورودی s را می‌گیرد و برای همه اقدامات a مقدار Q را به‌صورت موازی خروجی می‌دهد.

نوآوری اول: Experience Replay

به‌جای آموزش آنلاین (هر گام یک آپدیت)، تجربیات (s, a, r, s') را در یک Replay Buffer ذخیره می‌کنیم:

python
replay_buffer = deque(maxlen=100_000)

هر گام:

replay_buffer.append((s_t, a_t, r_{t+1}, s_{t+1}, done))

هر N گام، یک batch تصادفی برای آموزش:

batch = random.sample(replay_buffer, batch_size=64)

این کار دو مشکل را حل می‌کند:
۱. Correlation: تجربیات متوالی همبسته هستند — نمونه‌برداری تصادفی این همبستگی را می‌شکند
۲. Sample Efficiency: هر تجربه می‌تواند چندین بار در آموزش استفاده شود

نوآوری دوم: Target Network

یک شبکه هدف جداگانه با وزن‌های θ⁻ داریم که با تأخیر آپدیت می‌شود:

Loss(θ) = E[(R + γ·max_{a'}Q(s', a'; θ⁻) - Q(s, a; θ))²]

هر C گام (معمولاً C = 1000) وزن‌های Target Network کپی می‌شوند: θ⁻ ← θ

اگر Target Network نداشتیم، هم هدف و هم پیش‌بینی با هر آپدیت تغییر می‌کردند — مثل تیراندازی به یک هدف متحرک — و یادگیری diverge می‌شد.

معماری شبکه برای ترید (LSTM + Dense):

Input Layer: [N_features × T_lookback]

LSTM Layer (64 units) ← برای گرفتن وابستگی زمانی در سری قیمتی

Dense Layer (128) + ReLU

Dense Layer (64) + ReLU

Output Layer: [N_actions] # مثلاً [Q_buy, Q_sell, Q_hold]

Dueling DQN — پیشرفت معماری:

Dueling DQN تابع Q را به دو جزء تجزیه می‌کند:

Q(s, a; θ) = V(s; θ_v) + A(s, a; θ_a) - (1/|A|)·Σ_{a'} A(s, a'; θ_a)

- V(s): ارزش ذاتی حالت (مستقل از اقدام انتخابی)
- A(s,a): مزیت نسبی هر اقدام در آن حالت

این تفکیک به‌ویژه در بازارهای sideways مفید است — عامل یاد می‌گیرد که در حالت‌های neutral، هیچ اقدامی مزیت خاصی ندارد، بدون اینکه نیاز باشد تک‌تک Q مقادیر را ارزیابی کند.

---

۴. PPO — بهینه‌سازی سیاست با Clipping

PPO از خانواده Policy Gradient است و مستقیماً سیاست π(a|s; θ) را بهینه می‌کند — نه تابع ارزش را.

تابع هدف استاندارد Policy Gradient:

J(θ) = E_t[log π_θ(a_t|s_t) · A_t]

که A_t (Advantage) برتری اقدام انتخابی نسبت به میانگین را نشان می‌دهد:

A_t = R_t - V(s_t) # شکل ساده
A_t = Σ_{l=0}^{T} (γλ)^l · δ_{t+l} # GAE (Generalized Advantage Estimation)

مشکل TRPO و راه‌حل PPO:

به‌روزرسانی‌های بزرگ در Policy Gradient می‌توانند سیاست را به شکل فاجعه‌باری خراب کنند. TRPO با یک KL-divergence constraint این را کنترل کرد اما محاسباتی بسیار سنگین بود. PPO همین هدف را با یک Clipped Objective ساده‌تر حل کرد:

L^CLIP(θ) = E_t[ min( r_t(θ)·A_t , clip(r_t(θ), 1-ε, 1+ε)·A_t ) ]

جایی که:

r_t(θ) = π_θ(a_t|s_t) / π_{θ_old}(a_t|s_t) # نسبت سیاست جدید به قدیم

با ε = 0.2، نسبت r_t در بازه [0.8, 1.2] کلیپ می‌شود.

مثال عددی PPO:

فرض کنید:
- A_t = +0.50 (اقدام خرید مفید بوده)
- r_t = 1.35 (سیاست جدید این اقدام را ۳۵٪ بیشتر احتمال می‌دهد)

term1 = r_t · A_t = 1.35 × 0.50 = 0.675
term2 = clip(1.35, 0.8, 1.2) × 0.50 = 1.20 × 0.50 = 0.600

L^CLIP = min(0.675, 0.600) = 0.600

کلیپ کردن از gradient خیلی بزرگ جلوگیری کرد — سیاست نمی‌تواند بیش از ۲۰٪ شیفت کند.

PPO برای فضای پیوسته:

در ترید با تخصیص سرمایه پیوسته، خروجی شبکه یک توزیع گاوسی است:

π_θ(a|s) = N(μ_θ(s), σ_θ(s))

عامل وزن موقعیت دقیق (مثلاً ۰.۳۷ از سرمایه) را تعیین می‌کند، نه فقط یک انتخاب باینری خرید/فروش.

---

۵. طراحی محیط ترید و تابع پاداش

اسکلت محیط با OpenAI Gym:

python
class CryptoTradingEnv(gym.Env):
def __init__(self, df, initial_balance=10_000, fee=0.001):
self.observation_space = spaces.Box(
low=-np.inf, high=np.inf, shape=(n_features,), dtype=np.float32
)
self.action_space = spaces.Box(-1.0, 1.0, shape=(1,)) # پیوسته
self.fee = fee # کارمزد ۰.۱٪

def _compute_reward(self):
raw_return = (self.balance - self.prev_balance) / self.prev_balance
cost = abs(self.position_delta) * self.fee
return raw_return - cost

مقایسه توابع پاداش:

| تابع پاداش | مزیت | عیب |
|---|---|---|
| بازده ساده PnL_t | ساده | ریسک نادیده گرفته می‌شود |
| نسبت شارپ rolling | ریسک تنظیم‌شده | محاسبه پیچیده‌تر است |
| Sortino Ratio | فقط downside جریمه می‌شود | نیاز به تنظیم بیشتر |
| MDD-penalized | از drawdown جلوگیری می‌کند | وزن penalty حساس است |

رویکرد پیشرفته — پاداش تنظیم‌شده با ریسک:

R_t = (PnL_t - cost_t) - λ · max(0, -PnL_t)²

پارامتر λ کنترل می‌کند که عامل چقدر نسبت به ضرر ریسک‌گریز باشد. با λ = 2.0 ضررهای بزرگ به‌صورت چهارگوشی جریمه می‌شوند.

نرمال‌سازی State — جلوگیری از Data Leakage:

python

صحیح: rolling normalization


s_normalized = (s - rolling_mean(window=100)) / (rolling_std(window=100) + 1e-8)

غلط: نرمال‌سازی با کل داده — اطلاعات آینده وارد گذشته می‌شود!

s_wrong = (s - df.mean()) / df.std()

---

۶. چالش‌های کوانتی و ملاحظات عملی

Non-Stationarity — اصلی‌ترین چالش:

بازارهای کریپتو non-stationary هستند — رژیم‌های بازار (bull، bear، sideways) توزیع داده را تغییر می‌دهند. مدلی که روی داده ۲۰۲۱ آموزش دیده، در رژیم ۲۰۲۳ عملکرد متفاوتی خواهد داشت.

راه‌حل‌های رایج:
- Walk-Forward Training: هر ۳۰ روز، fine-tune روی داده جدید
- Meta-RL (MAML): عامل یاد می‌گیرد که سریع با رژیم جدید adapt شود
- Regime Detection: قبل از RL، رژیم بازار شناسایی و به State اضافه شود

هزینه‌های معاملاتی — محاسبه واقع‌بینانه:

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

total_cost = spread_cost + taker_fee + slippage
≈ 0.0005 + 0.0010 + 0.0002
= 0.0017 per trade (0.17%)

با فرض ۵۰ معامله در ماه:

monthly_drag = 50 × 0.17% = 8.5%

این عدد نشان می‌دهد چرا high-frequency RL استراتژی‌ها با هزینه‌های واقعی اغلب unprofitable هستند — هر الگوریتمی باید این سد را پشت سر بگذارد.

Reward Shaping در برابر Sparse Reward:

در ترید، پاداش اغلب sparse است — سود واقعی فقط در بسته شدن پوزیشن مشخص می‌شود. Reward Shaping با دادن سیگنال‌های intermediate یادگیری را سرعت می‌بخشد:

R_shaped = R_actual + F(s_{t+1}) - F(s_t)

جایی که F یک تابع shaping potential است (مثلاً unrealized PnL). شرط Ng et al. تضمین می‌کند که policy بهینه تغییر نکند.

Hyperparameter Sensitivity — مقادیر رایج:

| پارامتر | DQN | PPO |
|---|---|---|
| Learning Rate | 1e-4 تا 5e-4 | 3e-4 تا 1e-3 |
| Batch Size | 32–128 | 64–2048 |
| γ (Discount) | 0.95–0.99 | 0.99 |
| Replay Buffer | 50k–1M | N/A |
| ε-greedy (exploration) | 1.0→0.01 | entropy coeff |

---

نتیجه‌گیری

یادگیری تقویتی در ترید یکی از پیچیده‌ترین و جذاب‌ترین حوزه‌های کوانت مالی است. هر سه الگوریتم رویکرد متفاوتی دارند:

- Q-Learning: پایه نظری محکم با معادله بلمن، اما محدود به فضاهای گسسته کوچک
- DQN: با Experience Replay و Target Network مشکل مقیاس‌پذیری را حل می‌کند
- PPO: با Clipped Objective انعطاف‌پذیرترین الگوریتم برای فضاهای پیوسته است

خلاصه نقطه‌ای:
- مسئله ترید را با MDP فرمال کنید: (S, A, P, R) را دقیق تعریف کنید
- معادله بلمن پایه ریاضی همه الگوریتم‌های value-based است
- DQN با Experience Replay correlation بین نمونه‌ها را می‌شکند
- Target Network از divergence در آموزش جلوگیری می‌کند
- PPO با clip(r_t, 1-ε, 1+ε) به‌روزرسانی‌های مخرب را محدود می‌کند
- تابع پاداش بحرانی‌ترین تصمیم طراحی است — پاداش اشتباه، رفتار اشتباه می‌سازد
- هزینه ۰.۱۷٪ در هر معامله با ۵۰ ترید ماهانه = ۸.۵٪ drag ماهانه
- Non-stationarity بازار اصلی‌ترین چالش تعمیم‌پذیری مدل‌هاست
- از نرمال‌سازی با کل داده اجتناب کنید تا data leakage نداشته باشید

Reinforcement Learning — ترید با RL | آکادمی تریدیار | تریدیار