This strategy generates trading signals on the 5-minute price chart using Williams Fractals for trend reversals, combined with EMA fast and slow trend filters and optional RSI confirmation.
//@version=5
strategy("5min Williams Fractals scalping (3commas)", shorttitle="5min WF scalp", format=format.price, precision=0, overlay=true)
// Define "n" as the number of periods and keep a minimum value of 2 for error handling.
import ZenAndTheArtOfTrading/ZenLibrary/2 as l_zen
var g_strategy = "Strategy"
i_chkLong = input.bool(true, title = 'Long', inline = '1', group = g_strategy)
i_chkShort = input.bool(true, title = 'Short', inline = '1', group = g_strategy)
i_startTime = input.time(title = 'Start Date', defval = timestamp('01 Jan 2020 00:00 +0100'), group = g_strategy, tooltip = 'Date & time to begin trading from')
i_endTime = input.time(title = 'End Date', defval = timestamp('1 Jan 2099 00:00 +0100'), group = g_strategy, tooltip = 'Date & time to stop trading')
i_longProfitPerc = input.float(title ='Long Target Profit R:R', minval = 1.1, step = 0.1, defval = 1.5, inline ='2', group = g_strategy, tooltip = "Times the Stop Loss which is always the current Ema slow value")
i_shortProfitPerc = input.float(title = 'Short Target Profit R:R', minval = 1.1, step = 0.1, defval = 1.5, inline = '3', group = g_strategy, tooltip = "Times the Stop Loss which is always the current Ema slow value")
i_tradeDontClose = input.int(title = "Stop loss not sooner than", defval = 0, minval = 0, group = g_strategy, tooltip = "If an open trade hits the Stop loss sooner than the expected amount of bars have passed, then SL will not trigger (eg. for thight stop losses)")
var g_wf = "Williams Fractals"
i_wf = input.int(title="Periods", defval=2, minval=2, group = g_wf)
i_wfBarsBack = input.bool(defval = true, title = "Evaluate WF bar", group = g_wf, tooltip = "By default it evaluates the bar for which the WF signal triggers so when turned off it evaluates the current bar")
var g_ema = "EMA trend"
i_emaFast = input.int(title = "Ema fast lenght", defval = 20, step = 1, group = g_ema)
i_emaSlow = input.int(title = "Ema slow lenght", defval = 50, step = 1, group = g_ema)
i_emaGapUse = input.bool(title = "Use Ema Gap", defval = false, group = g_ema, tooltip = "Gap between Emas must be greater than % of the whole security price,\neg. 0.1 = 0.1% of the security price which is the minimum gap tolerated for an entry")
i_emaGap = input.float(title = "Ema Gap %", defval = 0.2, step = 0.05, group = g_ema)
var g_rsi = "RSI filter"
i_rsiChk = input.bool(false, "Use RSI Filter", group = g_rsi)
i_rsiRes = input.timeframe("", "TimeFrame", group = g_rsi)
i_rsiLen = input.int(14, "Lenght", group = g_rsi)
i_rsiMinLong = input.float(0.0, "Min for Long", minval = 0.0, maxval = 100.0, step = 0.5, group = g_rsi, inline = "4")
i_rsiMaxLong = input.float(70.0, "Max for Long", minval = 0.0, maxval = 100.0, step = 0.5, group = g_rsi, inline = "4")
i_rsiMinShort = input.float(30.0, "Min for Short", minval = 0.0, maxval = 100.0, step = 0.5, group = g_rsi, inline = "5")
i_rsiMaxShort = input.float(100.0, "Max for Short", minval = 0.0, maxval = 100.0, step = 0.5, group = g_rsi, inline = "5")
i_rsiDirectChk = input.bool(false, "RSI direction confirmation", group = g_rsi, tooltip = "Long only if its supported by rising RSI and Short only if its supported by falling RSI\nWorks only if Use RSI is ON")
i_rsiDirectLen = input.int(1, "Bars back for direction", minval = 1, group = g_rsi, tooltip = "Lookback period for RSI values")
var g_additional = "Pullback filter"
i_usePullbackChk = input.bool(false, title = 'Use Pullback', group = g_additional, tooltip = "Checks if the amount of oposite candles (bearish when long) in the lookback period (counts from second history candle)is smaller than the NOT bigger count and vice versa")
i_pullbackCount = input.int(title="Lookback period", defval=2, minval = 1, group = g_additional)
i_pullbackNotBigger = input.int(title="NOT bigger than", defval=1, minval = 0, group = g_additional)
var g_tester = 'Back tester table'
i_drawTester = input.bool(title='Show back tester', defval=true, group=g_tester, tooltip='Turn on/off inbuilt backtester display', inline = "backtester1")
i_testerPosition = input.string(title='', defval="Top right", options = ["Top right", "Top left", "Bottom right", "Bottom left"], group=g_tester, tooltip='Position of the backtester table', inline = "backtester1")
i_rr = input.float(title='R:R', defval=1.0, group=g_tester, tooltip='Risk:Reward profile')
startBalance = input.float(title='Starting Balance', defval=100.0, group=g_tester, tooltip='Your starting balance for the custom inbuilt tester system')
riskPerTrade = input.float(title='Risk Per Trade', defval=1.0, group=g_tester, tooltip='Your desired % risk per trade (as a whole number)')
var i_3Commas = '3Commas bot settings'
i_useComments = input.bool(false, "Show 3Commas bot comments", group = i_3Commas)
i_botIDLong = input(title="Long Bot ID", defval="", group = i_3Commas)
i_botIDShort = input(title="Short Bot ID", defval="", group = i_3Commas)
i_tokenID = input(title="Email Token", defval="", group = i_3Commas)
// Get strategy direction and set time
strategy.risk.allow_entry_in(i_chkLong and i_chkShort ? strategy.direction.all : i_chkLong ? strategy.direction.long : strategy.direction.short)
c_dateFilter = time >= i_startTime and time <= i_endTime // See if this bar's time happened within date filter
// Get Ema trend and conditions
c_emaFast = ta.ema(close[barstate.isrealtime ? 1 : 0], i_emaFast)
c_emaSlow = ta.ema(close[barstate.isrealtime ? 1 : 0], i_emaSlow)
c_emaCrossBull = c_emaFast >= c_emaSlow
c_emaCrossBear = c_emaFast < c_emaSlow
c_emaLong = i_wfBarsBack ?
close[i_wf] < c_emaFast[i_wf] and close[i_wf] > c_emaSlow[i_wf] :
close[barstate.isrealtime ? 1 : 0] < c_emaFast and close[barstate.isrealtime ? 1 : 0] > c_emaSlow
c_emaShort = i_wfBarsBack ?
(close[i_wf] > c_emaFast[i_wf] and close[i_wf] < c_emaSlow[i_wf]) :
close[barstate.isrealtime ? 1 : 0] > c_emaFast and close[barstate.isrealtime ? 1 : 0] < c_emaSlow
c_emaGap = math.abs(c_emaFast - c_emaSlow) >= ((close[barstate.isrealtime ? 1 : 0] / 100) * i_emaGap)
c_tradesOpen = ta.barssince(strategy.opentrades != 0)
// Get RSI values
f_rpSecurity(_symbol, _res, _src) =>
request.security(_symbol, _res, _src[barstate.isrealtime ? 1 : 0])
c_rsiSrc = f_rpSecurity("", i_rsiRes, close)
c_rsi = ta.rsi(c_rsiSrc, i_rsiLen)
c_rsiRising = ta.rising(c_rsi, i_rsiDirectLen)
c_rsiFalling = ta.falling(c_rsi, i_rsiDirectLen)
// Get pullback filter
c_pullbackCountGreen = l_zen.getPullbackBarCount(i_pullbackCount, 1)
c_pullbackCountRed = l_zen.getPullbackBarCount(i_pullbackCount, -1)
// Get Williams Fractals
bool c_upflagDownFrontier = true
bool c_upflagUpFrontier0 = true
bool c_upflagUpFrontier1 = true
bool c_upflagUpFrontier2 = true
bool c_upflagUpFrontier3 = true
bool c_upflagUpFrontier4 = true
for i = 1 to i_wf
c_upflagDownFrontier := c_upflagDownFrontier and (high[i_wf - i] < high[i_wf])
c_upflagUpFrontier0 := c_upflagUpFrontier0 and (high[i_wf + i] < high[i_wf])
c_upflagUpFrontier1 := c_upflagUpFrontier1 and (high[i_wf + 1] <= high[i_wf] and high[i_wf+i + 1] < high[i_wf])
c_upflagUpFrontier2 := c_upflagUpFrontier2 and (high[i_wf + 1] <= high[i_wf] and high[i_wf + 2] <= high[i_wf] and high[i_wf + i + 2] < high[i_wf])
c_upflagUpFrontier3 := c_upflagUpFrontier3 and (high[i_wf + 1] <= high[i_wf] and high[i_wf + 2] <= high[i_wf] and high[i_wf + 3] <= high[i_wf] and high[i_wf + i + 3] < high[i_wf])
c_upflagUpFrontier4 := c_upflagUpFrontier4 and (high[i_wf + 1] <= high[i_wf] and high[i_wf + 2] <= high[i_wf] and high[i_wf + 3] <= high[i_wf] and high[i_wf + 4] <= high[i_wf] and high[i_wf+i + 4] < high[i_wf])
c_flagUpFrontier = c_upflagUpFrontier0 or c_upflagUpFrontier1 or c_upflagUpFrontier2 or c_upflagUpFrontier3 or c_upflagUpFrontier4
c_upFractal = (c_upflagDownFrontier and c_flagUpFrontier)
bool c_downflagDownFrontier = true
bool c_downflagUpFrontier0 = true
bool c_downflagUpFrontier1 = true
bool c_downflagUpFrontier2 = true
bool c_downflagUpFrontier3 = true
bool c_downflagUpFrontier4 = true
for i = 1 to i_wf
c_downflagDownFrontier := c_downflagDownFrontier and (low[i_wf - i] > low[i_wf])
c_downflagUpFrontier0 := c_downflagUpFrontier0 and (low[i_wf + i] > low[i_wf])
c_downflagUpFrontier1 := c_downflagUpFrontier1 and (low[i_wf + 1] >= low[i_wf] and low[i_wf+i + 1] > low[i_wf])
c_downflagUpFrontier2 := c_downflagUpFrontier2 and (low[i_wf + 1] >= low[i_wf] and low[i_wf + 2] >= low[i_wf] and low[i_wf + i + 2] > low[i_wf])
c_downflagUpFrontier3 := c_downflagUpFrontier3 and (low[i_wf + 1] >= low[i_wf] and low[i_wf + 2] >= low[i_wf] and low[i_wf + 3] >= low[i_wf] and low[i_wf+i + 3] > low[i_wf])
c_downflagUpFrontier4 := c_downflagUpFrontier4 and (low[i_wf + 1] >= low[i_wf] and low[i_wf + 2] >= low[i_wf] and low[i_wf + 3] >= low[i_wf] and low[i_wf + 4] >= low[i_wf] and low[i_wf+i + 4] > low[i_wf])
c_flagDownFrontier = c_downflagUpFrontier0 or c_downflagUpFrontier1 or c_downflagUpFrontier2 or c_downflagUpFrontier3 or c_downflagUpFrontier4
c_downFractal = (c_downflagDownFrontier and c_flagDownFrontier)
c_sinceLastTrade = ta.barssince(strategy.closedtrades > strategy.closedtrades[1])
c_sinceLastLossTrade = ta.barssince(strategy.losstrades > strategy.losstrades[1])
var float c_longExitPrice = 0.0
var float c_shortExitPrice = 0.0
var float c_longStopPrice = 0.0
var float c_shortStopPrice = 0.0
c_longCondition = c_dateFilter and (c_downFractal) and
c_emaLong and c_emaCrossBull and (i_emaGapUse ? c_emaGap : true) and
(i_rsiChk ? (c_rsi >= i_rsiMinLong and c_rsi <= i_rsiMaxLong and (i_rsiDirectChk ? c_rsiRising : true)) : true) and
(i_usePullbackChk ? c_pullbackCountGreen : true)
c_shortCondition = c_dateFilter and (c_upFractal) and
c_emaShort and c_emaCrossBear and (i_emaGapUse ? c_emaGap : true) and
(i_rsiChk ? (c_rsi >= i_rsiMinShort and c_rsi <= i_rsiMaxShort and (i_rsiDirectChk ? c_rsiFalling : true)) : true) and
(i_usePullbackChk ? c_pullbackCountRed : true)
if c_longCondition and strategy.opentrades == 0
strategy.entry(
id = "LONG open",
direction = strategy.long,
comment = i_useComments ? '{\n "action": "start_bot_and_start_deal",\n "message_type": "bot",\n "bot_id": ' + str.tostring(i_botIDLong) + ',\n "email_token": "' + str.tostring(i_tokenID) + '",\n "delay_seconds": 0}' : na)
c_longStopPrice := c_emaSlow
c_longExitPrice := close + ((close - c_emaSlow) * i_longProfitPerc)
if ta.barssince(strategy.opentrades == 0) >= i_tradeDontClose
strategy.exit(
id = "Long close",
from_entry = "LONG open",
limit = c_longExitPrice,
stop = c_longStopPrice,
comment = i_useComments ? '{\n "action": "close_at_market_price_all_and_stop_bot",\n "message_type": "bot",\n "bot_id": ' + str.tostring(i_botIDLong) + ',\n "email_token": "' + str.tostring(i_tokenID) + '",\n "delay_seconds": 0}' : na)
if c_shortCondition and strategy.opentrades == 0
strategy.entry(
id = "SHORT open",
direction = strategy.short,
comment = i_useComments ? '{\n "action": "start_bot_and_start_deal",\n "message_type": "bot",\n "bot_id": ' + str.tostring(i_botIDShort) + ',\n "email_token": "' + str.tostring(i_tokenID) + '",\n "delay_seconds": 0}' : na)
c_shortStopPrice := c_emaSlow
c_shortExitPrice := close - ((c_emaSlow - close) * i_shortProfitPerc)
if ta.barssince(strategy.opentrades == 0) >= i_tradeDontClose
strategy.exit(
id = "Short close",
from_entry = "SHORT open",
limit = c_shortExitPrice,
stop = c_shortStopPrice,
comment = i_useComments ? '{\n "action": "close_at_market_price_all_and_stop_bot",\n "message_type": "bot",\n "bot_id": ' + str.tostring(i_botIDShort) + ',\n "email_token": "' + str.tostring(i_tokenID) + '",\n "delay_seconds": 0}' : na)
// Plot
plot(c_emaFast, "Ema fast", color = color.new(#4db6ac, 40), linewidth = 2, display = display.none)
plot(c_emaSlow, "Ema slow", color = color.new(#f57c00, 40), linewidth = 2, display = display.none)
plotshape(c_downFractal, title = "Long shape", style=shape.triangleup, location=location.belowbar, offset=-i_wf, color=#009688, size = size.tiny, display = display.none)
plotshape(c_upFractal, title = "Short shape", style=shape.triangledown, location=location.abovebar, offset=-i_wf, color=#F44336, size = size.tiny, display = display.none)
// --- BEGIN TESTER CODE --- //
// Declare performance tracking variables
var balance = startBalance
var maxBalance = 0.0
var totalWins = 0
var totalLoss = 0
// Detect winning trades
if strategy.wintrades != strategy.wintrades[1]
balance := balance + riskPerTrade / 100 * balance * i_rr
totalWins := totalWins + 1
if balance > maxBalance
maxBalance := balance
maxBalance
// Detect losing trades
if strategy.losstrades != strategy.losstrades[1]
balance := balance - riskPerTrade / 100 * balance
totalLoss := totalLoss + 1
// Prepare stats table
var table testTable = table.new(i_testerPosition == "Top right" ? position.top_right : i_testerPosition == "Top left" ? position.top_left : i_testerPosition == "Bottom right" ? position.bottom_right :position.bottom_left, 5, 2, border_width=2)
f_fillCell(_table, _column, _row, _title, _value, _bgcolor, _txtcolor) =>
_cellText = _title + '\n' + _value
table.cell(_table, _column, _row, _cellText, bgcolor=_bgcolor, text_color=_txtcolor)
// Draw stats table
var bgcolor = color.new(color.black, 0)
if i_drawTester
if barstate.islastconfirmedhistory
// Update table
dollarReturn = balance - startBalance
f_fillCell(testTable, 0, 0, 'Total Trades:', str.tostring(strategy.closedtrades), bgcolor, color.white)
f_fillCell(testTable, 0, 1, 'Win Rate:', str.tostring(l_zen.truncate(strategy.wintrades / strategy.closedtrades * 100, 2)) + '%', (strategy.wintrades / strategy.closedtrades * 100) >= 50 ? color.green : bgcolor, color.white)
f_fillCell(testTable, 1, 0, 'Starting:', '$' + str.tostring(startBalance), bgcolor, color.white)
f_fillCell(testTable, 1, 1, 'Ending:', '$' + str.tostring(l_zen.truncate(balance, 2)), balance > startBalance ? color.green : bgcolor, color.white)
// --- END BACKTESTER CODE --- //
Expand (219 lines)
Overview
Long entries trigger on bullish fractals aligned with rising EMA and RSI conditions, while short entries occur on bearish fractals with falling EMA and RSI. The stop loss is set to the EMA slow value, with profit targets set to a configurable R:R multiple of that stop loss. It supports time-based entry windows and bar evaluation options, with backtesting visualization on the chart.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Long
bool
true
i_chkLong
Short
bool
true
i_chkShort
Start Date
time
timestamp('01 Jan 2020 00:00 +0100')
i_startTime
End Date
time
timestamp('1 Jan 2099 00:00 +0100')
i_endTime
Long Target Profit R:R
float
1.5
i_longProfitPerc
Short Target Profit R:R
float
1.5
i_shortProfitPerc
Stop loss not sooner than
int
0
i_tradeDontClose
Periods
int
2
i_wf
Evaluate WF bar
bool
true
i_wfBarsBack
Ema fast lenght
int
20
i_emaFast
Ema slow lenght
int
50
i_emaSlow
Use Ema Gap
bool
false
i_emaGapUse
Show all 34 settings
Setting
Type
Default
Variable
Ema Gap %
float
0.2
i_emaGap
Use RSI Filter
bool
false
i_rsiChk
TimeFrame
timeframe
i_rsiRes
Lenght
int
14
i_rsiLen
Min for Long
float
0.0
i_rsiMinLong
Max for Long
float
70.0
i_rsiMaxLong
Min for Short
float
30.0
i_rsiMinShort
Max for Short
float
100.0
i_rsiMaxShort
RSI direction confirmation
bool
false
i_rsiDirectChk
Bars back for direction
int
1
i_rsiDirectLen
Use Pullback
bool
false
i_usePullbackChk
Lookback period
int
2
i_pullbackCount
NOT bigger than
int
1
i_pullbackNotBigger
Show back tester
bool
true
i_drawTester
i_testerPosition
string
Top right
i_testerPosition
R:R
float
1.0
i_rr
Starting Balance
float
100.0
startBalance
Risk Per Trade
float
1.0
riskPerTrade
Show 3Commas bot comments
bool
false
i_useComments
Long Bot ID
string
i_botIDLong
Short Bot ID
string
i_botIDShort
Email Token
string
i_tokenID
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.
This strategy trades EUR/USD on the price chart using a fast and slow exponential moving average crossover filtered by RSI. It enters long when the fast EMA crosses above the slow EMA while RSI is above 50 and below the overbought level, and enters short on th
This strategy plots on the price chart and enters from a moving-average filter, ADX and Gann colours. Price above a 200-period EMA allows only longs; price below allows only shorts. A Gann aqua colour with ADX above a key level opens a long, and a Gann brown c
This long-only strategy calculates three exponential moving averages with lengths of 50, 100, and 150 periods, plotting them directly on the price chart. It also calculates a 14-period RSI and MACD. The strategy opens long positions when the moving averages al
2 EMA/SMA + RSI Strategy [Alorse] combines three configurable moving averages with a short-period Relative Strength Index filter. It plots fast, slow, and trend moving averages on the price chart. Long entries trigger when the fast moving average is above the
This strategy plots on the price chart and opens long or short positions when a weighted score from MACD, Stochastic RSI, RSI, Supertrend and a moving-average crossover exceeds a user-set trigger. Each component has its own weight and candle delay. Entries can
This strategy combines Arnaud Legoux Moving Average percentage price change calculations with an adaptive Gaussian Moving Average to generate trend-following signals. Long and short entries are triggered when the ALMA change calculation crosses above or below