Торговый агент на SAC (Soft Actor-Critic)
SAC — off-policy алгоритм с maximum entropy reinforcement learning. Оптимизирует не только reward, но и энтропию policy — агент учится быть хорошим И максимально разнообразным. Для трейдинга это означает: не застрять в одной стратегии, лучше исследовать рыночные режимы.
Принцип Maximum Entropy RL
Стандартный RL: max E[R]. SAC: max E[R + α·H(π)].
H(π) = -E[log π(a|s)] — энтропия policy. α — температура (auto-tuning в SAC v2).
Практически: агент предпочитает две одинаково прибыльные стратегии той, которая более стохастична. В трейдинге: устойчивость к переобучению к конкретному рыночному режиму.
SAC vs PPO для трейдинга
| Характеристика | SAC | PPO |
|---|---|---|
| Тип | Off-policy | On-policy |
| Replay buffer | Есть (1M+) | Нет |
| Sample efficiency | Высокая | Средняя |
| Стабильность обучения | Высокая | Высокая |
| Action space | Continuous (лучше) | Continuous/Discrete |
| Инфраструктура | Сложнее (replay) | Проще |
SAC предпочтителен при: ограниченном объёме исторических данных, непрерывных action (веса портфеля), необходимости sample-efficient обучения.
Архитектура SAC
Три сети:
- Policy network π_θ(a|s): Gaussian policy с reparameterization trick
- Two Q-networks Q_φ1, Q_φ2: double Q trick для уменьшения overestimation bias
- Target Q-networks (EMA копии): стабилизация обучения
import torch
import torch.nn as nn
from torch.distributions import Normal
class SACPolicy(nn.Module):
def __init__(self, state_dim, action_dim, hidden=256):
super().__init__()
self.net = nn.Sequential(
nn.Linear(state_dim, hidden), nn.ReLU(),
nn.Linear(hidden, hidden), nn.ReLU()
)
self.mean_layer = nn.Linear(hidden, action_dim)
self.log_std_layer = nn.Linear(hidden, action_dim)
self.LOG_STD_MIN, self.LOG_STD_MAX = -20, 2
def forward(self, state):
feat = self.net(state)
mean = self.mean_layer(feat)
log_std = self.log_std_layer(feat).clamp(self.LOG_STD_MIN, self.LOG_STD_MAX)
std = log_std.exp()
dist = Normal(mean, std)
# reparameterization: a = tanh(mean + std * ε)
action = torch.tanh(dist.rsample())
log_prob = dist.log_prob(action).sum(-1, keepdim=True)
# поправка для tanh squashing
log_prob -= torch.log(1 - action.pow(2) + 1e-6).sum(-1, keepdim=True)
return action, log_prob
Автоматическая настройка температуры α
SAC v2 убирает ручную настройку α. Целевая энтропия = -dim(action_space):
target_entropy = -action_dim # для 5 активов = -5
log_alpha = torch.zeros(1, requires_grad=True)
alpha_optimizer = torch.optim.Adam([log_alpha], lr=3e-4)
# alpha loss (обновляется каждый шаг)
alpha_loss = -(log_alpha * (log_pi + target_entropy).detach()).mean()
alpha_optimizer.zero_grad()
alpha_loss.backward()
alpha_optimizer.step()
alpha = log_alpha.exp().item()
Replay Buffer для финансовых временных рядов
Стандартный uniform replay buffer не учитывает временную структуру. Prioritized Experience Replay (PER): сэмплирует transitions с высоким TD-error чаще.
Temporal replay buffer: хранит не i.i.d. transitions, а последовательности (для LSTM policy):
- Sequence length = 20 (20 дней контекст)
- При сэмплировании берётся случайный непрерывный отрезок
- BPTT через всю последовательность
class SequenceReplayBuffer:
def __init__(self, capacity, seq_len):
self.buffer = deque(maxlen=capacity)
self.seq_len = seq_len
def sample_sequences(self, batch_size):
starts = np.random.randint(0, len(self.buffer) - self.seq_len, batch_size)
return [list(self.buffer)[s:s+self.seq_len] for s in starts]
Реализация через Stable Baselines3
from stable_baselines3 import SAC
model = SAC(
"MlpPolicy",
env,
learning_rate=3e-4,
buffer_size=1_000_000,
learning_starts=10_000, # прогрев без обновлений
batch_size=256,
tau=0.005, # EMA для target networks
gamma=0.99,
train_freq=1,
gradient_steps=1,
ent_coef='auto', # auto-tuning α
target_entropy='auto',
verbose=1
)
model.learn(total_timesteps=500_000)
learning_starts критично для трейдинга: первые 10K шагов — случайное исследование без обновления сетей. Наполняет replay buffer разнообразными experiences.
Сравнение производительности
При прочих равных SAC обычно превосходит PPO по Sharpe Ratio на 10–15% за счёт лучшего исследования и sample efficiency. Но требует больше GPU памяти (replay buffer) и сложнее в отладке.
Сроки: 6–10 недель
Базовый SAC на OHLCV данных — 3–5 недель. PER + sequence replay, LSTM policy, live подключение к брокеру — 8–10 недель.







