Adaptive Trend Ensemble [BackQuant] For ThinkOrSwim

Typegod

New member
nJayuyY.png


# Adaptive Trend Ensemble [BackQuant]
# Converted from Pine Script v6 to thinkScript - W. Kalvin

Author states:
Adaptive Trend Ensemble is an online-learning trend filter that combines eight different moving-average methods into one continuously weighted trend estimate.

Instead of selecting one moving average permanently, the indicator treats each method as an independent forecasting expert. Every bar, each expert is evaluated according to whether its previous slope correctly anticipated the direction of the latest price move.

Experts that were directionally correct retain more influence. Experts that were wrong lose influence through a multiplicative penalty. The weights are then normalised and used to blend all eight moving-average values into one adaptive ensemble line.

The indicator therefore attempts to answer two separate questions:
  • Which smoothing method has recently aligned best with price direction?*
  • How strongly do the weighted methods currently agree on the direction of trend?

The final output includes:
  • A dynamically weighted ensemble trend line.
  • Bullish and bearish trend-state colouring.
  • A gradient between price and the ensemble.
  • A consensus-driven glow.
  • Trend-coloured candles.
  • A live label showing the leading expert and its current weight.
  • Alerts when the ensemble trend changes direction.

This is not a fixed moving average and it is not a simple average of several indicators. The contribution of each expert changes over time according to its recent directional performance.

Original Tradingivew script:
https://www.tradingview.com/script/hnrtyc7O-Adaptive-Trend-Ensemble-BackQuant/

Here is the ThinkOrSwim script:
Code:
# Adaptive Trend Ensemble [BackQuant]
# Converted from Pine Script v6 to thinkScript - W. Kalvin

# -------------------------------------------------------------------
# Inputs
# -------------------------------------------------------------------
input src = close;
input len = 30; # Base Lookback Length for all Moving Averages

# Online Learning Settings
input eta = 2.0; # Learning Rate (η)
input lossMode = {default "Directional (0/1)", "Magnitude-weighted"};
input floorW = 0.0; # Weight Floor

# UI Settings
input smooth = 25; # Line Smoothing (EMA)
input paintBar = yes; # Color Bars
input showClouds = yes; # Display Cloud Fill

# -------------------------------------------------------------------
# Moving Average Experts Bank (8 Base Models)
# -------------------------------------------------------------------
# 0: SMA
def e0 = Average(src, len);

# 1: EMA
def e1 = ExpAverage(src, len);

# 2: WMA
def e2 = WMA(src, len);

# 3: HMA (Fixed: HullMovingAvg instead of HullMovingAverage)
def e3 = HullMovingAvg(price = src, length = len);

# 4: DEMA
def e4 = 2 * ExpAverage(src, len) - ExpAverage(ExpAverage(src, len), len);

# 5: RMA (Wilder's Smoothing)
def e5 = WildersAverage(src, len);

# 6: ALMA Approximation (Replaced GaussianFilter with double-smoothed WMA)
def alma_wma = WMA(src, Floor(len * 0.85));
def e6 = WMA(alma_wma, Floor(len * 0.5));

# 7: LSMA (Linear Regression Curve / Inertia)
def e7 = Inertia(src, len);

# -------------------------------------------------------------------
# Directional Predictions & Loss Metrics
# -------------------------------------------------------------------
# Current directions (+1 or -1)
def d0 = if e0 > e0[1] then 1 else -1;
def d1 = if e1 > e1[1] then 1 else -1;
def d2 = if e2 > e2[1] then 1 else -1;
def d3 = if e3 > e3[1] then 1 else -1;
def d4 = if e4 > e4[1] then 1 else -1;
def d5 = if e5 > e5[1] then 1 else -1;
def d6 = if e6 > e6[1] then 1 else -1;
def d7 = if e7 > e7[1] then 1 else -1;

# Previous directions (Predictions made on prior bar)
def pd0 = d0[1];
def pd1 = d1[1];
def pd2 = d2[1];
def pd3 = d3[1];
def pd4 = d4[1];
def pd5 = d5[1];
def pd6 = d6[1];
def pd7 = d7[1];

# Realized move & Loss scale
def realized = Sign(close - close[1]);
def atrN = ATR(len);
def move = if atrN > 0 then AbsValue(close - close[1]) / atrN else 0.0;

# Indicator readiness guard
def isReady = BarNumber() > len + 2;

# Calculation of directional errors (1.0 = Wrong prediction, 0.0 = Correct)
def wrong0 = if pd0 == realized then 0.0 else 1.0;
def wrong1 = if pd1 == realized then 0.0 else 1.0;
def wrong2 = if pd2 == realized then 0.0 else 1.0;
def wrong3 = if pd3 == realized then 0.0 else 1.0;
def wrong4 = if pd4 == realized then 0.0 else 1.0;
def wrong5 = if pd5 == realized then 0.0 else 1.0;
def wrong6 = if pd6 == realized then 0.0 else 1.0;
def wrong7 = if pd7 == realized then 0.0 else 1.0;

# Loss penalty per expert
def lossMult = if lossMode == lossMode."Magnitude-weighted" then Min(move, 3.0) else 1.0;
def loss0 = wrong0 * lossMult;
def loss1 = wrong1 * lossMult;
def loss2 = wrong2 * lossMult;
def loss3 = wrong3 * lossMult;
def loss4 = wrong4 * lossMult;
def loss5 = wrong5 * lossMult;
def loss6 = wrong6 * lossMult;
def loss7 = wrong7 * lossMult;

# -------------------------------------------------------------------
# Hedge Multiplicative-Weights Online Learning Model
# -------------------------------------------------------------------
# Recursive un-normalized updates: w_i = w_i[1] * exp(-eta * loss_i)
rec raw_w0 = if !isReady then 0.125 else raw_w0[1] * Exp(-eta * loss0);
rec raw_w1 = if !isReady then 0.125 else raw_w1[1] * Exp(-eta * loss1);
rec raw_w2 = if !isReady then 0.125 else raw_w2[1] * Exp(-eta * loss2);
rec raw_w3 = if !isReady then 0.125 else raw_w3[1] * Exp(-eta * loss3);
rec raw_w4 = if !isReady then 0.125 else raw_w4[1] * Exp(-eta * loss4);
rec raw_w5 = if !isReady then 0.125 else raw_w5[1] * Exp(-eta * loss5);
rec raw_w6 = if !isReady then 0.125 else raw_w6[1] * Exp(-eta * loss6);
rec raw_w7 = if !isReady then 0.125 else raw_w7[1] * Exp(-eta * loss7);

def sumW = raw_w0 + raw_w1 + raw_w2 + raw_w3 + raw_w4 + raw_w5 + raw_w6 + raw_w7;

# Normalization & Floor Adjustments
def norm0 = if sumW > 0 then raw_w0 / sumW else 0.125;
def norm1 = if sumW > 0 then raw_w1 / sumW else 0.125;
def norm2 = if sumW > 0 then raw_w2 / sumW else 0.125;
def norm3 = if sumW > 0 then raw_w3 / sumW else 0.125;
def norm4 = if sumW > 0 then raw_w4 / sumW else 0.125;
def norm5 = if sumW > 0 then raw_w5 / sumW else 0.125;
def norm6 = if sumW > 0 then raw_w6 / sumW else 0.125;
def norm7 = if sumW > 0 then raw_w7 / sumW else 0.125;

def w0 = floorW + (1.0 - floorW * 8) * norm0;
def w1 = floorW + (1.0 - floorW * 8) * norm1;
def w2 = floorW + (1.0 - floorW * 8) * norm2;
def w3 = floorW + (1.0 - floorW * 8) * norm3;
def w4 = floorW + (1.0 - floorW * 8) * norm4;
def w5 = floorW + (1.0 - floorW * 8) * norm5;
def w6 = floorW + (1.0 - floorW * 8) * norm6;
def w7 = floorW + (1.0 - floorW * 8) * norm7;

# -------------------------------------------------------------------
# Ensemble Blending and Consensus
# -------------------------------------------------------------------
def ensVal = (w0 * e0) + (w1 * e1) + (w2 * e2) + (w3 * e3) + (w4 * e4) + (w5 * e5) + (w6 * e6) + (w7 * e7);

# Smoothed line
plot Ensemble = ExpAverage(ensVal, smooth);

# Trend evaluation
def trend = if Ensemble > Ensemble[1] then 1 else if Ensemble < Ensemble[1] then -1 else trend[1];

Ensemble.AssignValueColor(if trend == 1 then Color.GREEN else if trend == -1 then Color.RED else Color.GRAY);
Ensemble.SetLineWeight(3);

# -------------------------------------------------------------------
# Visual Enhancements (Glow, Clouds, and Bar Coloring)
# -------------------------------------------------------------------
def glowRange = atrN * 0.06;
plot GlowUpper = Ensemble + glowRange;
plot GlowLower = Ensemble - glowRange;

GlowUpper.SetDefaultColor(Color.CURRENT);
GlowLower.SetDefaultColor(Color.CURRENT);
GlowUpper.HideTitle();
GlowLower.HideTitle();

# Cloud Fills
AddCloud(if showClouds then GlowUpper else Double.NaN, GlowLower, Color.DARK_GRAY, Color.DARK_GRAY);
AddCloud(if showClouds and close > Ensemble then close else Double.NaN, Ensemble, Color.DARK_GREEN, Color.DARK_GREEN);
AddCloud(if showClouds and close < Ensemble then Ensemble else Double.NaN, close, Color.DARK_RED, Color.DARK_RED);

# Bar Coloring
AssignPriceColor(if paintBar then (if trend == 1 then Color.GREEN else if trend == -1 then Color.RED else Color.CURRENT) else Color.CURRENT);

# -------------------------------------------------------------------
# Alerts
# -------------------------------------------------------------------
def bullFlip = trend == 1 and trend[1] == -1;
def bearFlip = trend == -1 and trend[1] == 1;

Alert(bullFlip, "Adaptive Trend Ensemble: Turned Bullish", Alert.BAR, Sound.Chimes);
Alert(bearFlip, "Adaptive Trend Ensemble: Turned Bearish", Alert.BAR, Sound.Bell);
 
Last edited by a moderator:

Join useThinkScript to post your question to a community of 21,000+ developers and traders.

Not the exact question you're looking for?

Start a new thread and receive assistance from our community.

87k+ Posts
1573 Online
Create Post

The Market Trading Game Changer

Join 2,500+ subscribers inside the useThinkScript VIP Membership Club
  • Exclusive indicators
  • Proven strategies & setups
  • Private Discord community
  • ‘Buy The Dip’ signal alerts
  • Exclusive members-only content
  • Add-ons and resources
  • 1 full year of unlimited support

Frequently Asked Questions

What is useThinkScript?

useThinkScript is the #1 community of stock market investors using indicators and other tools to power their trading strategies. Traders of all skill levels use our forums to learn about scripting and indicators, help each other, and discover new ways to gain an edge in the markets.

How do I get started?

We get it. Our forum can be intimidating, if not overwhelming. With thousands of topics, tens of thousands of posts, our community has created an incredibly deep knowledge base for stock traders. No one can ever exhaust every resource provided on our site.

If you are new, or just looking for guidance, here are some helpful links to get you started.

What are the benefits of VIP Membership?
VIP members get exclusive access to these proven and tested premium indicators: Buy the Dip, Advanced Market Moves 2.0, Take Profit, and Volatility Trading Range. In addition, VIP members get access to over 50 VIP-only custom indicators, add-ons, and strategies, private VIP-only forums, private Discord channel to discuss trades and strategies in real-time, customer support, trade alerts, and much more. Learn all about VIP membership here.
How can I access the premium indicators?
To access the premium indicators, which are plug and play ready, sign up for VIP membership here.
Back
Top