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:
- Hard stop: structure invalidation (pullback low / key swing break)
- Profit protection: when trade is up materially, move older legs to BE+ / lock a small cushion
- 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)
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 = 5emaFilterMode = HTF_15M_APPROXuseVWAPFilter = trueenableTransitionLong = trueenableTransitionShort = trueallowTransitionDots = 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.