This strategy executes trades based on a combination of Smart Money Concepts, including Inverse Fair Value Gaps (IFVG) and Change in State of Delivery (CISD) structure breaks.
1Copy the scriptPress Copy script above, or the copy button on the code below.
2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
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
412 lines of Pine Script v6 from FYP_Bot_1.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.
FYP_Bot_1.pine
//@version=6
strategy("FYP BOT 1", overlay = true, default_qty_type=strategy.fixed, default_qty_value=1)
// Algorithm settings
maxTradesPerDay = input.int(1, "Max Trades Per Day", minval=1, maxval=9223372036854775807, group="Money Management")
swingLookback = input.int(8, "Swing Lookback Bars", minval=1, maxval=20, group="Money Management")
riskRewardRatio = input.float(1.5, "Risk/Reward Ratio", minval=0.3, maxval=10.0, step=0.1, group="Money Management")
useTradingSession = input.bool(true, "Use NY Trading Session Only?", group="Trading Hours")
startHour = input.int(9, "NY Session Start (Hour)", minval=0, maxval=23, group="Trading Hours")
startMinute = input.int(32, "Start Minute", minval=0, maxval=59, group="Trading Hours")
endHour = input.int(10, "NY Session End (Hour)", minval=0, maxval=23, group="Trading Hours")
endMinute = input.int(0, "End Minute", minval=0, maxval=59, group="Trading Hours")
// Convert current bar time to ET
etHour = hour(time, "America/New_York")
etMinute = minute(time, "America/New_York")
currentTimeInMinutes = etHour * 60 + etMinute
startTimeInMinutes = startHour * 60 + startMinute
endTimeInMinutes = endHour * 60 + endMinute
inSession = currentTimeInMinutes >= startTimeInMinutes and currentTimeInMinutes < endTimeInMinutes
isWeekday = dayofweek != dayofweek.sunday and dayofweek != dayofweek.saturday
inTradingSession = useTradingSession ? (inSession and isWeekday) : true
// CISD
bullishBreakColor = input.color(color.black, "Bull CISD", inline = "bup")
bearishBreakColor = input.color(color.black, "Bear CISD", inline = "bep")
bullStr = input.string("+CISD", " ", tooltip = "Text to be displayed next to the CISD level.", inline = "bup")
bearStr = input.string("-CISD", " ", tooltip = "Text to be displayed next to the CISD level.", inline = "bep")
// CISD settings
lineWidth = input.int(1, "Line Width", minval=1, maxval=5)
lookAheadBars = input.int(5, "Line Extension Bars", minval=1, maxval=5)
styleOption = input.string("Solid (─)", title="Line Style", options=["Solid (─)", "Dotted (┈)", "Dashed (╌)"])
keepLevels = input.bool(false, "Keep old CISD levels")
// Settings for table
showTable = input(false, title="Enable stat table", group="Table")
tablePosition = input.string(defval="Top Right", title="Table Position",
options=["Top Right", "Bottom Right", "Middle Right", "Bottom Center", "Middle Left"], group="Table")
// Filter: 1 minute liquidity sweep filter
useSweepFilter = input.bool(true, "Use 1-Min Liquidity Sweep Filter", group="Liquidity Sweep (1-Min)")
sweepLookback = input.int(5, "Sweep Lookback Bars", minval=2, maxval=50, group="Liquidity Sweep (1-Min)", tooltip="How many 1-min bars back to look for the swept high/low.")
sweepExpiry = input.int(5, "Sweep Expiry Bars", minval=1, maxval=50, group="Liquidity Sweep (1-Min)", tooltip="How many bars the detected sweep remains valid for an entry.")
// Structure Definitions
type MarketStructure
float topPrice
float bottomPrice
bool isBullish
type cisd
line level
label txt
bool completed
// Line style option
lineStyle = styleOption == "Dotted (┈)" ? line.style_dotted :
styleOption == "Dashed (╌)" ? line.style_dashed :
line.style_solid
// Variable Declarations
var MarketStructure currentStructure = MarketStructure.new(0, 0, false)
var cisdLevelsBu = array.new<cisd>()
var cisdLevelsBe = array.new<cisd>()
var bool isBullishPullback = false
var bool isBearishPullback = false
var float potentialTopPrice = na
var float potentialBottomPrice = na
var int bullishBreakIndex = na
var int bearishBreakIndex = na
var bool currentState = false
var int tradesToday = 0
var string ifvgState = "None"
// Table position helper
gettablePos(pos) =>
switch pos
"Top Right" => position.top_right
"Bottom Right" => position.bottom_right
"Middle Right" => position.middle_right
"Bottom Center" => position.bottom_center
"Middle Left" => position.bottom_left
// IFVG Types
type FVGData
float top
float bottom
bool isBullish
int startBar
int endBar
bool isInverted
int invertBar
box ifvgBox
var array<FVGData> fvgArray = array.new<FVGData>()
var int lastInvertedIndex = -1
// Session reset logic
var int lastTradeDay = 0
currentDay = dayofmonth + month * 100 + year * 10000
if currentDay != lastTradeDay
tradesToday := 0
lastTradeDay := currentDay
array.clear(fvgArray)
// Reset IFVG state when entering session each day
var bool sessionStarted = false
if inTradingSession and not sessionStarted[1]
sessionStarted := true
ifvgState := "None"
else if not inTradingSession
sessionStarted := false
// Filter: 1 minute liquidity sweep
recentSwingLow = ta.lowest(low, sweepLookback)
recentSwingHigh = ta.highest(high, sweepLookback)
bullishSweep1m = low < recentSwingLow[1] and close > recentSwingLow[1]
bearishSweep1m = high > recentSwingHigh[1] and close < recentSwingHigh[1]
var bool pendingBullSweep1m = false
var int bullSweepExpiry1m = 0
var bool pendingBearSweep1m = false
var int bearSweepExpiry1m = 0
if bullishSweep1m
pendingBullSweep1m := true
bullSweepExpiry1m := bar_index + sweepExpiry
if bearishSweep1m
pendingBearSweep1m := true
bearSweepExpiry1m := bar_index + sweepExpiry
if bar_index >= bullSweepExpiry1m
pendingBullSweep1m := false
if bar_index >= bearSweepExpiry1m
pendingBearSweep1m := false
sweep1mLongOk = useSweepFilter ? pendingBullSweep1m : true
sweep1mShortOk = useSweepFilter ? pendingBearSweep1m : true
// Pullback Detection
bearishPullbackDetected = close[1] > open[1]
bullishPullbackDetected = close[1] < open[1]
if bearishPullbackDetected and not isBearishPullback
isBearishPullback := true
potentialTopPrice := open[1]
bullishBreakIndex := bar_index[1]
if bullishPullbackDetected and not isBullishPullback
isBullishPullback := true
potentialBottomPrice := open[1]
bearishBreakIndex := bar_index[1]
// Update Potential Levels
if isBullishPullback
if open < potentialBottomPrice
potentialBottomPrice := open
bearishBreakIndex := bar_index
if (close < open) and (open > potentialBottomPrice)
potentialBottomPrice := open
bearishBreakIndex := bar_index
if isBearishPullback
if open > potentialTopPrice
potentialTopPrice := open
bullishBreakIndex := bar_index
if (close > open) and open < potentialTopPrice
potentialTopPrice := open
bullishBreakIndex := bar_index
// Structure Updates - Bearish Break
if low < currentStructure.bottomPrice
currentStructure.bottomPrice := low
currentStructure.isBullish := false
if isBearishPullback and (bar_index - bullishBreakIndex != 0)
currentStructure.topPrice := math.max(high[bar_index - bullishBreakIndex], high[bar_index - bullishBreakIndex + 1])
isBearishPullback := false
bearishLine = line.new(bullishBreakIndex, potentialTopPrice, bar_index + lookAheadBars, potentialTopPrice, color=bullishBreakColor, width=lineWidth, style=lineStyle)
bearishLabel = label.new(bar_index + lookAheadBars, potentialTopPrice, bullStr, color=color.new(color.white, 100), textcolor=bullishBreakColor, style=label.style_label_left, text_font_family=font.family_default, size=size.small, text_formatting=text.format_italic)
b = cisd.new(bearishLine, bearishLabel, false)
cisdLevelsBe.push(b)
else if close[1] > open[1] and close < open
currentStructure.topPrice := high[1]
isBearishPullback := false
bearishLine = line.new(bullishBreakIndex, potentialTopPrice, bar_index + lookAheadBars, potentialTopPrice, color=bullishBreakColor, width=lineWidth, style=lineStyle)
bearishLabel = label.new(bar_index + lookAheadBars, potentialTopPrice, bullStr, color=color.new(color.white, 100), textcolor=bullishBreakColor, style=label.style_label_left, text_font_family=font.family_default, size=size.small, text_formatting=text.format_italic)
b = cisd.new(bearishLine, bearishLabel, false)
cisdLevelsBe.push(b)
// Structure Updates - Bullish Break
if high > currentStructure.topPrice
currentStructure.isBullish := true
currentStructure.topPrice := high
if isBullishPullback and (bar_index - bearishBreakIndex != 0)
currentStructure.bottomPrice := math.min(low[bar_index - bearishBreakIndex], low[bar_index - bearishBreakIndex + 1])
isBullishPullback := false
bullishLine = line.new(bearishBreakIndex, potentialBottomPrice, bar_index + lookAheadBars, potentialBottomPrice, color=bearishBreakColor, width=lineWidth, style=lineStyle)
bullishLabel = label.new(bar_index + lookAheadBars, potentialBottomPrice, bearStr, color=color.new(color.white, 100), textcolor=bearishBreakColor, style=label.style_label_left, text_font_family=font.family_default, size=size.small, text_formatting=text.format_italic)
bu = cisd.new(bullishLine, bullishLabel, false)
cisdLevelsBu.push(bu)
else if close[1] < open[1] and close > open
currentStructure.bottomPrice := low[1]
isBullishPullback := false
bullishLine = line.new(bearishBreakIndex, potentialBottomPrice, bar_index + lookAheadBars, potentialBottomPrice, color=bearishBreakColor, width=lineWidth, style=lineStyle)
bullishLabel = label.new(bar_index + lookAheadBars, potentialBottomPrice, bearStr, color=color.new(color.white, 100), textcolor=bearishBreakColor, style=label.style_label_left, text_font_family=font.family_default, size=size.small, text_formatting=text.format_italic)
bu = cisd.new(bullishLine, bullishLabel, false)
cisdLevelsBu.push(bu)
if array.size(cisdLevelsBu) > 1 and not keepLevels
latest = array.shift(cisdLevelsBu)
line.delete(latest.level)
label.delete(latest.txt)
if array.size(cisdLevelsBe) > 1 and not keepLevels
latest = array.shift(cisdLevelsBe)
line.delete(latest.level)
label.delete(latest.txt)
if array.size(cisdLevelsBu) >= 1
latest = array.get(cisdLevelsBu, 0)
if not (close < latest.level.get_y2()) and not latest.completed
line.set_x2(latest.level, bar_index + lookAheadBars)
label.set_x(latest.txt, bar_index + lookAheadBars)
if close < latest.level.get_y2() and not latest.completed and close[1] > latest.level.get_y2()
latest.completed := true
if array.size(cisdLevelsBe) >= 1 and not keepLevels
latest = array.get(cisdLevelsBe, 0)
if not (close > latest.level.get_y2()) and not latest.completed
line.set_x2(latest.level, bar_index + lookAheadBars)
label.set_x(latest.txt, bar_index + lookAheadBars)
if close > latest.level.get_y2() and not latest.completed and close[1] < latest.level.get_y2()
latest.completed := true
// CISD state logic
var float bullCisdLevel = na
var float bearCisdLevel = na
if array.size(cisdLevelsBu) > 0
bullCisdLevel := array.get(cisdLevelsBu, 0).level.get_y2()
if array.size(cisdLevelsBe) > 0
bearCisdLevel := array.get(cisdLevelsBe, 0).level.get_y2()
bullCross = not na(bearCisdLevel) and close > bearCisdLevel and close[1] <= bearCisdLevel
bearCross = not na(bullCisdLevel) and close < bullCisdLevel and close[1] >= bullCisdLevel
if bullCross
currentState := true
else if bearCross
currentState := false
// IFVG logic
fvgThreshold = input.float(0, "FVG Threshold %", minval=0, step=0.1)
endMethod = input.string("Close", "Invalidation Method", options=["Wick", "Close"])
bullIFVGColor = input.color(color.new(color.blue, 70), "Bullish IFVG")
bearIFVGColor = input.color(color.new(color.orange, 70), "Bearish IFVG")
showLastIFVG = 1
bullishGap = low > high[2]
bullGapSize = bullishGap ? low - high[2] : 0.0
bullThreshold = bullishGap ? (bullGapSize / high[2]) * 100 >= fvgThreshold : false
bearishGap = high < low[2]
bearGapSize = bearishGap ? low[2] - high : 0.0
bearThreshold = bearishGap ? (bearGapSize / low[2]) * 100 >= fvgThreshold : false
// Session restricted IFVG creation
if bullThreshold and inTradingSession
fvg = FVGData.new(top=low, bottom=high[2], isBullish=true, startBar=bar_index, endBar=na, isInverted=false, invertBar=na, ifvgBox=na)
array.unshift(fvgArray, fvg)
if bearThreshold and inTradingSession
fvg = FVGData.new(top=low[2], bottom=high, isBullish=false, startBar=bar_index, endBar=na, isInverted=false, invertBar=na, ifvgBox=na)
array.unshift(fvgArray, fvg)
if array.size(fvgArray) > 0
for i = 0 to array.size(fvgArray) - 1
fvg = array.get(fvgArray, i)
if not fvg.isInverted
if fvg.isBullish
breached = endMethod == "Wick" ? low < fvg.bottom : close < fvg.bottom
if breached
fvg.isInverted := true
fvg.invertBar := bar_index
fvg.endBar := bar_index
lastInvertedIndex := i
else
breached = endMethod == "Wick" ? high > fvg.top : close > fvg.top
if breached
fvg.isInverted := true
fvg.invertBar := bar_index
fvg.endBar := bar_index
lastInvertedIndex := i
if array.size(fvgArray) > 0
bullIFVGCount = 0
bearIFVGCount = 0
for i = 0 to array.size(fvgArray) - 1
fvg = array.get(fvgArray, i)
box.delete(fvg.ifvgBox)
if fvg.isInverted
if not fvg.isBullish and bullIFVGCount < showLastIFVG
fvg.ifvgBox := box.new(left=fvg.startBar, top=fvg.top, right=bar_index + 10, bottom=fvg.bottom, border_color=color.new(color.blue, 50), bgcolor=bullIFVGColor, text="IFVG", text_color=color.blue, text_size=size.small, border_style=line.style_dashed)
bullIFVGCount += 1
else if fvg.isBullish and bearIFVGCount < showLastIFVG
fvg.ifvgBox := box.new(left=fvg.startBar, top=fvg.top, right=bar_index + 10, bottom=fvg.bottom, border_color=color.new(color.orange, 50), bgcolor=bearIFVGColor, text="IFVG", text_color=color.orange, text_size=size.small, border_style=line.style_dashed)
bearIFVGCount += 1
// IFVG state update
if array.size(fvgArray) > 0
for i = 0 to array.size(fvgArray) - 1
fvg = array.get(fvgArray, i)
if fvg.isInverted
ifvgState := fvg.isBullish ? "Bearish" : "Bullish"
break
if not inTradingSession
ifvgState := "None"
// IFVG + CISD double confirmation
var string lastIfvgState = "None"
var bool lastCisdBullish = false
ifvgTurnedBullish = (lastIfvgState != "Bullish") and (ifvgState == "Bullish")
cisdTurnedBullish = (lastCisdBullish == false) and (currentState == true)
ifvgTurnedBearish = (lastIfvgState != "Bearish") and (ifvgState == "Bearish")
cisdTurnedBearish = (lastCisdBullish == true) and (currentState == false)
bullDouble = ((lastIfvgState == "Bullish") and (ifvgState == "Bullish") and cisdTurnedBullish) or (ifvgTurnedBullish and cisdTurnedBullish)
bearDouble = ((lastIfvgState == "Bearish") and (ifvgState == "Bearish") and cisdTurnedBearish) or (ifvgTurnedBearish and cisdTurnedBearish)
var line doubleConfirmLine = na
if bullDouble
doubleConfirmLine := line.new(bar_index, close, bar_index + 5, close, color=color.green, width=3, style=line.style_solid)
if bearDouble
doubleConfirmLine := line.new(bar_index, close, bar_index + 5, close, color=color.red, width=3, style=line.style_solid)
lastIfvgState := ifvgState
lastCisdBullish := currentState
// Most recent IFVG levels (expire after 10 bars)
ifvgLookback = 10
var float ifvgTop = na
var float ifvgBottom = na
var int ifvgBarIndex = na
if array.size(fvgArray) > 0
for i = 0 to array.size(fvgArray) - 1
fvg = array.get(fvgArray, i)
if fvg.isInverted
if bar_index - fvg.invertBar <= ifvgLookback
ifvgTop := fvg.top
ifvgBottom := fvg.bottom
ifvgBarIndex := fvg.invertBar
else
ifvgTop := na
ifvgBottom := na
ifvgBarIndex := na
break
// Swing high/low for stops
swingLow = ta.lowest(low, swingLookback)
swingHigh = ta.highest(high, swingLookback)
// EMA trend filter
emaLength = input.int(20, "EMA Length", minval=1, group="Trend Filter")
emaSource = input.source(close, "EMA Source", group="Trend Filter")
showEma = input.bool(true, "Show EMA on Chart", group="Trend Filter")
ema = ta.ema(emaSource, emaLength)
plot(showEma ? ema : na, "Trend EMA", color=color.yellow, linewidth=2)
// Strategy execution
noTradeOpen = strategy.position_size == 0
canTradeNow = tradesToday < maxTradesPerDay
longEntry = bullDouble and inTradingSession and noTradeOpen and canTradeNow and close > ema and sweep1mLongOk
shortEntry = bearDouble and inTradingSession and noTradeOpen and canTradeNow and close < ema and sweep1mShortOk
if longEntry and not na(ifvgBottom)
longStopPrice = swingLow
riskAmount = close - longStopPrice
longTakeProfit = close + (riskAmount * riskRewardRatio)
strategy.entry("Long", strategy.long)
strategy.exit("Long Exit", "Long", stop=longStopPrice, limit=longTakeProfit)
tradesToday += 1
if shortEntry and not na(ifvgTop)
shortStopPrice = swingHigh
riskAmount = shortStopPrice - close
shortTakeProfit = close - (riskAmount * riskRewardRatio)
strategy.entry("Short", strategy.short)
strategy.exit("Short Exit", "Short", stop=shortStopPrice, limit=shortTakeProfit)
tradesToday += 1
// Table
if showTable and barstate.islast
var tbl = table.new(gettablePos(tablePosition), 3, 6, bgcolor=chart.bg_color, border_color=chart.fg_color, frame_color=chart.fg_color, frame_width=1, border_width=1)
table.cell(tbl, 0, 0, syminfo.ticker + ", " + timeframe.period + " Strategy|", text_size=size.tiny, text_color=chart.fg_color, text_font_family=font.family_monospace)
table.cell(tbl, 0, 1, "CISD State", text_color=chart.bg_color, text_size=size.small, text_font_family=font.family_monospace, text_formatting=text.format_bold, bgcolor=chart.fg_color)
table.cell(tbl, 0, 2, currentState ? "Bullish" : "Bearish", text_color=chart.fg_color, text_size=size.small, text_font_family=font.family_monospace, text_formatting=text.format_bold)
table.cell(tbl, 1, 1, "IFVG State", text_color=chart.bg_color, text_size=size.small, text_font_family=font.family_monospace, text_formatting=text.format_bold, bgcolor=chart.fg_color)
table.cell(tbl, 1, 2, na(ifvgTop) ? "Expired" : ifvgState, text_color=chart.fg_color, text_size=size.small, text_font_family=font.family_monospace, text_formatting=text.format_bold)
table.cell(tbl, 2, 1, "Trades Today", text_color=chart.bg_color, text_size=size.small, text_font_family=font.family_monospace, text_formatting=text.format_bold, bgcolor=chart.fg_color)
table.cell(tbl, 2, 2, str.tostring(tradesToday) + "/" + str.tostring(maxTradesPerDay), text_color=chart.fg_color, text_size=size.small, text_font_family=font.family_monospace, text_formatting=text.format_bold)
Overview
Entries require confluence between IFVG flips, structural CISD breaks, a 20-period exponential moving average trend filter, and a recent liquidity sweep. Risk management enforces a daily trade limit, swing-based stop losses, and configurable risk-to-reward take profits. It plots structural levels, imbalance zones, and an optional EMA directly on the price chart.
Inverse Fair Value Gap detection
Change in State of Delivery structural breaks
One-minute liquidity sweep filter
Session time and daily trade limits
Swing-based stop loss with fixed risk-reward ratio
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Max Trades Per Day
int
1
maxTradesPerDay
Swing Lookback Bars
int
8
swingLookback
Risk/Reward Ratio
float
1.5
riskRewardRatio
Use NY Trading Session Only?
bool
true
useTradingSession
NY Session Start (Hour)
int
9
startHour
Start Minute
int
32
startMinute
NY Session End (Hour)
int
10
endHour
End Minute
int
0
endMinute
Bull CISD
color
color.black
bullishBreakColor
Bear CISD
color
color.black
bearishBreakColor
string
+CISD
bullStr
string
-CISD
bearStr
Show all 28 settings
Setting
Type
Default
Variable
Line Width
int
1
lineWidth
Line Extension Bars
int
5
lookAheadBars
Line Style
string
Solid (─)
styleOption
Keep old CISD levels
bool
false
keepLevels
Enable stat table
bool
false
showTable
Table Position
string
Top Right
tablePosition
Use 1-Min Liquidity Sweep Filter
bool
true
useSweepFilter
Sweep Lookback Bars
int
5
sweepLookback
Sweep Expiry Bars
int
5
sweepExpiry
FVG Threshold %
float
0
fvgThreshold
Invalidation Method
string
Close
endMethod
Bullish IFVG
color
color.new(color.blue, 70)
bullIFVGColor
Bearish IFVG
color
color.new(color.orange, 70)
bearIFVGColor
EMA Length
int
20
emaLength
EMA Source
source
close
emaSource
Show EMA on Chart
bool
true
showEma
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 tracks GBP/USD market structure by storing confirmed pivot highs and lows. It enters long when price crosses above the last pivot high while above a trend EMA, and short when price crosses below the last pivot low while below that EMA. Entries oc
This futures trading strategy combines Fair Value Gap (FVG) detection filtered by Average True Range (ATR) with multi-timeframe Exponential Moving Averages (8, 55, and 165 lengths). It identifies entry setups during a defined intraday trading session window wh
This strategy enters long or short on the price chart when price is on the correct side of a 200-period EMA, MACD agrees, RSI is not at the opposing extreme, ADX exceeds a threshold, volume beats a 20-bar average, and close breaks the recent high or low. A tim
This strategy evaluates breakout and trend opportunities on the price chart using a weighted composite score of volume, trend, and candlestick structure. It tracks volume spikes, fast and slow EMAs, higher-timeframe EMA trend alignment, opening ranges, and ses
This strategy trades Nikkei 225 futures during a configurable overnight session, defaulting to 19:00–03:00 ET. It builds long and short trend conditions from an 8/21 EMA pair and a 200-period SMA, with RSI, momentum, an ATR-scaled breakout range, and a volume
This ICT-based strategy trades during the 9:45 AM to 10:15 AM New York session window. It calculates a daily bias using a 20-period daily EMA and tracks previous day highs and lows to identify liquidity sweeps. The script refines order block entry levels and m