Skip to content

Add WaveTrend Oscillator indicator#9429

Open
AlexCatarino wants to merge 1 commit intoQuantConnect:masterfrom
AlexCatarino:feature-6411-wavetrend-oscillator
Open

Add WaveTrend Oscillator indicator#9429
AlexCatarino wants to merge 1 commit intoQuantConnect:masterfrom
AlexCatarino:feature-6411-wavetrend-oscillator

Conversation

@AlexCatarino
Copy link
Copy Markdown
Member

@AlexCatarino AlexCatarino commented Apr 21, 2026

Description

Implements WaveTrendOscillator (extends BarIndicator) per the linked issue. Adds the indicator class, the WTO helper in QCAlgorithm.Indicators.cs, unit tests inheriting CommonIndicatorTests<IBaseDataBar>, and the reference CSV under Tests/TestData/.

The main line (WT1) is an EMA of a normalized channel index derived from HLC3 and its EMA; the signal line (WT2) is a short SMA of the main line:

hlc3 = (High + Low + Close) / 3
esa  = EMA(hlc3, channelPeriod)
d    = EMA(|esa - hlc3|, channelPeriod)
ci   = (hlc3 - esa) / (0.015 * d)
wt1  = EMA(ci, averagePeriod)   (main line, exposed via Current.Value and ChannelIndexAverage)
wt2  = SMA(wt1, signalPeriod)   (signal line, exposed via Signal)

Each sub-indicator (ChannelAverage, ChannelDeviation, ChannelIndexAverage, Signal) is a public read-only property so callers can plot or chain them. IsReady becomes true only once Signal has accumulated signalPeriod samples downstream of the chained EMAs (WarmUpPeriod = 2 * channelPeriod + averagePeriod + signalPeriod - 3).

Related Issue

Closes #6411

Motivation and Context

The WaveTrend Oscillator is a widely-followed momentum oscillator on TradingView for identifying overbought/oversold conditions and main/signal crossovers. Issue #6411 requested it as a first-class LEAN indicator so algorithms can use it without hand-rolling the EMA/SMA chain. Reference: https://www.tradingview.com/script/2KE8wTuF-Indicator-WaveTrend-Oscillator-WT/

Requires Documentation Change

Yes — QuantConnect/Documentation#2309

How Has This Been Tested?

  • dotnet build QuantConnect.Lean.sln
  • dotnet test Tests/QuantConnect.Tests.csproj --filter "FullyQualifiedName~WaveTrendOscillatorTests" — all 14 tests pass (main-line external-data comparison, signal-line external-data comparison, reset, warm-up, renko, volume-renko, time-moves-forward, constructor argument validation, sub-indicator readiness ordering, etc.).
  • Reference values generated via TA-Lib (talib.EMA, talib.SMA) following the script posted by @LouisSzeto in Implement WaveTrend Oscillator Indicator #6411; main and signal lines match within 1e-4.

The reference CSV at Tests/TestData/spy_wto.csv was produced with the script below. Reviewers can paste it into a file and re-run to reproduce the expected values:

Test data generator
"""
Test data generator for WaveTrendOscillator (WTO).

Source: Tier 1 - TA-Lib (talib.EMA + talib.SMA), per the formula in
QuantConnect/Lean issue #6411 (https://github.com/QuantConnect/Lean/issues/6411).

The reference implementation matches the script posted by @LouisSzeto in the
issue thread; only the output format differs (we keep the OHLCV columns and
write a header so the LEAN test framework can parse the file).

Generated: 2026-05-04
Usage:     python generate_test_data.py
Output:    spy_wto.csv

Dependencies: ta-lib, numpy, pandas
"""

import pandas as pd
import talib


def main() -> None:
    url = (
        "https://github.com/QuantConnect/Lean/raw/refs/heads/master/"
        "Data/equity/usa/daily/spy.zip"
    )
    history = pd.read_csv(
        url,
        names=["Date", "Open", "High", "Low", "Close", "Volume"],
    )

    # Lean stores prices scaled by 10000 in the daily zip files.
    for col in ["Open", "High", "Low", "Close"]:
        history[col] = pd.to_numeric(history[col], errors="coerce") / 10000.0
    history["Volume"] = pd.to_numeric(history["Volume"], errors="coerce")

    history["Date"] = pd.to_datetime(history["Date"], format="%Y%m%d %H:%M").dt.strftime(
        "%Y-%m-%d"
    )

    channel_period = 10
    average_period = 21
    signal_period = 4

    typical_price = (history["High"] + history["Low"] + history["Close"]) / 3.0
    channel_average = talib.EMA(typical_price, timeperiod=channel_period)
    channel_deviation = talib.EMA(
        (channel_average - typical_price).abs(), timeperiod=channel_period
    )
    channel_index = (typical_price - channel_average) / (0.015 * channel_deviation)
    wt1 = talib.EMA(channel_index, timeperiod=average_period)
    wt2 = talib.SMA(wt1, timeperiod=signal_period)

    history["WT1"] = wt1
    history["WT2"] = wt2

    history.to_csv("spy_wto.csv", index=False, float_format="%.6f")


if __name__ == "__main__":
    main()

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

  • My code follows the code style of this project.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.
  • My branch follows the naming convention feature-<issue>-<description>

Implement WaveTrendOscillator (bar indicator) per the linked issue. Adds
the indicator class, the WTO helper in QCAlgorithm.Indicators.cs, unit
tests inheriting CommonIndicatorTests<IBaseDataBar>, and the reference
CSV under Tests/TestData/.

The oscillator is computed from the typical price (HLC/3): an EMA
smooths it (ESA), a second EMA tracks its absolute deviation (D), the
normalized channel index (HLC3 - ESA) / (0.015 * D) is smoothed by a
third EMA to produce WT1, and an SMA of WT1 produces the signal line
WT2. Crossovers between WT1 and WT2 are commonly used as entry and exit
signals for momentum reversals.

Reference values were generated with TA-Lib (talib.EMA + talib.SMA)
following the script posted by @LouisSzeto in the issue thread.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@AlexCatarino AlexCatarino force-pushed the feature-6411-wavetrend-oscillator branch from 1162eb7 to 6900147 Compare May 4, 2026 20:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement WaveTrend Oscillator Indicator

1 participant