σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademyRoteiros
Ctrl K
AO
+1,2 mil alunos
EntrarParticipar ↗
Roteiros
AO
+1,2 mil alunos
Entrar
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Roteiro de Python›Módulo 11 · Sistemas de produção e modelos de ponta›Tópico 11.12·~2 hEspecialista

Aprendizado por reforço para trading

Posição no roteiroMódulo 11 de 11 · Tópico 12 de 13
11.12 Aprendizado por reforço para trading
—Nota do lab— / 100XP total—Sequência—

Aprender tentando

No aprendizado supervisionado cada exemplo vem com a resposta certa. O trading não é assim: ninguém diz a melhor posição, você age e o mercado responde com lucro ou prejuízo. O aprendizado por reforço modela isso como um agente que observa um estado, toma uma ação, recebe uma recompensa e passa a um novo estado, com o objetivo de maximizar a recompensa total ao longo do tempo. No trading o estado pode ser os retornos recentes ou a posição atual, as ações comprar, manter ou vender, e a recompensa o lucro do passo menos os custos. A dificuldade é que as ações têm consequências depois e o agente precisa descobrir o que paga tentando.

# Reward of one step: profit from the position held, minus the cost of changing position
def step_reward(position, price_change, previous_position, cost):
    return position * price_change - cost * abs(position - previous_position)

print(round(step_reward(1, 0.5, 0, 0.1), 4))         # 0.4    went long, the price rose 0.5, paid 0.1 to trade
print(round(step_reward(-1, 0.5, 1, 0.1), 4))        # -0.7   flipped from long to short just before a rise: lost 0.5 and paid 0.2
print(step_reward(1, 0.5, 1, 0.1))                   # 0.5    holding the same position costs nothing

Q-learning: o valor de uma ação em um estado

O Q-learning mantém uma tabela Q[estado, ação]: a recompensa total esperada se você toma ação em estado e depois age bem. Após cada passo empurra a entrada para uma estimativa melhor, o alvo: a recompensa recém-recebida mais o valor descontado da melhor ação no estado seguinte, reward + gamma * max(Q[estado seguinte]). A atualização é Q[s, a] += alpha * (alvo - Q[s, a]), com uma taxa de aprendizado alpha. O fator de desconto gamma (0 a 1) diz o quanto as recompensas futuras importam: gamma = 0 é um agente míope, perto de 1 um paciente, e o retorno de uma sequência é r0 + gamma r1 + gamma^2 r2 + .... Num passo terminal não há estado seguinte e o alvo é só a recompensa.

# One Q-learning update on a table of 2 states x 3 actions (short, flat, long)
import numpy as np

def q_update(q, state, action, reward, next_state, alpha, gamma):
    updated = q.copy()
    target = reward if next_state is None else reward + gamma * updated[next_state].max()
    updated[state, action] += alpha * (target - updated[state, action])
    return updated

q = np.zeros((2, 3))
q = q_update(q, 0, 2, 1.0, 1, 0.5, 0.9)               # in state 0 we went long and earned 1.0
print(q[0])                                           # [0.  0.  0.5]   moved halfway (alpha 0.5) toward the target of 1.0
q = q_update(q, 0, 2, 1.0, 1, 0.5, 0.9)
print(q[0])                                           # [0.   0.   0.75]   a second visit moves it further

Explorar e um laço de treino completo

Um agente que sempre toma a melhor ação que conhece nunca descobre se outra é melhor. O epsilon-greedy resolve: com probabilidade epsilon explora uma ação aleatória, senão explora a ação de maior Q, e os empates vão para a primeira. O epsilon costuma começar alto e decair. O laço é então: observar o estado, escolher uma ação, receber a recompensa e o estado seguinte, atualizar Q, repetir. Num mercado de brinquedo em que um dia de alta costuma ser seguido de outro, o agente que vê «ontem foi de alta» aprende que ficar comprado paga, e uma tabela de dois estados e três ações basta para mostrar isso.

# A toy market where an up day tends to be followed by another up day; the agent learns to follow the trend
import random
import numpy as np

rng = random.Random(1)
positions = [-1, 0, 1]                                # action 0 = short, 1 = flat, 2 = long
q = np.zeros((2, 3))                                  # state 0 = yesterday down, state 1 = yesterday up
alpha, gamma, cost = 0.1, 0.9, 0.0005
state, previous = 1, 0
for step in range(20000):
    epsilon = max(0.05, 1 - step / 10000)
    u, explore = rng.random(), rng.randrange(3)
    action = explore if u < epsilon else int(np.argmax(q[state]))
    up = rng.random() < (0.65 if state == 1 else 0.35)           # the trend continues 65% of the time
    change = 0.01 if up else -0.01
    reward = positions[action] * change - cost * abs(positions[action] - previous)
    next_state = 1 if up else 0
    target = reward + gamma * q[next_state].max()
    q[state, action] += alpha * (target - q[state, action])
    state, previous = next_state, positions[action]

print(int(np.argmax(q[0])), int(np.argmax(q[1])))     # 0 2   after a down day go short, after an up day go long
# The discounted return: with gamma below 1 a reward now is worth more than the same reward later
def discounted_return(rewards, gamma):
    total = 0.0
    for reward in reversed(rewards):
        total = reward + gamma * total
    return total

rewards = [1.0, 1.0, 1.0]
print(discounted_return(rewards, 0.5))               # 1.75   1 + 0.5 + 0.25
print(discounted_return(rewards, 1.0))               # 3.0    a patient agent: a plain sum
print(discounted_return(rewards, 0.0))               # 1.0    a short-sighted agent: only the first reward counts
O backtest de uma política aprendida é fácil de enganarUm agente treinado num trecho da história achará qualquer padrão que pagou ali, real ou acidental, e os dados de mercado são escassos e não estacionários: a lição de um ano pode não valer no seguinte. Treine e teste em períodos separados (walk-forward), cobre custos e slippage realistas dentro da recompensa, penalize o risco e posições grandes, compare com uma regra trivial como comprar e manter e desconfie de qualquer resultado bom demais. O aprendizado por reforço é aqui mais uma ferramenta de pesquisa do que uma estratégia de produção.
  • Um agente observa um estado, age, recebe uma recompensa (lucro menos custos) e aprende quais ações pagam; o retorno é a soma descontada r0 + gamma r1 + gamma^2 r2 + ....
  • O Q-learning atualiza Q[s, a] += alpha * (reward + gamma * max Q[s'] - Q[s, a]) (só reward num passo terminal) e aqui não edita a tabela antiga no lugar.
  • O epsilon-greedy explora com probabilidade epsilon e senão explora o maior Q; treine e teste em períodos separados, cobre custos realistas e compare com referências simples.

A seguir: o projeto final, uma biblioteca completa de precificação ou de estratégia que reúne tudo.

Lab: o que é avaliado

  • Explorar ou explorar o conhecido com choose_action25 pts
  • Aprender de um passo com q_update25 pts
  • Cobrar pelo trading em step_reward25 pts
  • Somar o futuro com discounted_return25 pts
Explorador · lab-141-1
▾ lab-141-1
…
bash · no navegador
learner@quantcraft:~/lab-141-1$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

Tudo em lab-141-1 volta aos arquivos iniciais e o terminal é limpo. Sua solução atual será perdida — isso não pode ser desfeito.

Será redefinido
  • main.py → código inicial
  • Histórico do terminal e saída dos testes
  • Alterações não enviadas
Será mantido
  • Melhor nota do lab
  • Seu XP e sua sequência
  • Progresso da aula e anotações

Entre para executar o seu código.

Todas as aulas são gratuitas para ler. Crie uma conta ou entre para executar código, rodar os testes e enviar este lab.

  • IDE no navegador com terminal bash — nada para instalar
  • Correção instantânea, XP e o seu lugar no roteiro
Criar uma contaEntrar
OU
Continuar com o GitHub