Why this strategy exists

My edge is structure + risk control, not prediction.

I trade /MES as the primary instrument and treat /ES as a scale-up option only when execution quality is consistent. The core idea:

  • 5-minute chart decides permission (bias/regime).
  • 10-second chart decides execution (entries/adds/stops).
  • Keep the 10-second view clean to reduce cognitive load and avoid indicator-chasing.

Chart architecture (non-negotiable)

1) 5-minute chart = Permission Engine

Purpose: decide whether I am allowed to be long/short, and whether I should be aggressive or conservative.

Indicators

  • HTF EMA200 (yellow) (approximated 15m EMA200 on a 5m chart)
  • VWAP (purple)
  • EMA20 (white)
  • Volume
  • Permission coloring + dots (script below)

Regimes (color meaning)

  • Green (BULL): confluence aligned + above VWAP + above HTF EMA200
  • Red (BEAR): confluence aligned + below VWAP + below HTF EMA200
  • Teal (TRANSITION LONG): confluence aligned + above VWAP but still below HTF EMA200
  • Orange (TRANSITION SHORT): confluence aligned + below VWAP but still above HTF EMA200
  • Gray (NEUTRAL): no permission; higher chop risk

2) 10-second chart = Execution Only

Purpose: time entries and adds with precision.

Indicators (keep it simple)

  • VWAP
  • EMA20 (optionally EMA9)
  • Volume

No confluence script here (by design). The 10-second is a microscope, not the thesis.


The strategy: how I trade it

Step 1 — Permission check (5m)

I only trade in the direction and intensity allowed by the 5m regime:

  • Green/Red: normal-to-aggressive execution allowed (including adding legs if structure remains intact).
  • Teal/Orange: conservative execution only (reclaim/fail context; take profits sooner; reduce leg stacking).
  • Gray: confirmation-only or no trade. I do not force trades.

Step 2 — Entry trigger (10s)

I enter using structure, not “perfect levels.”

Long (example)

  • Price above VWAP
  • EMA20 rising
  • Pullback holds above EMA20 (or shallow reclaim into EMA20)
  • Pullback volume is lighter than prior impulse volume
  • Entry: market on first strong green close after the pullback or on a clean higher-low confirmation
  • Initial stop: below the pullback low (structure invalidation)

Short is symmetrical (below VWAP, EMA20 falling, pullback fails, etc.).

Important lesson: On strong momentum days, price may not return to VWAP. If micro structure stays strong, waiting for a “perfect” VWAP fill can mean missing the move.


Exits (to protect runners)

The biggest upgrade: don’t let the 10-second flip become my thesis exit.

I use:

  • 10s for tightening and micro management
  • 5m for “structure invalidation” (thesis exit)

A simple exit hierarchy I can follow mechanically:

  1. Hard stop: structure invalidation (pullback low / key swing break)
  2. Profit protection: when trade is up materially, move older legs to BE+ / lock a small cushion
  3. Runner logic: only exit the runner on a 5m permission loss or a clear 5m structure break

Adding legs (wave effect)

I add only when:

  • The 5m regime still supports my direction, and
  • The 10s makes a clean pullback that holds EMA20/VWAP structure

Rule:

  • New legs must not increase overall risk.
  • Older legs are protected (BE+ / in-profit stop) before adding.

Risk rules (my “keep the weekend” rules)

  • No trading between classes if I can’t focus. My red days disproportionately come from fragmented attention.
  • Stop trading when green and stable, especially after a losing streak week. Consistency > hero trades.
  • /MES first. /ES only after multiple weeks of clean execution.

Flowchart (fast reminder)

flowchart TD A["fa:fa-compass 5m Permission Check"]:::c-start --> B{"Regime Color?"}:::c-decision B -->|"Green (Bull)"| C["fa:fa-bolt Execute Longs on 10s"]:::c-process B -->|"Red (Bear)"| D["fa:fa-bolt Execute Shorts on 10s"]:::c-process B -->|"Teal (Trans Long)"| E["fa:fa-shield Conservative Longs"]:::c-process B -->|"Orange (Trans Short)"| F["fa:fa-shield Conservative Shorts"]:::c-process B -->|"Gray (Neutral)"| G["fa:fa-ban No Trade or Confirmation Only"]:::c-end C --> H["fa:fa-crosshairs 10s Entry: EMA20 + VWAP + Volume + Structure"]:::c-process D --> H E --> H F --> H H --> I{"Structure Intact?"}:::c-decision I -->|"Yes"| J["fa:fa-layer-group Add Legs on Pullbacks (Risk Protected)"]:::c-process I -->|"No"| K["fa:fa-flag Exit: 5m Thesis Break or Invalidation"]:::c-end classDef c-start fill:#E0F7FA, stroke:#00BCD4, stroke-width:3px, color:#212121, font-weight:bold; classDef c-process fill:#E3F2FD, stroke:#2196F3, stroke-width:2px, color:#212121; classDef c-decision fill:#FFFDE7, stroke:#FFC107, stroke-width:2px, color:#212121; classDef c-end fill:#F3E5F5, stroke:#9C27B0, stroke-width:3px, color:#212121, font-weight:bold;
Figure System architecture: 5m permission → 10s execution. Trade structure, not noise.

Scripts

A) ThinkScript (3-Factor Confluence + EMA Bias + DOT alerts)

Use on the timeframe you want to evaluate confluence (commonly 5m). This is the original reference logic.

# 3-Factor Confluence + EMA Bias + DOT Alerts (ThinkScript)
# Use on desired timeframe (5m for permission alerts, 3m for entry alerts)

input emaFastLen = 9;
input emaSlowLen = 20;

input biasEmaLen = 200;          # on THIS chart timeframe
input useBiasFilter = yes;

input macdFast = 12;
input macdSlow = 26;
input macdSignal = 12;

input rsiLen = 21;
input rsiMid = 50;

input stochK = 14;
input stochSmoothK = 5;
input stochMid = 50;

# --- EMA(s)
def emaFast = ExpAverage(close, emaFastLen);
def emaSlow = ExpAverage(close, emaSlowLen);
def emaBias = ExpAverage(close, biasEmaLen);

# --- MACD
def macdLine = MovingAverage(AverageType.EXPONENTIAL, close, macdFast)
             - MovingAverage(AverageType.EXPONENTIAL, close, macdSlow);
def macdSig  = MovingAverage(AverageType.EXPONENTIAL, macdLine, macdSignal);

# --- RSI
def rsi = RSI(length = rsiLen);

# --- Stoch %K (smoothed)
def ll = Lowest(low, stochK);
def hh = Highest(high, stochK);
def rawK = if hh - ll == 0 then 50 else 100 * (close - ll) / (hh - ll);
def k = Average(rawK, stochSmoothK);

# --- Confluence
def bullCore = macdLine > macdSig and rsi > rsiMid and k > stochMid;
def bearCore = macdLine < macdSig and rsi < rsiMid and k < stochMid;

def biasLongOk  = !useBiasFilter or close > emaBias;
def biasShortOk = !useBiasFilter or close < emaBias;

def bull = bullCore and biasLongOk;
def bear = bearCore and biasShortOk;

# --- DOT = first bar of a new confluence state
def bullDot = bull and !bull[1];
def bearDot = bear and !bear[1];

# --- Plots (dots)
plot LongDot = if bullDot then low else Double.NaN;
LongDot.SetPaintingStrategy(PaintingStrategy.POINTS);
LongDot.SetLineWeight(4);

plot ShortDot = if bearDot then high else Double.NaN;
ShortDot.SetPaintingStrategy(PaintingStrategy.POINTS);
ShortDot.SetLineWeight(4);

# Optional: plot bias EMA
plot Bias = emaBias;
Bias.SetLineWeight(2);

# --- Alerts
Alert(bullDot, "LONG DOT: Confluence triggered", Alert.BAR, Sound.Ding);
Alert(bearDot, "SHORT DOT: Confluence triggered", Alert.BAR, Sound.Ding);

B) Tradovate Script (v5): Permission Engine + VWAP + HTF EMA200 + Transition Regimes

This is the latest “5-minute permission engine” script.

How I run it

  • On 5m chart:
    • chartTfMinutes = 5
    • emaFilterMode = HTF_15M_APPROX
    • useVWAPFilter = true
    • enableTransitionLong = true
    • enableTransitionShort = true
    • allowTransitionDots = false (bars only; cleaner)
  • On 10s chart:
    • I do not run this script. I keep the chart clean (VWAP + EMA20 + Volume).
// MomentumConfluence_3Factor_ES_TRADOVATE_v5.js
// 3-Factor Confluence + EMA bias approx + VWAP filter + Transition regimes
// States:
//   - BULL (Green): bullCore + above VWAP + above HTF EMA bias
//   - BEAR (Red):   bearCore + below VWAP + below HTF EMA bias
//   - TRANS LONG (Teal):  bullCore + above VWAP but still below HTF EMA bias
//   - TRANS SHORT (Orange): bearCore + below VWAP but still above HTF EMA bias
//
// Tradovate-safe syntax: avoids ?? and template literals; uses var where helpful.

var predef = require("./tools/predef");
var meta   = require("./tools/meta");
var EMA    = require("./tools/EMA");
var MMA    = require("./tools/MMA");

function smaPushAndGet(buf, v, period) {
  buf.push(v);
  if (buf.length > period) buf.shift();
  if (buf.length < period) return undefined;
  var s = 0;
  for (var i = 0; i < buf.length; i++) s += buf[i];
  return s / buf.length;
}

function isDefined(x) {
  return x !== undefined && x !== null;
}

class MomentumConfluence3F_ES {
  init() {
    // MACD
    this.emaFast = EMA(this.props.macdFast);
    this.emaSlow = EMA(this.props.macdSlow);
    this.emaSig  = EMA(this.props.macdSignal);

    // RSI (Wilder)
    this.avgGain = MMA(this.props.rsiLength);
    this.avgLoss = MMA(this.props.rsiLength);
    this.prevClose = undefined;

    // Stoch buffers
    this.highBuf = [];
    this.lowBuf  = [];
    this.rawKBuf = [];

    // EMA bias
    this.emaBias = EMA(1);
    this._biasPeriodInitialized = false;

    // VWAP accumulators
    this._vwapPV = 0;
    this._vwapV  = 0;
    this._vwap   = undefined;
    this._vwapSessionKey = undefined;

    // Edge detection
    this.prevBullStrict = false;
    this.prevBearStrict = false;
    this.prevTransLong  = false;
    this.prevTransShort = false;
  }

  _biasAllowed(biasDir) {
    var m = (this.props.biasMode || "BOTH");
    m = String(m).toUpperCase();
    if (m === "BOTH") return true;
    if (m === "LONG_ONLY") return biasDir === "LONG";
    if (m === "SHORT_ONLY") return biasDir === "SHORT";
    return true;
  }

  _effectiveBiasPeriod() {
    var base = this.props.biasEmaPeriod; // 200
    var mode = (this.props.emaFilterMode || "SAME_TF_200");
    mode = String(mode).toUpperCase();
    if (mode === "SAME_TF_200") return base;

    var chartTfMin = this.props.chartTfMinutes;
    if (!isDefined(chartTfMin) || chartTfMin <= 0) chartTfMin = 5;

    var htfMin = 15;
    if (mode === "HTF_60M_APPROX") htfMin = 60;

    var mult = htfMin / chartTfMin;
    var eff = Math.round(base * mult);
    if (eff < 1) eff = 1;
    return eff;
  }

  _getTimestampMs(d) {
    if (typeof d.timestamp === "function") return d.timestamp();
    if (typeof d.time === "function") return d.time();
    if (typeof d.date === "function") {
      var dt = d.date();
      if (dt && typeof dt.getTime === "function") return dt.getTime();
    }
    return undefined;
  }

  _vwapSessionKeyFromTime(ms) {
    var mode = (this.props.vwapResetMode || "CUSTOM");
    mode = String(mode).toUpperCase();

    var dt = new Date(ms);
    var y = dt.getFullYear();
    var mo = dt.getMonth() + 1;
    var day = dt.getDate();

    if (mode === "DAY") {
      return String(y) + "-" + String(mo) + "-" + String(day);
    }

    var resetH = (isDefined(this.props.vwapResetHour)) ? this.props.vwapResetHour : 17;
    var resetM = (isDefined(this.props.vwapResetMinute)) ? this.props.vwapResetMinute : 0;

    var mins = dt.getHours() * 60 + dt.getMinutes();
    var resetMins = resetH * 60 + resetM;

    var keyY = y, keyM = mo, keyD = day;
    if (mins < resetMins) {
      var dt2 = new Date(ms - 24 * 60 * 60 * 1000);
      keyY = dt2.getFullYear();
      keyM = dt2.getMonth() + 1;
      keyD = dt2.getDate();
    }

    return String(keyY) + "-" + String(keyM) + "-" + String(keyD) + "@" + String(resetH) + ":" + String(resetM);
  }

  _updateVWAP(d, close, high, low) {
    if (!this.props.useVWAPFilter && !this.props.plotVWAP) return;

    var ms = this._getTimestampMs(d);
    if (!isDefined(ms)) return;

    var key = this._vwapSessionKeyFromTime(ms);
    if (!isDefined(this._vwapSessionKey) || key !== this._vwapSessionKey) {
      this._vwapSessionKey = key;
      this._vwapPV = 0;
      this._vwapV  = 0;
      this._vwap   = undefined;
    }

    var vol = (typeof d.volume === "function") ? d.volume() : undefined;
    if (!isDefined(vol) || vol <= 0) return;

    var tp = (high + low + close) / 3;
    this._vwapPV += tp * vol;
    this._vwapV  += vol;

    if (this._vwapV > 0) this._vwap = this._vwapPV / this._vwapV;
  }

  map(d) {
    var close = d.close();
    var high  = d.high();
    var low   = d.low();

    // VWAP
    this._updateVWAP(d, close, high, low);
    var vwapVal = this._vwap;

    var vwapLongOk  = isDefined(vwapVal) ? (close > vwapVal) : false;
    var vwapShortOk = isDefined(vwapVal) ? (close < vwapVal) : false;

    // EMA Bias (approx HTF)
    if (!this._biasPeriodInitialized) {
      var p = this._effectiveBiasPeriod();
      this.emaBias = EMA(p);
      this._biasPeriodInitialized = true;
    }
    var emaBiasVal = this.emaBias(close);

    var emaLongOk  = isDefined(emaBiasVal) ? (close > emaBiasVal) : false;
    var emaShortOk = isDefined(emaBiasVal) ? (close < emaBiasVal) : false;

    // MACD
    var fast = this.emaFast(close);
    var slow = this.emaSlow(close);
    var macdLine = (isDefined(fast) && isDefined(slow)) ? (fast - slow) : undefined;
    var macdSig  = isDefined(macdLine) ? this.emaSig(macdLine) : undefined;

    // RSI
    var rsiVal;
    if (isDefined(this.prevClose)) {
      var chg = close - this.prevClose;
      var gain = Math.max(chg, 0);
      var loss = Math.max(-chg, 0);

      var ag = this.avgGain(gain);
      var al = this.avgLoss(loss);

      if (isDefined(ag) && isDefined(al)) {
        if (al === 0) rsiVal = 100;
        else {
          var rs = ag / al;
          rsiVal = 100 - (100 / (1 + rs));
        }
      }
    }
    this.prevClose = close;

    // Stoch (optional)
    var stochKsm;
    if (this.props.useStoch) {
      this.highBuf.push(high);
      this.lowBuf.push(low);
      if (this.highBuf.length > this.props.stochK) this.highBuf.shift();
      if (this.lowBuf.length  > this.props.stochK) this.lowBuf.shift();

      if (this.highBuf.length === this.props.stochK) {
        var hh = -Infinity, ll = Infinity;
        for (var j = 0; j < this.highBuf.length; j++) {
          if (this.highBuf[j] > hh) hh = this.highBuf[j];
          if (this.lowBuf[j]  < ll) ll = this.lowBuf[j];
        }
        var denom = (hh - ll);
        var rawK = (denom === 0) ? 50 : ((close - ll) / denom) * 100;
        stochKsm = smaPushAndGet(this.rawKBuf, rawK, this.props.stochSmoothK);
      }
    }

    // Confluence components
    var macdBull = (isDefined(macdLine) && isDefined(macdSig)) ? (macdLine > macdSig) : false;
    var macdBear = (isDefined(macdLine) && isDefined(macdSig)) ? (macdLine < macdSig) : false;

    var rsiBull  = isDefined(rsiVal) ? (rsiVal > this.props.rsiMid) : false;
    var rsiBear  = isDefined(rsiVal) ? (rsiVal < this.props.rsiMid) : false;

    var stochBull = this.props.useStoch ? (isDefined(stochKsm) ? (stochKsm > this.props.stochMid) : false) : true;
    var stochBear = this.props.useStoch ? (isDefined(stochKsm) ? (stochKsm < this.props.stochMid) : false) : true;

    var bullCore =
      (!this.props.useMACD || macdBull) &&
      (!this.props.useRSI  || rsiBull)  &&
      stochBull;

    var bearCore =
      (!this.props.useMACD || macdBear) &&
      (!this.props.useRSI  || rsiBear)  &&
      stochBear;

    // Filters
    var useEmaFilter  = !!this.props.useEmaFilter;
    var useVWAPFilter = !!this.props.useVWAPFilter;

    var emaLongGate  = (!useEmaFilter  || (emaLongOk  && this._biasAllowed("LONG")));
    var emaShortGate = (!useEmaFilter  || (emaShortOk && this._biasAllowed("SHORT")));

    var vwapLongGate  = (!useVWAPFilter || vwapLongOk);
    var vwapShortGate = (!useVWAPFilter || vwapShortOk);

    // Strict regimes
    var bullStrict = bullCore && emaLongGate && vwapLongGate;
    var bearStrict = bearCore && emaShortGate && vwapShortGate;

    // Transition regimes
    var transitionLong = false;
    var transitionShort = false;

    if (this.props.enableTransitionLong) {
      var emaBlocksLong = (useEmaFilter && isDefined(emaBiasVal)) ? (!emaLongOk) : false;
      transitionLong = bullCore && vwapLongGate && emaBlocksLong;
    }

    if (this.props.enableTransitionShort) {
      var emaBlocksShort = (useEmaFilter && isDefined(emaBiasVal)) ? (!emaShortOk) : false;
      transitionShort = bearCore && vwapShortGate && emaBlocksShort;
    }

    // Candle coloring priority
    var candleStyle;
    if (bullStrict) candleStyle = { color: this.props.bullColor };
    else if (bearStrict) candleStyle = { color: this.props.bearColor };
    else if (transitionLong) candleStyle = { color: this.props.transitionLongColor };
    else if (transitionShort) candleStyle = { color: this.props.transitionShortColor };
    else candleStyle = { color: this.props.neutralColor };

    // Dots: state change markers (optional)
    var allowTransDots = !!this.props.allowTransitionDots;

    var longEntry  = bullStrict && !this.prevBullStrict;
    var shortEntry = bearStrict && !this.prevBearStrict;

    var transLongEntry  = allowTransDots ? (transitionLong  && !this.prevTransLong)  : false;
    var transShortEntry = allowTransDots ? (transitionShort && !this.prevTransShort) : false;

    this.prevBullStrict = bullStrict;
    this.prevBearStrict = bearStrict;
    this.prevTransLong  = transitionLong;
    this.prevTransShort = transitionShort;

    var tick = this.contractInfo.tickSize || 0.25;
    var offset = (this.props.signalOffsetTicks || 2) * tick;

    return {
      long:  longEntry  ? (low  - offset) : undefined,
      short: shortEntry ? (high + offset) : undefined,
      transLong:  transLongEntry  ? (low  - (offset * 0.5)) : undefined,
      transShort: transShortEntry ? (high + (offset * 0.5)) : undefined,

      emaBias: emaBiasVal,
      vwap:    (this.props.plotVWAP ? vwapVal : undefined),

      candlestick: candleStyle,

      style: {
        long:       { color: this.props.longSignalColor },
        short:      { color: this.props.shortSignalColor },
        transLong:  { color: this.props.transitionLongDotColor },
        transShort: { color: this.props.transitionShortDotColor },
        emaBias:    { color: this.props.emaColor },
        vwap:       { color: this.props.vwapColor }
      }
    };
  }
}

module.exports = {
  name: "MomentumConfluence3F_ES_v5",
  description: "Confluence permission engine + HTF EMA bias approx + VWAP + Transition Long/Short regimes",
  calculator: MomentumConfluence3F_ES,

  inputType: meta.InputType.BARS,
  areaChoice: meta.AreaChoice.OVERLAY,

  params: {
    chartTfMinutes: predef.paramSpecs.number(5, 0.01, 0),

    useMACD: predef.paramSpecs.bool(true),
    useRSI:  predef.paramSpecs.bool(true),
    useStoch: predef.paramSpecs.bool(false),

    macdFast:   predef.paramSpecs.period(12),
    macdSlow:   predef.paramSpecs.period(26),
    macdSignal: predef.paramSpecs.period(9),

    rsiLength: predef.paramSpecs.period(21),
    rsiMid:    predef.paramSpecs.number(50, 1, 0),

    stochK:       predef.paramSpecs.period(14),
    stochSmoothK: predef.paramSpecs.period(5),
    stochMid:     predef.paramSpecs.number(50, 1, 0),

    useEmaFilter:  predef.paramSpecs.bool(true),
    biasEmaPeriod: predef.paramSpecs.period(200),

    emaFilterMode: predef.paramSpecs.enum({
      SAME_TF_200: "Same TF 200 EMA",
      HTF_15M_APPROX: "HTF 15m 200 (approx)",
      HTF_60M_APPROX: "HTF 60m 200 (approx)"
    }, "HTF_15M_APPROX"),

    biasMode: predef.paramSpecs.enum({
      BOTH: "Both",
      LONG_ONLY: "Long only",
      SHORT_ONLY: "Short only"
    }, "BOTH"),

    useVWAPFilter: predef.paramSpecs.bool(true),
    plotVWAP:      predef.paramSpecs.bool(true),

    vwapResetMode: predef.paramSpecs.enum({
      DAY: "Reset at midnight",
      CUSTOM: "Reset at custom time"
    }, "CUSTOM"),
    vwapResetHour:   predef.paramSpecs.number(17, 1, 0),
    vwapResetMinute: predef.paramSpecs.number(0, 1, 0),

    enableTransitionLong:  predef.paramSpecs.bool(true),
    enableTransitionShort: predef.paramSpecs.bool(true),
    allowTransitionDots:   predef.paramSpecs.bool(false),

    signalOffsetTicks: predef.paramSpecs.number(2, 1, 0),

    bullColor:    predef.paramSpecs.color("#0B3D0B"),
    bearColor:    predef.paramSpecs.color("#5A0B0B"),
    neutralColor: predef.paramSpecs.color("#808080"),

    transitionLongColor:  predef.paramSpecs.color("#007C91"),
    transitionShortColor: predef.paramSpecs.color("#B36A00"),

    longSignalColor:   predef.paramSpecs.color("#00FF00"),
    shortSignalColor:  predef.paramSpecs.color("#FF3333"),
    transitionLongDotColor:  predef.paramSpecs.color("#00D5FF"),
    transitionShortDotColor: predef.paramSpecs.color("#FFB347"),

    emaColor:  predef.paramSpecs.color("#FFD700"),
    vwapColor: predef.paramSpecs.color("#B000FF")
  },

  plots: {
    long:       { title: "Long Entry (Strict)" },
    short:      { title: "Short Entry (Strict)" },
    transLong:  { title: "Transition Long Dot" },
    transShort: { title: "Transition Short Dot" },
    emaBias:    { title: "EMA Bias" },
    vwap:       { title: "VWAP" }
  },

  plotter: [
    predef.plotters.dots("long"),
    predef.plotters.dots("short"),
    predef.plotters.dots("transLong"),
    predef.plotters.dots("transShort"),
    predef.plotters.singleline("emaBias"),
    predef.plotters.singleline("vwap")
  ],

  tags: ["ES", "MES", "Momentum", "Confluence", "EMA Bias", "VWAP", "Transition"]
};

Notes for future refinement

  • If I want more signals (not recommended), I can enable transition dots:
    • allowTransitionDots = true
  • If the day is extremely choppy, I can simplify confluence:
    • leave MACD+RSI ON, keep Stoch OFF

Personal rule that protects my equity

If I cannot fully focus, I don’t trade.
My best trading comes from calm, uninterrupted attention.