σμ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 AcademySkills
Ctrl K
AO
+1.2k Learners
LoginJoin ↗
Skills
AO
+1.2k Learners
Login
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python skill›Module 11 · Production systems & frontier models›Topic 11.12·~2 hrExpert

Reinforcement learning for trading

Skill positionModule 11 of 11 · Topic 12 of 13
11.12 Reinforcement learning for trading
—Lab score— / 100Total XP—Streak—

Learning by trying

In supervised learning every example comes with the right answer. Trading is not like that: nobody tells you the best position, you act and the market answers with a profit or a loss. Reinforcement learning models this as an agent that observes a state, takes an action, receives a reward and moves to a new state, with the goal of maximising the total reward over time. For trading the state can be recent returns or the current position, the actions buy, hold or sell, and the reward the profit of the step minus costs. The difficulty is that actions have consequences later and the agent must discover what pays by trying.

# 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: value of an action in a state

Q-learning keeps a table Q[state, action]: the total reward expected if you take action in state and act well afterwards. After each step it nudges the entry toward a better estimate, the target: the reward just received plus the discounted value of the best action in the next state, reward + gamma * max(Q[next_state]). The update is Q[s, a] += alpha * (target - Q[s, a]), with a learning rate alpha. The discount factor gamma (0 to 1) says how much future rewards matter: gamma = 0 is a short-sighted agent, near 1 a patient one, and the return of a sequence is r0 + gamma r1 + gamma^2 r2 + .... At a terminal step there is no next state and the target is just the reward.

# 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

Exploring, and a worked training loop

An agent that always takes the best action it knows never finds out whether another is better. Epsilon-greedy fixes that: with probability epsilon it explores a random action, otherwise it exploits the action with the largest Q, with ties going to the first. Epsilon usually starts high and decays. The loop is then: observe the state, choose an action, receive the reward and the next state, update Q, repeat. In a toy market where an up day tends to be followed by another up day, the agent that sees "yesterday was up" learns that holding long pays, and a table of two states and three actions is enough to show it.

# 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
A backtest of a learned policy is easy to foolAn agent trained on one stretch of history will find whatever pattern paid there, real or accidental, and market data is scarce and non-stationary: the lesson of one year may not hold the next. Train and test on separate periods (walk-forward), charge realistic costs and slippage inside the reward, penalise risk and large positions, compare with a trivial rule such as buy-and-hold, and be suspicious of any result that looks too good. Reinforcement learning is a research tool here more than a production strategy.
  • An agent observes a state, acts, receives a reward (profit minus costs) and learns which actions pay; the return is the discounted sum r0 + gamma r1 + gamma^2 r2 + ....
  • Q-learning updates Q[s, a] += alpha * (reward + gamma * max Q[s'] - Q[s, a]) (just reward at a terminal step) and never edits the old table in place here.
  • Epsilon-greedy explores with probability epsilon and otherwise exploits the largest Q; train and test on separate periods, charge realistic costs and compare with simple baselines.

Next up: the capstone, an end-to-end pricing or strategy library that puts everything together.

Lab · what's graded

  • Explore or exploit with choose_action25 pts
  • Learn from one step with q_update25 pts
  • Pay for trading in step_reward25 pts
  • Add up the future with discounted_return25 pts
Explorer · lab-141-1
▾ lab-141-1
…
bash · in-browser
learner@quantcraft:~/lab-141-1$
Python not loaded yetPythonLn 1, Col 1Spaces: 4UTF-8

Reset this topic?

Everything in lab-141-1 goes back to the starter files and the terminal is cleared. Your current solution will be lost — this can't be undone.

Will be reset
  • main.py → starter code
  • Terminal history and test output
  • Unsubmitted changes
Will be kept
  • Best lab score
  • Your XP and streak
  • Lecture progress and notes

Log in to run your code.

Every lecture is free to read. Create an account or log in to run code, run tests and submit this lab.

  • In-browser IDE with a bash terminal — nothing to install
  • Instant grading, XP and your place on the skill
Create an accountLog in
OR
Continue with GitHub