Торговый агент на TD3 (Twin Delayed Deep Deterministic Policy Gradient)
TD3 — улучшенный DDPG от Scott Fujimoto (2018). Исправляет три проблемы DDPG: overestimation, высокую variance, нестабильность. Off-policy, continuous actions, детерминированная policy. Для трейдинга с непрерывным position sizing — конкурент SAC.
TD3 vs DDPG vs SAC
DDPG страдает от Q-overestimation: Q-функция завышает ожидаемые returns → policy оптимизируется под нереалистичные цели → плохое реальное поведение.
TD3 исправляет тремя трюками:
1. Twin Q-networks (double Q): Два независимых критика Q₁, Q₂. Target использует min(Q₁, Q₂):
y = r + γ · min(Q₁_target(s', π(s')), Q₂_target(s', π(s')))
Консервативная оценка → меньше overestimation.
2. Delayed policy updates: Policy обновляется каждые d шагов (d=2 по умолчанию), критики — каждый шаг. Менее стохастичные Q-оценки перед обновлением actor.
3. Target policy smoothing: Шум добавляется к target actions при вычислении Q-targets:
noise = (torch.randn_like(action) * policy_noise).clamp(-noise_clip, noise_clip)
next_action = (target_actor(next_state) + noise).clamp(-1, 1)
Регуляризация: критик не перефитирует к узким пикам Q-поверхности.
Архитектура для трейдинга
class TD3Actor(nn.Module):
def __init__(self, state_dim, action_dim, max_action):
super().__init__()
self.net = nn.Sequential(
nn.Linear(state_dim, 256), nn.ReLU(),
nn.Linear(256, 256), nn.ReLU(),
nn.Linear(256, action_dim), nn.Tanh()
)
self.max_action = max_action
def forward(self, state):
return self.net(state) * self.max_action
class TD3Critic(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
# Q1
self.q1 = nn.Sequential(
nn.Linear(state_dim + action_dim, 256), nn.ReLU(),
nn.Linear(256, 256), nn.ReLU(),
nn.Linear(256, 1)
)
# Q2
self.q2 = nn.Sequential(
nn.Linear(state_dim + action_dim, 256), nn.ReLU(),
nn.Linear(256, 256), nn.ReLU(),
nn.Linear(256, 1)
)
def forward(self, state, action):
sa = torch.cat([state, action], dim=1)
return self.q1(sa), self.q2(sa)
def q1_forward(self, state, action):
sa = torch.cat([state, action], dim=1)
return self.q1(sa)
Трейдинг-специфичный reward и action space
Continuous position sizing: Action [-1, 1]: -1 = 100% short, 0 = flat, +1 = 100% long. Portfolio с N активами: action ∈ [-1,1]^N, нормировать до Σ|wᵢ| ≤ 1.
Reward с учётом risk:
# Sharpe-like reward
def reward_fn(returns_series):
if len(returns_series) < 20:
return returns_series[-1]
mean_r = np.mean(returns_series[-20:])
std_r = np.std(returns_series[-20:]) + 1e-8
sharpe = mean_r / std_r
return sharpe * returns_series[-1] # масштабирование
Transaction cost penalization:
position_change = np.abs(new_position - old_position)
transaction_cost = position_change * 0.001 # 0.1%
reward -= transaction_cost
Это заставляет агента избегать гиперактивной торговли.
Exploration: Ornstein-Uhlenbeck vs Gaussian
Оригинальный DDPG использует OU noise для exploration (временно коррелированный шум). TD3 переходит на простой Gaussian noise:
# Gaussian exploration (рекомендуется для TD3)
action = actor(state) + np.random.normal(0, exploration_noise, action_dim)
action = np.clip(action, -max_action, max_action)
Для трейдинга: exploration_noise = 0.1–0.3. Decay по мере обучения.
Обучение
from stable_baselines3 import TD3
from stable_baselines3.common.noise import NormalActionNoise
action_noise = NormalActionNoise(
mean=np.zeros(n_actions),
sigma=0.1 * np.ones(n_actions)
)
model = TD3(
"MlpPolicy",
env,
action_noise=action_noise,
learning_rate=3e-4,
buffer_size=1_000_000,
learning_starts=10_000,
batch_size=256,
tau=0.005,
gamma=0.99,
train_freq=(1, "episode"),
gradient_steps=-1, # gradient_steps = episodes collected
policy_delay=2, # delayed policy update
target_policy_noise=0.2,
target_noise_clip=0.5,
verbose=1
)
model.learn(total_timesteps=500_000)
Когда TD3 выигрывает у SAC
TD3 — детерминированная policy (с exploration noise). SAC — stochastic policy с entropy bonus.
TD3 предпочтителен когда:
- Нужна воспроизводимость live торговли (детерминированные сигналы)
- Рынок с чёткими трендами (низкая энтропия оптимальна)
- Хочется явно контролировать exploration через noise schedule
SAC предпочтителен когда:
- Высокая неопределённость рынка
- Нужна naturalistic diversification позиций
Сроки: 5–9 недель
TD3 baseline с continuous position sizing — 3 недели. Multi-asset, OU/adaptive noise, risk-adjusted reward, интеграция с брокерским API — 7–9 недель.







