StealthTrail SuperTrend ML Pro [WillyAlgoTrader]

StealthTrail SuperTrend ML Pro [WillyAlgoTrader]

Loading chart…

StealthTrail SuperTrend ML Pro [WillyAlgoTrader] is a sophisticated overlay indicator that extends the classic SuperTrend with three layers of adaptive intelligence: an instrument profiling engine that classifies the market into Trending, Ranging, or Volatile regimes and auto-tunes all SuperTrend parameters accordingly; a 13-feature machine learning scoring system that assigns a 0–100 confidence score to every candidate signal; and a self-learning mechanism that tracks signal outcomes over time and dynamically adjusts the confidence gate. The result is a SuperTrend that configures itself, scores its own signals, and improves from its history.

In this article we’ll see exactly how this indicator works, what data it produces, and, most importantly, how to load it, configure it, and run it in NodeJS using PineTS, QuantForge’s Pine Script runtime. We’ll cover both batch execution with pineTS.run() and live-streaming execution with pineTS.stream().


Running StealthTrail SuperTrend ML Pro Indicator on NodeJS

Installing PineTS

npm install pinets

Loading the Pine Script Indicator

Save the Pine Script source to a .pine file (e.g., stealthtrail.pine) or pass it directly as a string. PineTS detects the //@version=6 marker and routes it through the Pine Script transpiler automatically.

Method 1: Batch execution with pineTS.run()

run() processes the full historical dataset in one go and returns the complete context. This is the right approach for backtesting, signal analysis, or building datasets.

import { PineTS, Indicator, Provider } from 'pinets';
import { readFileSync } from 'fs';

const script = readFileSync('./stealthtrail.pine', 'utf8');

// Configure inputs by their title strings (matching the first argument of each input.* call)
const indicator = new Indicator(script, {
  // Main Settings
  'Auto-Tune Parameters': true,

  // ML Signal Filter: enable scoring and raise the gate
  'Enable ML Signal Filter': true,
  'Confidence Gate': 45.0,
  'Self-Learning Gate': true,
  'Evaluation Horizon (bars)': 15,

  // Multi-Timeframe: strict alignment required
  'Multi-Timeframe Confluence': true,
  'MTF Strictness': 'Strict',

  // TP/SL
  'Enable TP/SL System': true,
  'Stop Loss Mode': 'Band',
  'Trailing Stop': true,
  'Trail Mode': 'ATR β†’ Band',
  'Take Profit Levels': 3,
  'TP1 ATR Multiplier': 2.0,
  'TP2 ATR Multiplier': 4.0,
  'TP3 ATR Multiplier': 6.0,
});
const pineTS = new PineTS(
  Provider.Binance,
  'BTCUSDT',
  '60',     // 1-hour bars
  undefined,
  new Date('2024-01-01').getTime(),
  new Date('2024-12-31').getTime(),
);

// Enable 'all' mode so alerts fire on every historical bar.
// Default 'realtime' only fires on the last bar, which is correct for live use;
// but use 'all' when you want a full alert log from a backtest run.
pineTS.setAlertMode('all');

const ctx = await pineTS.run(indicator);

// ── Access plots ──────────────────────────────────────────
const band   = ctx.plots['Band'];      // SuperTrend band values
const longs  = ctx.plots['Long'];      // Confirmed buy signals
const shorts = ctx.plots['Short'];     // Confirmed sell signals

// Extract confirmed signals with their bar time and price
const signals = [];

for (const point of longs.data) {
  if (point.value !== null && !isNaN(point.value)) {
    signals.push({ time: new Date(point.time), direction: 'LONG', price: point.value });
  }
}
for (const point of shorts.data) {
  if (point.value !== null && !isNaN(point.value)) {
    signals.push({ time: new Date(point.time), direction: 'SHORT', price: point.value });
  }
}

signals.sort((a, b) => a.time - b.time);

console.log(`Total signals: ${signals.length}`);
for (const s of signals.slice(-10)) {
  console.log(`[${s.time.toISOString()}] ${s.direction} @ ${s.price}`);
}

// ── Access band values ─────────────────────────────────────
const lastBar = band.data[band.data.length - 1];
console.log(`Current band: ${lastBar?.value} (${new Date(lastBar?.time).toISOString()})`);

// ── Replay alerts from the run ─────────────────────────────
// Each alert object has: { message, time, bar_index, type, freq }
for (const alertObj of ctx.alerts) {
  console.log(`[${new Date(alertObj.time).toISOString().slice(0,10)}] ${alertObj.message}`);
}

Method 2: Live streaming with pineTS.stream()

stream() delivers results incrementally, page by page. When no eDate is provided and the provider supports it, PineTS automatically transitions into live mode, fetching new candles as they close and re-running the indicator. This makes stream() the natural choice for real-time signal delivery, automated order routing, and live monitoring dashboards.

import { PineTS, Indicator, Provider } from 'pinets';
import { readFileSync } from 'fs';

const script = readFileSync('./stealthtrail.pine', 'utf8');

const indicator = new Indicator(script, {
  'Auto-Tune Parameters': true,
  'Enable ML Signal Filter': true,
  'Confidence Gate': 40.0,
  'Self-Learning Gate': true,
  'Multi-Timeframe Confluence': true,
  'MTF Strictness': 'Moderate',
  'Enable TP/SL System': true,
  'Stop Loss Mode': 'Band',
  'Trailing Stop': true,
  'Trail Mode': 'Band',
  'Webhook JSON Format': true,   // emit JSON payloads in alerts
});

// Omitting eDate β†’ PineTS enters live mode after historical data is consumed
const pineTS = new PineTS(
  Provider.Binance,
  'BTCUSDT',
  '60',
  undefined,
  new Date('2024-01-01').getTime(),
  // no eDate
);

const feed = pineTS.stream(indicator, {
  pageSize: 500,   // bars per incremental update
  live: true,      // continue polling for new candles
  interval: 5000,  // poll every 5 seconds in live mode
});

// ── Incremental data ──────────────────────────────────────
feed.on('data', (ctx) => {
  const longs  = ctx.plots['Long'];
  const shorts = ctx.plots['Short'];
  const band   = ctx.plots['Band'];

  // Only process the most recent bars in this page
  const recentLong  = longs?.data.findLast(p => p.value !== null && !isNaN(p.value));
  const recentShort = shorts?.data.findLast(p => p.value !== null && !isNaN(p.value));

  if (recentLong)  console.log(`[LONG]  ${new Date(recentLong.time).toISOString()}  band=${band?.data.at(-1)?.value}`);
  if (recentShort) console.log(`[SHORT] ${new Date(recentShort.time).toISOString()} band=${band?.data.at(-1)?.value}`);
});

// ── Real-time alerts ──────────────────────────────────────
// The callback receives a full alert object: { message, time, bar_index, type, freq }
feed.on('alert', (alertObj) => {
  const message = alertObj.message;

  // With 'Webhook JSON Format': true, message is a JSON string
  try {
    const payload = JSON.parse(message);
    console.log(`[ALERT] ${payload.action.toUpperCase()} ${payload.ticker} @ ${payload.price}`);
    console.log(`        ML=${payload.ml}  ADX=${payload.adx}  HTF=${payload.htf}  Regime=${payload.regime}`);
    console.log(`        SL=${payload.sl}  TP1=${payload.tp1}`);

    // Route to your order execution layer
    if (payload.action === 'long' || payload.action === 'short') {
      placeOrder(payload);
    }
  } catch {
    console.log('[ALERT]', message);  // plain text fallback
  }
});

feed.on('warning', (w) => console.warn('[Warning]', w));
feed.on('error',   (e) => console.error('[Error]', e));

// Stop after 1 hour (or call feed.stop() from any external trigger)
setTimeout(() => {
  feed.stop();
  console.log('Stream stopped.');
}, 60 * 60 * 1000);

function placeOrder(payload) {
  // Implement your order routing logic here
  console.log(`Order: ${payload.action} ${payload.ticker}, SL=${payload.sl}, TP=${payload.tp1}`);
}


Indicator Properties

Inputs

The indicator exposes a rich set of configurable inputs grouped into logical sections.

Main Settings

InputDefaultDescription
Auto-Tune ParameterstrueEnables regime-driven self-configuration of all SuperTrend parameters
ATR Length (manual)13ATR period when auto-tune is off
Base Multiplier (manual)2.5Band multiplier when auto-tune is off

Adaptive Engine

InputDefaultDescription
Profiling Lookback100Bars used for regime classification
Regime Sensitivity1.0Scaling factor for regime score magnitudes
Show Instrument ProfiletrueShow regime section in dashboard

Multi-Timeframe

InputDefaultDescription
Multi-Timeframe ConfluencetrueChecks that the higher-timeframe trend agrees
HTF Selection"Auto"Auto derives HTF from current TF, Manual lets you specify it
MTF Strictness"Moderate"Loose / Moderate / Strict: controls how strongly MTF misalignment penalizes or blocks signals

ML Signal Filter

InputDefaultDescription
Enable ML Signal FilterfalseActivates the 13-feature confidence scoring gate
Confidence Gate21.0Minimum ML score for a signal to pass
Self-Learning GatetrueAuto-adjusts the gate based on tracked win rate
Evaluation Horizon (bars)15Bars after entry to assess signal outcome
W1–W13 + BiasvariousIndividual feature weights (fully configurable)

Classic Filters

InputDefaultDescription
Flip Cushion Γ—ATR0.15Required penetration past the band to confirm a flip
Signal Cooldown3Minimum bars between two consecutive signals
Momentum Filter (RSI)trueRequire RSI alignment with the trade direction
Volume FilterfalseRequire volume above its moving average

TP / SL System

InputDefaultDescription
Enable TP/SL SystemtrueActivates automatic trade management levels
Stop Loss Mode"Band"ATR / Band / Fixed %
Trailing StoptrueRatchets the SL in the profit direction
Trail Mode"Band"ATR / Band / ATR β†’ Band (hybrid)
Take Profit Levels3Number of TP targets (1–3)
TP1/2/3 ATR Multiplier2 / 4 / 6ATR distance from entry for each TP level

Outputs

Pine Script indicators have two kind of outputs, plots and alerts. Plots hold the data used by charting tools to represent the indicator visually, while alerts can be used to trigger custom actions.

In PineTS context, the alerts will emit events that you can listen to and run custom logic based on it.

Plots

Plot NameTypeDescription
BandLineMain SuperTrend band (green when bullish, red when bearish)
SwitchCirclesAppears at band flip points to highlight the direction change
LongShape (labelup)Green “Long” label below bar: confirmed buy signal
ShortShape (labeldown)Red “Short” label above bar: confirmed sell signal
FilteredCirclesGray dots: flips blocked by classic filters
MLRejBuy / MLRejSellTrianglesYellow: ML-rejected signals (passed classic filters, failed ML gate)
BullDiv / BearDivCirclesRSI divergence dots (optional)
Close / BandFillHiddenInternal references used for the gradient fill between band and price

Alerts

The indicator fires alerts on bar close (alert.freq_once_per_bar_close) in six conditions:

AlertCondition
Long signalconfirmedBuy: classic filters pass, ML gate pass (if enabled)
Short signalconfirmedSell: classic filters pass, ML gate pass (if enabled)
TP1 hitPrice reaches the first take-profit level
TP2 hitPrice reaches the second take-profit level
TP3 hitPrice reaches the third take-profit level and closes the position
SL hitPrice hits the trailing stop and closes the position

All alerts support both plain-text and JSON webhook formats. The JSON payload includes: actiontickerpricetfbandml score, adxhtf trend, sltp1, and regime.


Understanding the Indicator for Algo Trading

The Three-Layer Intelligence Stack

What makes StealthTrail Pro genuinely interesting from a quantitative perspective is not any single technique but the way three adaptive layers compose into a system that attempts to solve the core problem of deploying a trend-following strategy across changing market regimes.

Layer 1: Regime Classification and Auto-Tuning

A standard SuperTrend has fixed ATR length and multiplier. These tend to work best in one type of regime and less effectively in another. The profiling engine continuously measures four microstructure metrics:

  • Efficiency Ratio (ER): net directional displacement over total path. ER β†’ 1 means pure trend; ER β†’ 0 means chop.
  • Autocorrelation: serial dependence between consecutive returns. High positive autocorrelation signals trend persistence.
  • Volatility Clustering: ratio of short-term ATR (20) to long-term ATR (lookback). Values above 1 indicate a volatility spike.
  • Normalized Volatility: ATR / price Γ— 100, which allows regime comparison across instruments with very different price scales.

From these four inputs, the engine computes three regime scores (TRENDING / RANGING / VOLATILE) and auto-blends six SuperTrend parameters using the regime weights as mixing coefficients. This means the ATR length, multiplier, flip cushion, cooldown, and RSI threshold shift continuously as market character changes, without manual intervention.

For algorithmic use, this is significant: you no longer need separate parameter sets per asset or timeframe. One indicator configuration adapts to its environment.

Layer 2: 13-Feature ML Confidence Scoring

Not all SuperTrend flips are equal. The ML layer scores every flip against 13 market features, each normalized to 0–100 and passed through a weighted linear model with sigmoid activation. The features cover:

  • Momentum (RSI alignment) and Volume Surge: classic confirmation factors
  • Trend Efficiency (ER) and ADX Strength: trend quality metrics
  • Band Distance and Price Structure (HH/HL pattern): entry quality metrics
  • MACD: momentum divergence between signal and histogram
  • Volatility Shock (negative weight): penalizes entries during vol spikes
  • MTF Confluence: whether the higher timeframe trend agrees
  • RSI Divergence: aligned divergence boosts the score, counter-divergence reduces it
  • Volume Profile Zone: proximity to the highest-volume bar in the last 50 bars
  • Session Quality: institutional session scoring for intraday timeframes
  • Regime Confidence: how certain the regime classification is

The sigmoid function compresses the weighted sum so that the final score clusters around 50 and only moves decisively toward 0 or 100 when multiple factors align. This avoids the common pitfall of a single dominant feature driving all decisions.

All 13 weights are user-configurable. For algorithmic use, this means you can tune the feature importance for your specific instrument class, for instance zeroing out the volume weight for forex, or raising the MTF weight when trading with trend-following bias.

Layer 3: Self-Learning Gate

The confidence gate that separates accepted from rejected signals is not fixed. The system maintains a sliding window of up to 5 pending signals (stored with their entry price, direction, and ATR at entry), evaluates their outcomes after a configurable horizon, and adjusts the gate up or down based on the rolling win rate. A decay factor (0.98 per evaluation batch) prevents historical results from dominating recent performance.

The practical effect: on instruments and timeframes where the indicator naturally generates high-quality signals, the gate relaxes and captures more of them. Where the indicator struggles, the gate tightens automatically, reducing noise rather than flooding you with false positives.

Using It for Algorithmic Signal Generation

Signal extraction is straightforward via PineTS. The Long and Short plot series contain NaN everywhere except at confirmed signal bars. Iterating these arrays gives you a timestamped list of all historical entry points, which you can use to build a signal dataset, run backtest statistics, or seed a position management system.

Alert-driven execution is the preferred real-time path. The JSON webhook format from pineTS.stream() gives you a structured payload on every confirmed signal, TP hit, or SL hit, including the ML score, ADX, HTF direction, computed SL level, and TP1 level. You can wire this directly to an order execution layer with minimal transformation.

Multi-asset scanning is natural: instantiate one PineTS per symbol, run them in parallel, and collect signals into a central signal queue. The regime badge in the payload (regime field) lets you filter or weight signals by market character, for example acting only on TRENDING regime signals in a trend-following strategy.

Experimental quant research applications:

  • Regime classification as a standalone feature: the TRENDING / RANGING / VOLATILE classification and its confidence score can be extracted even without acting on the SuperTrend signals themselves, and used as regime filters for other strategies.
  • ML feature backtesting: because the 13 feature weights are configurable and the scoring architecture is transparent (linear model + sigmoid), you can systematically vary weights and measure impact on signal quality, essentially running a grid search over the weight space.
  • Gate sensitivity analysis: vary Confidence Gate between 20 and 80 and observe the signal frequency vs win-rate tradeoff on historical data. The self-learning mechanism provides one automated optimum, but understanding the full sensitivity curve is valuable for regime-specific deployment.
  • Cross-timeframe consistency checks: run the same indicator on 1H and 4H for the same asset and compare the direction of confirmed signals. Disagreement is a useful regime instability signal.
  • TP/SL analytics: the indicator computes and tracks TP1/TP2/TP3 and SL levels per position. Extracting these from a completed run() gives you a full trade log including theoretical exits, useful for building realistic P&L models without a separate backtesting engine.

Performance Notes

The indicator pre-computes ATR values at 9 fixed periods and uses linear interpolation (lerp) to produce smooth ATR estimates at arbitrary lengths. This avoids calling ta.atr() with a variable-length argument (Pine Script requires series function arguments to be fixed at the call site) and keeps the per-bar computation cost flat regardless of the auto-tuned ATR length. The same pattern applies to RSI. This design is well-suited to PineTS’s incremental execution model where each function call maintains O(1) per-bar state.

The self-learning mechanism uses a fixed pool of 5 pending signal slots. When all 5 slots are occupied and a new signal fires, it is enqueued but not tracked. In a high-frequency signal environment (very loose gate) this can mean under-sampling the win-rate feedback. For quantitative use, increasing the gate moderately (40–50) is advisable to ensure the learning loop has clean signal samples.

Important Notes

  • No repainting: All signals require barstate.isconfirmed. PineTS inherits this behavior: run() processes only closed bars, so signals are stable.
  • Warmup period: The indicator requires max(profileLookback, 55) bars before emitting any signal. PineTS respects this automatically. When running on short historical windows, ensure you have sufficient data preceding your target analysis period.
  • The ML layer is a weighted linear model, not a neural network. There is no training phase, backpropagation, or gradient descent. The “self-learning” adjusts only the confidence threshold, not the feature weights. Treat the ML score as a composite momentum/quality index, not as a probabilistic prediction.
  • Volume features degrade on instruments without volume data (some forex pairs, synthetic indices). PineTS will not error, but the volume-related features (F2, F12) will fall back to neutral values (50).

Disclaimer

QuantForge publishes technical material about indicators: what they do, how they are built, and how you can experiment with them in code and on charts. All of this is offered for education and illustration only. It is not financial, investment, or trading advice, and it should not be read as a recommendation to buy, sell, or hold any asset. Past or simulated performance does not guarantee future results. You are responsible for your own decisions; seek independent professional advice where appropriate.