Strategy · Pine Script v5 · On the chart

EMA Trend Surfer

by nxtaiapp

EMA Trend Surfer is a dual-timeframe trend-following strategy designed for index futures such as MES and MNQ.

View on GitHub

GitHub stars2on the repository
Pine versionv5runs as is
Lines of code314
Settings19inputs
LicenceMIT
Last update11 months ago30 Nov 2025

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

314 lines of Pine Script v5 from MES_MNQ_DualTF_EMA_Strategy.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

MES_MNQ_DualTF_EMA_Strategy.pine
//@version=5
strategy("EMA Trend Surfer", 
         overlay=true, 
         initial_capital=50000,
         default_qty_type=strategy.fixed,
         default_qty_value=4,
         commission_type=strategy.commission.cash_per_contract,
         commission_value=0.34,
         slippage=2,
         process_orders_on_close=true,
         calc_on_every_tick=false)

// ═══════════════════════════════════════════════════════════════════
// INPUTS
// ═══════════════════════════════════════════════════════════════════

// Symbol Selection
symbolType = input.string("AUTO", "Symbol Type", options=["AUTO", "MES", "MNQ"], 
                          tooltip="AUTO will detect based on current symbol")

// EMA Settings
ema1_length = input.int(8, "Fast EMA Length", minval=1)
ema2_length = input.int(21, "Slow EMA Length", minval=1)

// Timeframes
htf_timeframe = input.timeframe("15", "Higher Timeframe (HTF)", tooltip="Used for trend direction")
ltf_timeframe = input.timeframe("1", "Lower Timeframe (LTF)", tooltip="Used for entries (should match chart)")

// Trading Session
useSession = input.bool(true, "Use NY Session Only")
sessionTime = input.session("0930-1600", "Trading Session (ET)", tooltip="Regular NY session 9:30 AM - 4:00 PM ET")

// Position Management
partial_exit_pct = input.float(50, "Partial Exit %", minval=1, maxval=100, step=1) / 100

// Daily loss limit
enable_daily_limit = input.bool(true, "Enable Daily Loss Limit")
max_daily_losses = input.int(3, "Max Daily Losses", minval=0)

// -- Filters to reduce over-trading when EMAs are flat/close
enable_ema_spread_filter = input.bool(true, "Enable EMA spread filter (ATR)")
min_ema_spread_atr = input.float(0.5, "Min EMA spread (ATR multiples)", step=0.1)
atr_length = input.int(14, "ATR Length")

enable_adx_filter = input.bool(false, "Enable ADX filter")
adx_length = input.int(14, "ADX Length")
min_adx = input.int(20, "Min ADX", minval=0)

// Display Options
showHTFEMAs = input.bool(true, "Show HTF EMAs on Chart")
showLabels = input.bool(true, "Show Entry/Exit Labels")
showStats = input.bool(true, "Show Statistics Table")

// ═══════════════════════════════════════════════════════════════════
// SYMBOL DETECTION AND TICK VALUES
// ═══════════════════════════════════════════════════════════════════

// Auto-detect symbol type
detectedSymbol = symbolType == "AUTO" ? 
                 (str.contains(syminfo.ticker, "MES") ? "MES" : 
                  str.contains(syminfo.ticker, "MNQ") ? "MNQ" : "MES") : 
                 symbolType

// Set tick value based on symbol
tickValue = detectedSymbol == "MES" ? 1.25 : 0.50  // MES = $1.25, MNQ = $0.50 per tick

// ═══════════════════════════════════════════════════════════════════
// SESSION FILTER
// ═══════════════════════════════════════════════════════════════════

// Check if we're in the trading session
inSession = not useSession or time(timeframe.period, sessionTime + ":1234567")

// -- Daily loss tracking
var int daily_losses = 0
var int prev_losstrades = strategy.losstrades
var int prev_day = dayofmonth(time)

// reset daily counter at midnight (new day)
curr_day = dayofmonth(time)
if curr_day != prev_day
    daily_losses := 0
    prev_day := curr_day

// detect newly closed losing trades and increment daily_losses
if strategy.losstrades > prev_losstrades
    lost_inc = strategy.losstrades - prev_losstrades
    daily_losses := daily_losses + lost_inc
    prev_losstrades := strategy.losstrades

// ═══════════════════════════════════════════════════════════════════
// CALCULATE EMAS
// ═══════════════════════════════════════════════════════════════════

// Current timeframe (LTF) EMAs
ltf_ema1 = ta.ema(close, ema1_length)
ltf_ema2 = ta.ema(close, ema2_length)

// Higher timeframe (HTF) EMAs
htf_ema1 = request.security(syminfo.tickerid, htf_timeframe, ta.ema(close, ema1_length))
htf_ema2 = request.security(syminfo.tickerid, htf_timeframe, ta.ema(close, ema2_length))
htf_close = request.security(syminfo.tickerid, htf_timeframe, close)

// ═══════════════════════════════════════════════════════════════════
// TREND DETECTION
// ═══════════════════════════════════════════════════════════════════

// HTF Trend (15-min)
htf_bullish = htf_ema1 > htf_ema2 and htf_close > htf_ema1
htf_bearish = htf_ema1 < htf_ema2 and htf_close < htf_ema1

// LTF Trend (1-min) - EMAs must be aligned with HTF
ltf_bullish_aligned = ltf_ema1 > ltf_ema2
ltf_bearish_aligned = ltf_ema1 < ltf_ema2

// ==== Volatility / trend filters ====
atr = ta.atr(atr_length)
ema_spread = math.abs(ltf_ema1 - ltf_ema2)
ema_spread_ok = ema_spread > atr * min_ema_spread_atr

adx_val = ta.adx(adx_length)
adx_ok = adx_val >= min_adx

// ═══════════════════════════════════════════════════════════════════
// ENTRY CONDITIONS
// ═══════════════════════════════════════════════════════════════════

// Long Entry: Price closes below 8 EMA then closes back above it
// Must have: HTF bullish, LTF aligned, in session
dipped_below_ema1 = close[1] < ltf_ema1[1]
closed_above_ema1 = close > ltf_ema1

long_entry = htf_bullish and ltf_bullish_aligned and dipped_below_ema1 and closed_above_ema1 and inSession and strategy.position_size == 0 and (not enable_daily_limit or daily_losses < max_daily_losses) and (not enable_ema_spread_filter or ema_spread_ok) and (not enable_adx_filter or adx_ok)

// Short Entry: Price closes above 8 EMA then closes back below it (inverse)
peaked_above_ema1 = close[1] > ltf_ema1[1]
closed_below_ema1 = close < ltf_ema1

short_entry = htf_bearish and ltf_bearish_aligned and peaked_above_ema1 and closed_below_ema1 and inSession and strategy.position_size == 0 and (not enable_daily_limit or daily_losses < max_daily_losses) and (not enable_ema_spread_filter or ema_spread_ok) and (not enable_adx_filter or adx_ok)

// ═══════════════════════════════════════════════════════════════════
// EXIT CONDITIONS
// ═══════════════════════════════════════════════════════════════════

// Long Exits
long_partial_exit = strategy.position_size > 0 and close < ltf_ema1
long_full_exit = strategy.position_size > 0 and close < ltf_ema2

// Short Exits
short_partial_exit = strategy.position_size < 0 and close > ltf_ema1
short_full_exit = strategy.position_size < 0 and close > ltf_ema2

// ═══════════════════════════════════════════════════════════════════
// POSITION TRACKING
// ═══════════════════════════════════════════════════════════════════

var float entry_price = 0.0
var bool partial_exit_done = false
var int contracts_remaining = 0

// ═══════════════════════════════════════════════════════════════════
// EXECUTE TRADES
// ═══════════════════════════════════════════════════════════════════

// LONG TRADES
if long_entry
    strategy.entry("Long", strategy.long, qty=4, comment="Long Entry")
    entry_price := close
    partial_exit_done := false
    contracts_remaining := 4
    if showLabels
        label.new(bar_index, low, "L", color=color.green, style=label.style_label_up, textcolor=color.white, size=size.small)

// Long Partial Exit (50%)
if long_partial_exit and not partial_exit_done and contracts_remaining > 2
    exit_qty = math.floor(4 * partial_exit_pct)
    strategy.close("Long", qty=exit_qty, comment="Partial Exit 50%")
    partial_exit_done := true
    contracts_remaining := contracts_remaining - exit_qty
    if showLabels
        label.new(bar_index, high, "P50%", color=color.orange, style=label.style_label_down, textcolor=color.white, size=size.small)

// Long Full Exit
if long_full_exit
    strategy.close("Long", comment="Full Exit")
    partial_exit_done := false
    contracts_remaining := 0
    if showLabels
        label.new(bar_index, high, "X", color=color.red, style=label.style_label_down, 
                 textcolor=color.white, size=size.small)

// SHORT TRADES
if short_entry
    strategy.entry("Short", strategy.short, qty=4, comment="Short Entry")
    entry_price := close
    partial_exit_done := false
    contracts_remaining := 4
    if showLabels
        label.new(bar_index, high, "S", color=color.red, style=label.style_label_down, 
                 textcolor=color.white, size=size.small)

// Short Partial Exit (50%)
if short_partial_exit and not partial_exit_done and contracts_remaining > 2
    exit_qty = math.floor(4 * partial_exit_pct)
    strategy.close("Short", qty=exit_qty, comment="Partial Exit 50%")
    partial_exit_done := true
    contracts_remaining := contracts_remaining - exit_qty
    if showLabels
        label.new(bar_index, low, "P50%", color=color.orange, style=label.style_label_up, 
                 textcolor=color.white, size=size.small)

// Short Full Exit
if short_full_exit
    strategy.close("Short", comment="Full Exit")
    partial_exit_done := false
    contracts_remaining := 0
    if showLabels
        label.new(bar_index, low, "X", color=color.green, style=label.style_label_up, 
                 textcolor=color.white, size=size.small)

// ═══════════════════════════════════════════════════════════════════
// PLOTTING
// ═══════════════════════════════════════════════════════════════════

// Plot LTF EMAs (always shown as they're on current timeframe)
plot(ltf_ema1, color=color.new(color.teal, 0), linewidth=2, title="LTF 8 EMA")
plot(ltf_ema2, color=color.new(color.red, 0), linewidth=2, title="LTF 21 EMA")

// Plot HTF EMAs (optional)
plot(showHTFEMAs ? htf_ema1 : na, color=color.new(color.teal, 50), linewidth=3, 
     title="HTF 8 EMA", style=plot.style_line)
plot(showHTFEMAs ? htf_ema2 : na, color=color.new(color.red, 50), linewidth=3, 
     title="HTF 21 EMA", style=plot.style_line)

// Background color for trend
bg_col = htf_bullish ? color.new(color.green, 95) : 
         htf_bearish ? color.new(color.red, 95) : na
bgcolor(bg_col, title="HTF Trend Background")

// Session background
bgcolor(inSession ? na : color.new(color.gray, 90), title="Outside Session")

// ═══════════════════════════════════════════════════════════════════
// STATISTICS TABLE
// ═══════════════════════════════════════════════════════════════════

if showStats and barstate.islastconfirmedhistory
    var table statsTable = table.new(position.top_right, 2, 9, 
                                     bgcolor=color.new(color.black, 80),
                                     frame_color=color.gray, frame_width=1,
                                     border_color=color.gray, border_width=1)
    
    // Header
    table.cell(statsTable, 0, 0, "Metric", bgcolor=color.new(color.blue, 50), 
              text_color=color.white, text_halign=text.align_center)
    table.cell(statsTable, 1, 0, "Value", bgcolor=color.new(color.blue, 50), 
              text_color=color.white, text_halign=text.align_center)
    
    // Symbol Info
    table.cell(statsTable, 0, 1, "Symbol", text_color=color.white)
    table.cell(statsTable, 1, 1, detectedSymbol + " ($" + str.tostring(tickValue) + "/tick)", 
              text_color=color.white)
    
    // Current Trend
    trend_text = htf_bullish ? "BULLISH ↑" : htf_bearish ? "BEARISH ↓" : "NEUTRAL —"
    trend_color = htf_bullish ? color.green : htf_bearish ? color.red : color.gray
    table.cell(statsTable, 0, 2, "HTF Trend", text_color=color.white)
    table.cell(statsTable, 1, 2, trend_text, text_color=trend_color)
    
    // Position
    pos_text = strategy.position_size > 0 ? "LONG " + str.tostring(math.abs(strategy.position_size)) :
               strategy.position_size < 0 ? "SHORT " + str.tostring(math.abs(strategy.position_size)) :
               "FLAT"
    pos_color = strategy.position_size > 0 ? color.green : (strategy.position_size < 0 ? color.red : color.gray)
    table.cell(statsTable, 0, 3, "Position", text_color=color.white)
    table.cell(statsTable, 1, 3, pos_text, text_color=pos_color)
    
    // Total Trades
    table.cell(statsTable, 0, 4, "Total Trades", text_color=color.white)
    table.cell(statsTable, 1, 4, str.tostring(strategy.closedtrades), text_color=color.white)
    
    // Win Rate
    wins = strategy.wintrades
    losses = strategy.losstrades
    winrate = strategy.closedtrades > 0 ? (wins / strategy.closedtrades) * 100 : 0
    table.cell(statsTable, 0, 5, "Win Rate", text_color=color.white)
    table.cell(statsTable, 1, 5, str.tostring(winrate, "##.#") + "%", 
              text_color=winrate >= 50 ? color.green : color.red)
    
    // Net Profit
    table.cell(statsTable, 0, 6, "Net Profit", text_color=color.white)
    table.cell(statsTable, 1, 6, "$" + str.tostring(strategy.netprofit, "##,###.##"), 
              text_color=strategy.netprofit >= 0 ? color.green : color.red)
    
    // Session Time
    session_active = inSession ? "✓ ACTIVE" : "✗ CLOSED"
    session_color = inSession ? color.green : color.gray
    table.cell(statsTable, 0, 7, "Session", text_color=color.white)
    table.cell(statsTable, 1, 7, session_active, text_color=session_color)
    
    // Losses Today
    table.cell(statsTable, 0, 8, "Losses Today", text_color=color.white)
    table.cell(statsTable, 1, 8, str.tostring(daily_losses), text_color=daily_losses >= max_daily_losses ? color.red : color.white)

// ═══════════════════════════════════════════════════════════════════
// ALERTS
// ═══════════════════════════════════════════════════════════════════

alertcondition(long_entry, title="Long Entry", message="Long Entry Signal: {{ticker}}")
alertcondition(short_entry, title="Short Entry", message="Short Entry Signal: {{ticker}}")
alertcondition(long_partial_exit, title="Long Partial Exit", message="Long 50% Exit: {{ticker}}")
alertcondition(short_partial_exit, title="Short Partial Exit", message="Short 50% Exit: {{ticker}}")
alertcondition(long_full_exit, title="Long Full Exit", message="Long Full Exit: {{ticker}}")
alertcondition(short_full_exit, title="Short Full Exit", message="Short Full Exit: {{ticker}}")

Overview

EMA Trend Surfer determines overall trend direction using fast and slow exponential moving averages on a higher timeframe while seeking entry signals on a lower timeframe. The strategy incorporates a New York session time filter, an ATR-based EMA spread filter, an optional ADX filter, and a daily loss limit to prevent overtrading. It plots higher-timeframe EMAs, trade labels, and a performance dashboard directly on the price chart. There is no predefined stop loss in default parameters.

Details

Published by

nxtaiapp on GitHub

Type

Strategy, Pine Script v5

Draws

On the price chart

Category

Trend, Moving averages, Sessions & time, Screeners & dashboards

Markets

Indices

Timeframes

M1-M5, M15-H1

Risk Notes

  • The strategy enters trades without a stop loss

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Symbol TypestringAUTOsymbolType
Fast EMA Lengthint8ema1_length
Slow EMA Lengthint21ema2_length
Higher Timeframe (HTF)timeframe15htf_timeframe
Lower Timeframe (LTF)timeframe1ltf_timeframe
Use NY Session OnlybooltrueuseSession
Trading Session (ET)session0930-1600sessionTime
Partial Exit %float50partial_exit_pct
Enable Daily Loss Limitbooltrueenable_daily_limit
Max Daily Lossesint3max_daily_losses
Enable EMA spread filter (ATR)booltrueenable_ema_spread_filter
Min EMA spread (ATR multiples)float0.5min_ema_spread_atr
Show all 19 settings
SettingTypeDefaultVariable
ATR Lengthint14atr_length
Enable ADX filterboolfalseenable_adx_filter
ADX Lengthint14adx_length
Min ADXint20min_adx
Show HTF EMAs on ChartbooltrueshowHTFEMAs
Show Entry/Exit LabelsbooltrueshowLabels
Show Statistics TablebooltrueshowStats
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

Lifecycle Example

by propuchon
1 stars

This strategy demonstrates a training, testing, and deployment lifecycle for Pine Script strategies. It calculates a simple moving average (SMA 20) and generates buy signals on crossover. The runMode input selects between Training, Testing, and Deployment. In

Strategy v6 TrendMoving averages
Updated 8 months ago · None stated GitHub repository of Lifecycle Example
257 stars

Donald - Test Logic Futures is an overlay strategy designed for intraday futures trading. It combines kernel regression functions with exponential moving averages to evaluate price retracements and entry timing. Trades are filtered by session execution windows

Strategy v5 TrendMoving averages Repaints
Updated 6 weeks ago · None stated GitHub repository of Donald - Test Logic Futures

AC- MY SCRIPT1

by hasnocool
106 stars

This strategy plots an Optimized Trend Tracker and a selectable moving-average support line on the price chart. It builds the average as SMA, EMA, WMA, TMA, VAR, WWMA, ZLEMA, or TSF, then offsets long and short stops by a percent of that average. Optional mark

Strategy v5 TrendMoving averages
Updated 2 years ago · GPL-3.0 GitHub repository of AC- MY SCRIPT1

Calculates a custom moving average (MaMA) by modifying the source price with price momentum, acceleration, and psychological win-rate probability before applying an EMA or SMA. Plots the adjusted moving average directly on the price chart alongside optional re

Strategy v5 Moving averagesTrend
Updated 4 years ago · None stated GitHub repository of Momentum Adjusted Moving Average by DGT

More strategys

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers