Reinforcement Learning in Financial Decision Making — Systematic Review
Authors: Mohammad Rezoanul Hoque và cộng sự (2025) Source: arXiv:2512.10913 (preprint, công bố 11/12/2025) Tag:
moi:2026-05-16#reinforcement-learning#review#machine-learning#implementation#preprint
Ý tưởng cốt lõi
Hoque et al. (2025) thực hiện một systematic review phân tích 167 paper RL-finance công bố 2017-2025, tập trung vào ba mảng ứng dụng chính: market making, portfolio optimization, và algorithmic trading. Đây là review có scope rộng nhất từ trước đến nay về chủ đề này, đưa ra một số kết luận đảo ngược "trí tuệ cộng đồng":
Phát hiện đáng giá nhất: chất lượng implementation và domain knowledge quan trọng hơn lựa chọn algorithm. Cụ thể:
- Tăng "implementation quality" có thể đem lại +25% độ tin cậy hệ thống (reliability).
- Tích hợp domain knowledge (financial features được thiết kế kỹ) cải thiện performance +5-20%.
- Algorithm choice (DQN vs PPO vs SAC vs A3C) có tác động ít hơn nhiều so với hai yếu tố trên.
Điều này đảo ngược cách tiếp cận thông thường — nhiều paper RL-finance dành 80% thời gian "đua" algorithm mới (transformer-based, multi-agent...) và chỉ 20% cho data engineering / feature engineering. Hoque et al. argue đó là upside-down.
Bốn vấn đề mở (open problems) được nêu:
- Explainability: RL agent thường "black box", khó pass compliance review.
- Robustness in non-stationary environments: thị trường tài chính bản chất non-stationary (xem AMH paper) — RL training trên past data có rủi ro distribution shift cao.
- Standardized benchmarking: chưa có benchmark suite chuẩn để so sánh paper với paper — mỗi paper dùng tập data và metric khác.
- Deployment feasibility: từ paper đến live trading còn rất xa — paper review nhấn mạnh "production gap".
Paper đề xuất một unified framework xử lý 4 vấn đề này: requires interpretable architectures, robustness testing (cross-regime), standardized benchmarks, và deployment-ready engineering.
Ứng dụng giao dịch chính
Bài học actionable cho quant practitioner:
Stop chasing algorithms: nếu đang dùng PPO mà chưa khai thác hết domain features, đừng switch sang transformer hay GNN. Focus vào features.
Implementation quality checklist:
- Reward function carefully designed (không chỉ raw P&L — cần Sharpe-like reward, penalty cho drawdown, slippage included).
- State representation đủ rich (price + volume + microstructure + cross-asset + regime context).
- Action space realistic (limit orders, không phải instant fill at mid).
- Training environment incorporate transaction cost, slippage, market impact.
Non-stationarity handling:
- Train trên rolling windows, refresh weight monthly.
- Curriculum learning: train từ regime dễ (bull steady) sang khó (crisis).
- Use out-of-regime test để evaluate robustness, không chỉ standard out-of-sample.
Benchmark riêng: build internal benchmark suite gồm các baseline (buy-hold, equal weight, 60/40, naive momentum) — so sánh RL agent của bạn với benchmark này, không chỉ với SOTA papers.
Áp dụng đa thị trường
VN30F (Hợp đồng tương lai chỉ số Việt Nam)
- RL có thể work trên VN30F nhưng cần nhiều adaptation:
- Data thấp (chỉ 8 năm 2017-2025) → khó train deep RL agent. Khuyến nghị dùng transfer learning từ ES/NQ agent rồi fine-tune.
- Domain features critical: phải có features về cơ sở VN30 (P/E, P/B của 30 mã thành phần), foreign flow, T+0 vs T+2 rule effects.
- Reward function: penalize drawdown nặng vì retail investor không chịu được DD > 20%.
US equity futures (ES, NQ, RTY)
- US futures là môi trường tốt nhất để bắt đầu vì:
- Data dài (30+ năm intraday).
- Liquidity cao → simulation chính xác.
- Có nhiều paper benchmark có sẵn để so sánh.
- Hoque review note: market making trên ES là use case RL thắng nhiều nhất (vs Avellaneda-Stoikov classical), portfolio optimization là use case thắng ít nhất.
Crypto spot (BTC, ETH, top altcoins)
- Crypto là môi trường lý tưởng cho RL vì:
- 24/7 trading → nhiều episode để train.
- Free historical tick data (Binance, Bybit public).
- Volatility cao → reward signal mạnh.
- Cảnh báo của review: nhiều paper RL-crypto overfitting nặng vì training trên 2017-2021 (bull cycle) và test trên 2022-2023 (bear). Kết quả không generalizable.
- Khuyến nghị: train trên multi-cycle data, test trên held-out cycle.
Crypto perpetual futures
- Perp có 2 reward signals: directional alpha + funding carry. RL agent có thể học cả hai cùng lúc.
- Funding rate là strong feature mà nhiều paper bỏ sót — luôn include vào state.
- Liquidation risk cần model carefully — không phải continuous cost mà discrete jump risk.
Cân nhắc cross-market chung
- Domain expertise > algorithm: một quant với 10 năm experience trên một market sẽ build RL agent tốt hơn ML engineer dùng SOTA algorithm trên cùng market đó (theo review). Pair up.
- Don't trust paper benchmarks blindly: kiểm tra reproducibility từng paper bạn quote — review chỉ ra many published RL-finance results không reproduce được.
Minh họa Python
import numpy as np
import pandas as pd
class TradingEnvWithDomainFeatures:
"""
Trading env minimal cho RL agent, theo guideline Hoque 2025:
- State rich (features đa chiều, không chỉ price).
- Reward Sharpe-like + drawdown penalty.
- Realistic cost & slippage.
"""
def __init__(self, price_df: pd.DataFrame,
feature_df: pd.DataFrame,
transaction_cost_bps: float = 1.0,
slippage_bps: float = 0.5,
dd_penalty_weight: float = 2.0):
self.prices = price_df
self.features = feature_df # rich domain features
self.tc = transaction_cost_bps / 1e4
self.slip = slippage_bps / 1e4
self.dd_pen = dd_penalty_weight
self.reset()
def reset(self):
self.t = 0
self.position = 0.0 # -1 to +1
self.equity = 1.0
self.peak_equity = 1.0
self.equity_history = [1.0]
return self._get_state()
def _get_state(self) -> np.ndarray:
"""State = current features + position + equity_drawdown."""
feat = self.features.iloc[self.t].values
dd = (self.equity - self.peak_equity) / self.peak_equity if self.peak_equity > 0 else 0
return np.concatenate([feat, [self.position, dd]])
def step(self, action: float):
"""action in [-1, 1] = target position. Reward = Δ equity - cost - dd_penalty."""
action = np.clip(action, -1.0, 1.0)
# Transaction cost + slippage proportional to position change
delta_pos = action - self.position
cost = (self.tc + self.slip) * abs(delta_pos)
# PnL
ret = self.prices.iloc[self.t + 1] / self.prices.iloc[self.t] - 1
pnl = action * ret - cost
self.equity *= (1 + pnl)
self.peak_equity = max(self.peak_equity, self.equity)
self.equity_history.append(self.equity)
self.position = action
# Reward: Sharpe-like (recent volatility-adjusted return) + dd penalty
recent_pnl = pd.Series(self.equity_history).pct_change().tail(20)
if recent_pnl.std() > 0:
sharpe_inst = recent_pnl.mean() / recent_pnl.std()
else:
sharpe_inst = recent_pnl.mean()
current_dd = (self.equity - self.peak_equity) / self.peak_equity if self.peak_equity > 0 else 0
reward = sharpe_inst + self.dd_pen * current_dd # dd_pen × negative number → penalty
self.t += 1
done = (self.t >= len(self.prices) - 1)
return self._get_state(), reward, done, {}
# Pattern usage với stable-baselines3:
# env = TradingEnvWithDomainFeatures(price_df, feature_df)
# from stable_baselines3 import SAC
# model = SAC('MlpPolicy', env, verbose=1)
# model.learn(total_timesteps=200_000)