σμ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 AcademyRoadmapsSkills
Ctrl K
AO
+1.2k Learners
LoginJoin ↗
RoadmapsSkills
AO
+1.2k Learners
Login
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
σμ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 AcademyRoadmapsSkills
Ctrl K
AO
+1.2k Learners
LoginJoin ↗
RoadmapsSkills
AO
+1.2k Learners
Login
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
SciPy skill›Module 3 · Matrices, signals and structure›Topic 3.6·~45 minIntermediate

Causal filters for trading signals

Skill positionModule 3 of 3 · Topic 6 of 10
3.6 Causal filters for trading signals
—Lab score— / 100Total XP—Streak—

A smooth line that has seen the future

A filter turns an input sequence xₜ into an output yₜ as a weighted sum of recent inputs and recent outputs. The exponential moving average is the simplest: yₜ=αxₜ+(1-α)yₜ₋₁. In scipy.signal a filter is a pair of coefficient lists, b for the inputs and a for the outputs, and yₜ=b₀xₜ-a₁yₜ₋₁ here with b = [alpha] and a = [1, -(1 - alpha)]. signal.lfilter(b, a, x) runs it forward in time, exactly like a live system, so yₜ depends only on xₛ with s≤t. By default it starts from rest (zero state), which makes the first values wrong for a price near 100; pass zi = signal.lfilter_zi(b, a) * x[0] to start from the first value instead. The result agrees with pd.Series(x).ewm(alpha=alpha, adjust=False).mean() to machine precision.

signal.filtfilt(b, a, x) runs the same filter forwards and then backwards. The lag cancels, so the smooth line sits right on the data, which looks perfect on a chart, but every output now depends on inputs from after it. Used to build a trading signal it is a leak. Take a trend signal, the log price minus its average, and correlate it with the next day's return: for SPY over 2,501 days and α=0.1 the causal signal has a correlation of -0.055 (a faint mean reversion), the zero-phase one -0.33; for α=0.3 the figures are -0.076 and -0.54. No trader could earn those zero-phase numbers, because the filtered value already contains tomorrow's price. The test of a signal is simple: truncate the data at some date and check that the values before it do not change.

  • lfilter is live, filtfilt is hindsight: only the first can run on a trading desk.
  • Set the initial state: lfilter_zi(b, a) * x[0] removes the start-up transient.
  • Truncation test: cut the data at a date; a valid signal does not change before it.

Lab · what's graded

  • Filter coefficients10 pts
  • The causal average20 pts
  • The pandas version10 pts
  • The zero-phase average15 pts
  • Signals and tomorrow's return25 pts
  • The SPY study20 pts
Explorer · lab-sp-21
▾ lab-sp-21
▾ data
…
bash · in-browser
learner@quantcraft:~/lab-sp-21$
Python not loaded yetPythonLn 1, Col 1Spaces: 4UTF-8

Reset this topic?

Everything in lab-sp-21 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