help improve this strategy,
//@Version=6
indicator("GGbest Backtest Lite v1.2 (Fixed Trade vars) — EMA+FVG + In-chart Backtester", overlay=true, max_boxes_count=250, max_lines_count=250)
// ========== USER INPUTS ==========
ema_fast_len = input.int(50, "EMA Fast")
ema_slow_len = input.int(200, "EMA Slow")
use_fvg = input.bool(true, "Use FVG")
use_atr_filter = input.bool(false, "Use ATR acceptance")
atr_len = input.int(14, "ATR length")
atr_min = input.float(0.0, "Min ATR (0=off)")
atr_max = input.float(0.0, "Max ATR (0=off)")
rsi_len = input.int(14, "RSI length")
// Session (your trading hours) — default 21:00-23:00 EAT
use_session = input.bool(true, "Restrict to session")
sess = input.session("2100-2300", "Session (24h)")
need_confs = input.int(3, "Min confluence (1..4)", minval=1, maxval=4)
// Backtest settings
sl_pips = input.int(7, "Stop Loss (pips)", minval=1, maxval=100) // you said 5-10 pips — change as needed
lookback_for_tp = input.int(50, "Lookback bars to find nearest swing (TP)", minval=5, maxval=500)
max_bars_holding = input.int(200, "Max bars to hold trade", minval=1, maxval=2000)
// Money / sizing
start_balance = input.float(100.0, "Start Balance (USD)")
pip_size = input.float(0.0001, "Pip size (price units). e.g. EURUSD=0.0001, XAUUSD=0.01, CL=0.01")
pip_value_usd = input.float(0.1, "USD per pip (per trade) — change to reflect lot size")
// Display
show_stats = input.bool(true, "Show backtest stats on chart")
show_trade_markers = input.bool(true, "Show entry/exit markers")
// ========== INDICATORS & BASIC SCANNER ==========
ema_fast = ta.ema(close, ema_fast_len)
ema_slow = ta.ema(close, ema_slow_len)
atr_val = ta.atr(atr_len)
rsi = ta.rsi(close, rsi_len)
in_session = not use_session or (time(timeframe.period, sess) != 0)
atr_ok = not use_atr_filter or ((atr_min == 0 or atr_val >= atr_min) and (atr_max == 0 or atr_val <= atr_max))
// FVG detection (3-bar rule)
var box[] fvg_bull_boxes = array.new_box()
var box[] fvg_bear_boxes = array.new_box()
max_boxes = 200
bool bull_fvg = false
bool bear_fvg = false
if use_fvg and bar_index >= 2
if (high[2] < low[1]) and (low > high[2])
bull_fvg := true
array.push(fvg_bull_boxes, box.new(bar_index-2, high[2], bar_index, low[1], bgcolor=color.new(color.green,85)))
else
bull_fvg := false
if (low[2] > high[1]) and (high < low[2])
bear_fvg := true
array.push(fvg_bear_boxes, box.new(bar_index-2, high[1], bar_index, low[2], bgcolor=color.new(color.red,85)))
else
bear_fvg := false
while array.size(fvg_bull_boxes) > max_boxes
box.delete(array.shift(fvg_bull_boxes))
while array.size(fvg_bear_boxes) > max_boxes
box.delete(array.shift(fvg_bear_boxes))
// Trend / RSI confluence
bull_trend = ema_fast > ema_slow
bear_trend = ema_fast < ema_slow
rsi_bull_ok = rsi > 50
rsi_bear_ok = rsi < 50
long_confs = (bull_trend ? 1 : 0) + ((bull_fvg) ? 1 : 0) + (atr_ok ? 1 : 0) + (rsi_bull_ok ? 1 : 0)
short_confs = (bear_trend ? 1 : 0) + ((bear_fvg) ? 1 : 0) + (atr_ok ? 1 : 0) + (rsi_bear_ok ? 1 : 0)
long_signal = in_session and (long_confs >= need_confs)
short_signal = in_session and (short_confs >= need_confs)
// ======= Backtester internal state (flat vars replacing custom type) =======
var float active_entry_price = na
var int active_entry_bar = 0
var string active_side = ""
var float active_sl_price = na
var float active_tp_price = na
var int active_exit_bar = 0
var float active_exit_price = na
var string active_exit_reason = ""
var bool in_trade = false
// stats
var int trades_count = 0
var int wins = 0
var int losses = 0
var float cum_pips = 0.0
var float balance = start_balance
// helper: convert pips to price units
f_pips_to_price(pips) =>
pips * pip_size
// helper: price -> pips difference (for buys positive if price increased)
f_price_to_pips(delta_price) =>
delta_price / pip_size
// helper: find nearest swing high above entry price within lookback_for_tp (TP for long)
find_tp_for_long(entry_price) =>
float tp = na
float hh = ta.highest(high, lookback_for_tp)
if hh > entry_price
for i = 1 to lookback_for_tp
if high[i] > entry_price
tp := high[i]
break
tp
find_tp_for_short(entry_price) =>
float tp = na
float ll = ta.lowest(low, lookback_for_tp)
if ll < entry_price
for i = 1 to lookback_for_tp
if low[i] < entry_price
tp := low[i]
break
tp
// On each new bar: handle entry logic or manage active trade
if not in_trade
// enter long
if long_signal
float entry = close
float sl_price = entry - f_pips_to_price(sl_pips)
float tp_price = find_tp_for_long(entry)
// fallback TP: if no swing high found, set TP at entry + 1.5*ATR
if na(tp_price)
tp_price := entry + 1.5 * atr_val
// open trade (assign to flat vars)
active_entry_price := entry
active_entry_bar := bar_index
active_side := "long"
active_sl_price := sl_price
active_tp_price := tp_price
active_exit_bar := na
active_exit_price := na
active_exit_reason := ""
in_trade := true
trades_count += 1
if show_trade_markers
label.new(bar_index, low, "ENTER LONG\nSL " + str.tostring(sl_pips) + "pips\nTP->" + str.tostring(tp_price), style=label.style_label_up, color=color.new(color.green, 10), textcolor=color.white)
// enter short
if short_signal
float entry = close
float sl_price = entry + f_pips_to_price(sl_pips)
float tp_price = find_tp_for_short(entry)
if na(tp_price)
tp_price := entry - 1.5 * atr_val
active_entry_price := entry
active_entry_bar := bar_index
active_side := "short"
active_sl_price := sl_price
active_tp_price := tp_price
active_exit_bar := na
active_exit_price := na
active_exit_reason := ""
in_trade := true
trades_count += 1
if show_trade_markers
label.new(bar_index, high, "ENTER SHORT\nSL " + str.tostring(sl_pips) + "pips\nTP->" + str.tostring(tp_price), style=label.style_label_down, color=color.new(color.red, 10), textcolor=color.white)
else
// trade is active — check for hits within current bar using high/low
string side = active_side
float curHigh = high
float curLow = low
bool hitTP = false
bool hitSL = false
// For long: TP hit if high >= tp_price; SL hit if low <= sl_price
if side == "long"
if curHigh >= active_tp_price
hitTP := true
else if curLow <= active_sl_price
hitSL := true
else
// short
if curLow <= active_tp_price
hitTP := true
else if curHigh >= active_sl_price
hitSL := true
// Also close if max holding exceeded
bool timed_out = (bar_index - active_entry_bar) >= max_bars_holding ? true : false
// Opposite signal closure (optional): if opposite signal appears we can close at bar open (conservative)
bool opp_close = false
if side == "long" and short_signal
opp_close := true
if side == "short" and long_signal
opp_close := true
if hitTP or hitSL or timed_out or opp_close
float exit_price = na
string exit_reason = ""
if hitTP
exit_price := active_tp_price
exit_reason := "TP"
else if hitSL
exit_price := active_sl_price
exit_reason := "SL"
else if opp_close
exit_price := open // close at next bar open
exit_reason := "opp_signal"
else
exit_price := close
exit_reason := "timeout"
// compute pips gained
float pips_gained = side == "long" ? f_price_to_pips(exit_price - active_entry_price) : f_price_to_pips(active_entry_price - exit_price)
cum_pips += pips_gained
// USD profit
float usd_profit = pips_gained * pip_value_usd
balance += usd_profit
// win/loss accounting
if pips_gained > 0
wins += 1
else
losses += 1
// mark exit on chart
if show_trade_markers
label.new(bar_index, exit_price, "EXIT " + exit_reason + "\nPips:" + str.tostring(pips_gained, format.mintick) + "\nBal:" + str.tostring(math.round(balance,2)), style=label.style_label_left, color=color.new(color.gray, 10), textcolor=color.white)
// reset
in_trade := false
active_entry_price := na
active_entry_bar := 0
active_side := ""
active_sl_price := na
active_tp_price := na
active_exit_bar := na
active_exit_price := na
active_exit_reason := ""
// ========== Stats label on chart ==========
var label stats_label = na
if barstate.islast
if not na(stats_label)
label.delete(stats_label)
win_rate = trades_count > 0 ? math.round(100 * wins / trades_count) : 0
stats_txt = "GGbest Backtest Lite\nTF: " + timeframe.period + "\nTrades: " + str.tostring(trades_count) + " Wins: " + str.tostring(wins) + " Losses: " + str.tostring(losses) + "\nWin%: " + str.tostring(win_rate) + "% CumPips: " + str.tostring(math.round(cum_pips,1)) + "\nBalance: $" + str.tostring(math.round(balance,2))
if show_stats
stats_label := label.new(bar_index, high, stats_txt, style=label.style_label_right, color=color.new(color.blue,85), textcolor=color.white, yloc=yloc.abovebar)
// ========== Plots for trend and FVG ==========
plot(ema_fast, color=color.blue)
plot(ema_slow, color=color.orange)
// use bull_fvg and bear_fvg booleans for plotshape; ensure they exist
plotshape(bull_fvg, title="BullFVG", location=location.belowbar, style=shape.circle, size=size.tiny, color=color.green)
plotshape(bear_fvg, title="BearFVG", location=location.abovebar, style=shape.circle, size=size.tiny, color=color.red)
// static alerts to notify of signals (constant message)
alertcondition(long_signal, title="GGbest Long Signal (alert)", message="GGbest: Long signal - check chart")
alertcondition(short_signal, title="GGbest Short Signal (alert)", message="GGbest: Short signal - check chart")
help improve this strategy,
//@Version=6
indicator("GGbest Backtest Lite v1.2 (Fixed Trade vars) — EMA+FVG + In-chart Backtester", overlay=true, max_boxes_count=250, max_lines_count=250)
// ========== USER INPUTS ==========
ema_fast_len = input.int(50, "EMA Fast")
ema_slow_len = input.int(200, "EMA Slow")
use_fvg = input.bool(true, "Use FVG")
use_atr_filter = input.bool(false, "Use ATR acceptance")
atr_len = input.int(14, "ATR length")
atr_min = input.float(0.0, "Min ATR (0=off)")
atr_max = input.float(0.0, "Max ATR (0=off)")
rsi_len = input.int(14, "RSI length")
// Session (your trading hours) — default 21:00-23:00 EAT
use_session = input.bool(true, "Restrict to session")
sess = input.session("2100-2300", "Session (24h)")
need_confs = input.int(3, "Min confluence (1..4)", minval=1, maxval=4)
// Backtest settings
sl_pips = input.int(7, "Stop Loss (pips)", minval=1, maxval=100) // you said 5-10 pips — change as needed
lookback_for_tp = input.int(50, "Lookback bars to find nearest swing (TP)", minval=5, maxval=500)
max_bars_holding = input.int(200, "Max bars to hold trade", minval=1, maxval=2000)
// Money / sizing
start_balance = input.float(100.0, "Start Balance (USD)")
pip_size = input.float(0.0001, "Pip size (price units). e.g. EURUSD=0.0001, XAUUSD=0.01, CL=0.01")
pip_value_usd = input.float(0.1, "USD per pip (per trade) — change to reflect lot size")
// Display
show_stats = input.bool(true, "Show backtest stats on chart")
show_trade_markers = input.bool(true, "Show entry/exit markers")
// ========== INDICATORS & BASIC SCANNER ==========
ema_fast = ta.ema(close, ema_fast_len)
ema_slow = ta.ema(close, ema_slow_len)
atr_val = ta.atr(atr_len)
rsi = ta.rsi(close, rsi_len)
in_session = not use_session or (time(timeframe.period, sess) != 0)
atr_ok = not use_atr_filter or ((atr_min == 0 or atr_val >= atr_min) and (atr_max == 0 or atr_val <= atr_max))
// FVG detection (3-bar rule)
var box[] fvg_bull_boxes = array.new_box()
var box[] fvg_bear_boxes = array.new_box()
max_boxes = 200
bool bull_fvg = false
bool bear_fvg = false
if use_fvg and bar_index >= 2
if (high[2] < low[1]) and (low > high[2])
bull_fvg := true
array.push(fvg_bull_boxes, box.new(bar_index-2, high[2], bar_index, low[1], bgcolor=color.new(color.green,85)))
else
bull_fvg := false
if (low[2] > high[1]) and (high < low[2])
bear_fvg := true
array.push(fvg_bear_boxes, box.new(bar_index-2, high[1], bar_index, low[2], bgcolor=color.new(color.red,85)))
else
bear_fvg := false
while array.size(fvg_bull_boxes) > max_boxes
box.delete(array.shift(fvg_bull_boxes))
while array.size(fvg_bear_boxes) > max_boxes
box.delete(array.shift(fvg_bear_boxes))
// Trend / RSI confluence
bull_trend = ema_fast > ema_slow
bear_trend = ema_fast < ema_slow
rsi_bull_ok = rsi > 50
rsi_bear_ok = rsi < 50
long_confs = (bull_trend ? 1 : 0) + ((bull_fvg) ? 1 : 0) + (atr_ok ? 1 : 0) + (rsi_bull_ok ? 1 : 0)
short_confs = (bear_trend ? 1 : 0) + ((bear_fvg) ? 1 : 0) + (atr_ok ? 1 : 0) + (rsi_bear_ok ? 1 : 0)
long_signal = in_session and (long_confs >= need_confs)
short_signal = in_session and (short_confs >= need_confs)
// ======= Backtester internal state (flat vars replacing custom type) =======
var float active_entry_price = na
var int active_entry_bar = 0
var string active_side = ""
var float active_sl_price = na
var float active_tp_price = na
var int active_exit_bar = 0
var float active_exit_price = na
var string active_exit_reason = ""
var bool in_trade = false
// stats
var int trades_count = 0
var int wins = 0
var int losses = 0
var float cum_pips = 0.0
var float balance = start_balance
// helper: convert pips to price units
f_pips_to_price(pips) =>
pips * pip_size
// helper: price -> pips difference (for buys positive if price increased)
f_price_to_pips(delta_price) =>
delta_price / pip_size
// helper: find nearest swing high above entry price within lookback_for_tp (TP for long)
find_tp_for_long(entry_price) =>
float tp = na
float hh = ta.highest(high, lookback_for_tp)
if hh > entry_price
for i = 1 to lookback_for_tp
if high[i] > entry_price
tp := high[i]
break
tp
find_tp_for_short(entry_price) =>
float tp = na
float ll = ta.lowest(low, lookback_for_tp)
if ll < entry_price
for i = 1 to lookback_for_tp
if low[i] < entry_price
tp := low[i]
break
tp
// On each new bar: handle entry logic or manage active trade
if not in_trade
// enter long
if long_signal
float entry = close
float sl_price = entry - f_pips_to_price(sl_pips)
float tp_price = find_tp_for_long(entry)
// fallback TP: if no swing high found, set TP at entry + 1.5*ATR
if na(tp_price)
tp_price := entry + 1.5 * atr_val
// open trade (assign to flat vars)
active_entry_price := entry
active_entry_bar := bar_index
active_side := "long"
active_sl_price := sl_price
active_tp_price := tp_price
active_exit_bar := na
active_exit_price := na
active_exit_reason := ""
in_trade := true
trades_count += 1
if show_trade_markers
label.new(bar_index, low, "ENTER LONG\nSL " + str.tostring(sl_pips) + "pips\nTP->" + str.tostring(tp_price), style=label.style_label_up, color=color.new(color.green, 10), textcolor=color.white)
// enter short
if short_signal
float entry = close
float sl_price = entry + f_pips_to_price(sl_pips)
float tp_price = find_tp_for_short(entry)
if na(tp_price)
tp_price := entry - 1.5 * atr_val
active_entry_price := entry
active_entry_bar := bar_index
active_side := "short"
active_sl_price := sl_price
active_tp_price := tp_price
active_exit_bar := na
active_exit_price := na
active_exit_reason := ""
in_trade := true
trades_count += 1
if show_trade_markers
label.new(bar_index, high, "ENTER SHORT\nSL " + str.tostring(sl_pips) + "pips\nTP->" + str.tostring(tp_price), style=label.style_label_down, color=color.new(color.red, 10), textcolor=color.white)
else
// trade is active — check for hits within current bar using high/low
string side = active_side
float curHigh = high
float curLow = low
bool hitTP = false
bool hitSL = false
// For long: TP hit if high >= tp_price; SL hit if low <= sl_price
if side == "long"
if curHigh >= active_tp_price
hitTP := true
else if curLow <= active_sl_price
hitSL := true
else
// short
if curLow <= active_tp_price
hitTP := true
else if curHigh >= active_sl_price
hitSL := true
// ========== Stats label on chart ==========
var label stats_label = na
if barstate.islast
if not na(stats_label)
label.delete(stats_label)
win_rate = trades_count > 0 ? math.round(100 * wins / trades_count) : 0
stats_txt = "GGbest Backtest Lite\nTF: " + timeframe.period + "\nTrades: " + str.tostring(trades_count) + " Wins: " + str.tostring(wins) + " Losses: " + str.tostring(losses) + "\nWin%: " + str.tostring(win_rate) + "% CumPips: " + str.tostring(math.round(cum_pips,1)) + "\nBalance: $" + str.tostring(math.round(balance,2))
if show_stats
stats_label := label.new(bar_index, high, stats_txt, style=label.style_label_right, color=color.new(color.blue,85), textcolor=color.white, yloc=yloc.abovebar)
// ========== Plots for trend and FVG ==========
plot(ema_fast, color=color.blue)
plot(ema_slow, color=color.orange)
// use bull_fvg and bear_fvg booleans for plotshape; ensure they exist
plotshape(bull_fvg, title="BullFVG", location=location.belowbar, style=shape.circle, size=size.tiny, color=color.green)
plotshape(bear_fvg, title="BearFVG", location=location.abovebar, style=shape.circle, size=size.tiny, color=color.red)
// static alerts to notify of signals (constant message)
alertcondition(long_signal, title="GGbest Long Signal (alert)", message="GGbest: Long signal - check chart")
alertcondition(short_signal, title="GGbest Short Signal (alert)", message="GGbest: Short signal - check chart")