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 indicator, select all of the template text and paste. Press Save and give it a name.
4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.
Source Code
915 lines of Pine Script v6 from openSourceFractal.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.
openSourceFractal.pine
//@version=6
indicator("openSourceFractal", overlay = true, max_boxes_count = 500, max_lines_count = 500, max_labels_count = 500)
//#region [INPUTS]
grpModel = 'General Settings'
showAlerts = input.bool(true, 'Alerts?', group = grpModel, inline = 'alerts', tooltip = "To turn on the alerts, follow these steps:\n\n1) Make sure that the 'Alerts?' toggle is ON.\n2) Find, and click on, the three dots next to the indicator title on the top left of your chart, or right-click on the indicator on the chart.\n3) Click 'Add Alert on Fractal Model [Pro+]'\n4) Choose the Alert Name, if you wish, and confirm by clicking 'Create'")
showEarlyCisdAlerts = input.bool(false, 'Early CISD Alerts?', group = grpModel, inline = 'alerts', tooltip = "The global 'Alerts?' toggle above acts as a master switch. If it is OFF, Early CISD alerts will not fire even if this toggle is ON.")
showHistory = input.int(1, 'History', minval = 0, maxval = 40, group = grpModel, tooltip = 'Number of past setups (valid or invalidated) to keep drawn on the chart. 0 = unlimited.')
biasFilter = input.string('Neutral', 'Bias', options = ['Neutral', 'Bullish', 'Bearish'], group = grpModel)
modelType = input.string('Auto', 'Fractal', options = ['Custom', 'Auto', '1m-15m', '3m-30m', '5m-1H', '15m-4H', '1H-1D', '4H-1W', '1D-1W'], group = grpModel, inline = 'fractal')
showEarlyCisd = input.bool(true, 'Early CISD', group = grpModel, inline = 'fractal')
customLTF = input.timeframe('5', 'Custom LTF ', group = grpModel, inline = 'custom', active = modelType == 'Custom')
customHTF = input.timeframe('60', '', group = grpModel, inline = 'custom', tooltip = 'Only active when Alignment is set to Custom', active = modelType == 'Custom')
grpCandles = 'HTF Candles'
htfCandleCount = input.int(4, 'HTF Candles', minval = 1, maxval = 10, group = grpCandles, inline = 'count')
hideHtf = input.bool(false, 'Hide?', group = grpCandles, inline = 'count', tooltip = 'Hide HTF candles preview')
candleOffsetIn = input.int(0, 'Offset', minval = 0, group = grpCandles)
bullBody = input.color(color.new(color.green, 0), 'Body', group = grpCandles, inline = 'body')
bearBody = input.color(color.new(#000000, 0), '', group = grpCandles, inline = 'body')
borderBull = input.color(color.new(#000000, 0), 'Borders', group = grpCandles, inline = 'border')
borderBear = input.color(color.new(#000000, 0), '', group = grpCandles, inline = 'border')
wickBull = input.color(color.new(#000000, 0), 'Wick', group = grpCandles, inline = 'wick')
wickBear = input.color(color.new(#000000, 0), '', group = grpCandles, inline = 'wick')
showHtfOpen = input.bool(true, 'HTF Open', group = grpCandles, inline = 'htfopen')
htfOpenColor = input.color(color.new(#1a1a1a, 30), '', group = grpCandles, inline = 'htfopen')
htfOpenStyle = input.string('Solid', '', options = ['Solid', 'Dashed', 'Dotted'], group = grpCandles, inline = 'htfopen')
htfOpenWidth = input.int(1, '', minval = 1, group = grpCandles, inline = 'htfopen')
showVlines = input.bool(true, 'O/C Time', group = grpCandles, inline = 'vline')
lineColor = input.color(color.new(color.gray, 60), '', group = grpCandles, inline = 'vline')
lineStyle = input.string('Solid', '', options = ['Solid', 'Dashed', 'Dotted'], group = grpCandles, inline = 'vline')
lineWidth = input.int(1, '', minval = 1, group = grpCandles, inline = 'vline')
showLH = input.bool(false, 'L/H Lines', group = grpCandles, inline = 'lh')
lhColor = input.color(color.new(#1a1a1a, 30), '', group = grpCandles, inline = 'lh')
lhStyle = input.string('Solid', '', options = ['Solid', 'Dashed', 'Dotted'], group = grpCandles, inline = 'lh')
lhWidth = input.int(1, '', minval = 1, group = grpCandles, inline = 'lh')
grpVisuals = 'Model Style'
showLabels = input.bool(true, 'Labels?', group = grpVisuals, inline = 'lbl')
labelSize = input.string('Small', '', group = grpVisuals, inline = 'lbl', options = ['Tiny', 'Small', 'Normal', 'Large', 'Huge'], active = showLabels)
showSweep = input.bool(false, 'Candle 1 Sweep', group = grpVisuals, inline = 'sweep')
sweepColor = input.color(color.new(#000000, 0), '', group = grpVisuals, inline = 'sweep')
sweepStyle = input.string('Solid', '', options = ['Solid', 'Dashed', 'Dotted'], group = grpVisuals, inline = 'sweep')
sweepWidth = input.int(1, '', minval = 1, group = grpVisuals, inline = 'sweep')
earlyCisdColor = input.color(color.new(color.blue, 0), 'Early CISD', group = grpVisuals, inline = 'earlycisd', active = showEarlyCisd)
earlyCisdStyle = input.string('Dashed', '', options = ['Solid', 'Dashed', 'Dotted'], group = grpVisuals, inline = 'earlycisd', active = showEarlyCisd)
earlyCisdWidth = input.int(1, '', minval = 1, group = grpVisuals, inline = 'earlycisd', active = showEarlyCisd)
showBullCisd = input.bool(true, 'Bullish CISD', group = grpVisuals, inline = 'bullcisd')
cisdBullColor = input.color(color.new(color.blue, 0), '', group = grpVisuals, inline = 'bullcisd')
cisdBullStyle = input.string('Solid', '', options = ['Solid', 'Dashed', 'Dotted'], group = grpVisuals, inline = 'bullcisd', active = showBullCisd)
cisdBullWidth = input.int(1, '', minval = 1, group = grpVisuals, inline = 'bullcisd', active = showBullCisd)
showBearCisd = input.bool(true, 'Bearish CISD', group = grpVisuals, inline = 'bearcisd')
cisdBearColor = input.color(color.new(color.blue, 0), '', group = grpVisuals, inline = 'bearcisd')
cisdBearStyle = input.string('Solid', '', options = ['Solid', 'Dashed', 'Dotted'], group = grpVisuals, inline = 'bearcisd', active = showBearCisd)
cisdBearWidth = input.int(1, '', minval = 1, group = grpVisuals, inline = 'bearcisd', active = showBearCisd)
showEQ = input.bool(true, 'Candle Equilibrium', group = grpVisuals, inline = 'eq')
eqColor = input.color(color.new(#000000, 0), '', group = grpVisuals, inline = 'eq')
eqStyle = input.string('Dotted', '', options = ['Solid', 'Dashed', 'Dotted'], group = grpVisuals, inline = 'eq')
eqWidth = input.int(1, '', minval = 1, group = grpVisuals, inline = 'eq')
showTSpot = input.bool(true, 'T-Spot Box', group = grpVisuals, inline = 'tspot')
tSpotBullColor = input.color(color.new(color.green, 80), '', group = grpVisuals, inline = 'tspot')
tSpotBearColor = input.color(color.new(color.gray, 80), '', group = grpVisuals, inline = 'tspot')
grpStdDev = 'Deviations'
showStdDev = input.bool(false, 'Enable Projections?', group = grpStdDev, inline = 'stdb')
stdDevInput = input.string('-1,-2,-3', '', group = grpStdDev, inline = 'stdb', active = showStdDev)
stdDevCalc = input.string('Wick', 'Method', options = ['Wick', 'Body'], group = grpStdDev, inline = 'stdb', active = showStdDev)
stdDevColor = input.color(color.new(#000000, 0), '', group = grpStdDev, inline = 'stdc', active = showStdDev)
stdDevStyle = input.string('Solid', '', options = ['Solid', 'Dashed', 'Dotted'], group = grpStdDev, inline = 'stdc', active = showStdDev)
stdDevWidth = input.int(1, '', minval = 1, group = grpStdDev, inline = 'stdc', active = showStdDev)
grpDash = 'Dashboard'
boolTable = input.bool(true, 'Show Dashboard', group = grpDash)
tableVert = input.string('Top', '', options = ['Top', 'Middle', 'Bottom'], inline = 'loc', group = grpDash)
tableHorz = input.string('Right', '', options = ['Left', 'Center', 'Right'], inline = 'loc', group = grpDash)
dashTextColor = input.color(color.new(#131722, 0), 'Text', group = grpDash, inline = 'color')
dashBgColor = input.color(color.new(#ffffff, 0), 'Background', group = grpDash, inline = 'color')
//#endregion
//#region [CONSTANTS AND STATE]
requireCisdToShow = true
candleSpacing = 3
noColor = color.new(#000000, 100)
type HtfCandle
float o
float h
float l
float c
int hBar
int lBar
box body
line wickUp
line wickDown
line eqLine
int midX
line sweepLine = na
bool triggerBuy = false
bool triggerSell = false
type PeriodState
float prevHigh = na
float prevLow = na
int prevHighBar = na
int prevLowBar = na
float prevPrevHigh = na
float prevPrevLow = na
int prevPrevHighBar = na
int prevPrevLowBar = na
bool prevC2Buy = false
bool prevC2Sell = false
type StdDevData
array<line> lines
array<label> labels
type C2Setup
bool isBull
float extreme
int phase = 1
bool dead = false
label c2Lbl = na
box c3Box = na
line c3Dash = na
float cisdLevel = na
int cisdIndex = na
bool cisdConfirmed = false
line cisdLine = na
label cisdLabel = na
StdDevData stdDevData = na
box c4Box = na
line c4Dash = na
label c4Lbl = na
float c4Top = na
float c4Bottom = na
bool c4Invalid = false
// --- gating / deferred-draw bookkeeping ---
bool revealed = false // true once C2/C3/(C4) have actually been drawn
int c2Bar = na
float c2Price = na
float sweepFromPrice = na
int sweepFromBar = na
int c3StartBar = na
int c4StartBar = na
float c3Top = na
float c3Bottom = na
bool cisdConfirmedInC4 = false // true if CISD confirmed while already in phase 2 (C4)
type SetupRecord
C2Setup setup
int creationBar
type EarlyCisdRecord
line ln
label lbl
int barIndex
var array<HtfCandle> htfCandles = array.new<HtfCandle>()
var array<line> vLines = array.new<line>()
var array<line> hLines = array.new<line>()
var array<line> lLines = array.new<line>()
var PeriodState state = PeriodState.new()
var C2Setup buySetup = na
var C2Setup sellSetup = na
var array<SetupRecord> setupRecords = array.new<SetupRecord>()
var array<EarlyCisdRecord> bullEarlyCisdRecords = array.new<EarlyCisdRecord>()
var array<EarlyCisdRecord> bearEarlyCisdRecords = array.new<EarlyCisdRecord>()
var int htfOpenBar = na
var line htfOpenLn = na
allowBuy = biasFilter != 'Bearish'
allowSell = biasFilter != 'Bullish'
//#endregion
//#region [FUNCTIONS]
tfLabel(string tf) =>
switch tf
'1' => '1m'
'3' => '3m'
'5' => '5m'
'15' => '15m'
'60' => '1h'
'240' => '4h'
'1D' => '1D'
'D' => '1D'
'1W' => '1W'
'W' => '1W'
'1M' => '1M'
'M' => '1M'
=> tf + 'm'
tfMins(string tf) =>
switch tf
'1D' => 1440
'D' => 1440
'1W' => 10080
'W' => 10080
'1M' => 43200
'M' => 43200
=> int(str.tonumber(tf))
curTfCode() =>
timeframe.isdaily ? '1D' : timeframe.isweekly ? '1W' : timeframe.ismonthly ? '1M' : timeframe.period
tablePosition(string vert, string horz) =>
switch vert + '-' + horz
'Top-Left' => position.top_left
'Top-Center' => position.top_center
'Top-Right' => position.top_right
'Middle-Left' => position.middle_left
'Middle-Center' => position.middle_center
'Middle-Right' => position.middle_right
'Bottom-Left' => position.bottom_left
'Bottom-Center' => position.bottom_center
'Bottom-Right' => position.bottom_right
=> position.top_right
lineStyleFromStr(string s) =>
switch s
'Solid' => line.style_solid
'Dashed' => line.style_dashed
'Dotted' => line.style_dotted
=> line.style_dashed
textSize(string s) =>
switch s
'Tiny' => size.tiny
'Small' => size.small
'Normal' => size.normal
'Large' => size.large
=> size.huge
directionalLabel(int xBar, float yPrice, string txt, bool bullish) =>
label.new(xBar, yPrice, text = txt, xloc = xloc.bar_index, yloc = yloc.price, style = bullish ? label.style_label_up : label.style_label_down, color = na, textcolor = color.gray, size = textSize(labelSize), text_font_family = font.family_monospace)
eqLineColor() => showEQ ? eqColor : noColor
drawSweepLine(int fromBar, float fromPrice, int toBar) =>
line ln = showSweep ? line.new(fromBar, fromPrice, toBar, fromPrice, color = sweepColor, style = lineStyleFromStr(sweepStyle), width = sweepWidth) : na
ln
newLHLine(float price) =>
line.new(bar_index, price, bar_index, price, xloc.bar_index, extend.none, lhColor, lineStyleFromStr(lhStyle), lhWidth)
updateLHLine(line ln, float price) =>
line.set_xy1(ln, line.get_x1(ln), price)
line.set_xy2(ln, bar_index, price)
line.set_color(ln, lhColor)
line.set_style(ln, lineStyleFromStr(lhStyle))
line.set_width(ln, lhWidth)
autoHTF() =>
switch timeframe.period
'1' => '15'
'3' => '30'
'5' => '60'
'15' => '240'
'60' => '1D'
'240' => '1W'
'1D' => '1M'
=> '1M'
ltfCode() =>
switch modelType
'1m-15m' => '1'
'3m-30m' => '3'
'5m-1H' => '5'
'15m-4H' => '15'
'1H-1D' => '60'
'4H-1W' => '240'
'1D-1W' => '1D'
'Custom' => customLTF
=> curTfCode()
isAutoValidTf(int mins) => mins == 1 or mins == 3 or mins == 5 or mins == 15 or mins == 60 or mins == 240 or mins == 1440
method delete(HtfCandle c) =>
box.delete(c.body)
line.delete(c.wickUp)
line.delete(c.wickDown)
line.delete(c.eqLine)
line.delete(c.sweepLine)
method update(HtfCandle c, float curH, float curL, float curC, int curBar) =>
if curH > c.h
c.h := curH
c.hBar := curBar
if curL < c.l
c.l := curL
c.lBar := curBar
c.c := curC
c
method draw(HtfCandle c, int leftBar) =>
bool isBull = c.c >= c.o
int midX = leftBar + 1
float eq = (c.h + c.l) / 2
box.set_left(c.body, leftBar)
box.set_right(c.body, leftBar + 2)
box.set_top(c.body, math.max(c.o, c.c))
box.set_bottom(c.body, math.min(c.o, c.c))
box.set_bgcolor(c.body, isBull ? bullBody : bearBody)
box.set_border_color(c.body, isBull ? borderBull : borderBear)
line.set_x1(c.wickUp, midX)
line.set_x2(c.wickUp, midX)
line.set_y1(c.wickUp, c.h)
line.set_y2(c.wickUp, math.max(c.o, c.c))
line.set_color(c.wickUp, isBull ? wickBull : wickBear)
line.set_x1(c.wickDown, midX)
line.set_x2(c.wickDown, midX)
line.set_y1(c.wickDown, c.l)
line.set_y2(c.wickDown, math.min(c.o, c.c))
line.set_color(c.wickDown, isBull ? wickBull : wickBear)
line.set_x1(c.eqLine, midX)
line.set_x2(c.eqLine, midX)
line.set_y1(c.eqLine, eq)
line.set_y2(c.eqLine, eq)
line.set_color(c.eqLine, eqLineColor())
line.set_style(c.eqLine, lineStyleFromStr(eqStyle))
line.set_width(c.eqLine, eqWidth)
c.midX := midX
newHtfCandle() =>
HtfCandle.new(open, high, low, close, bar_index, bar_index,
box.new(bar_index, high, bar_index, low, border_color = borderBull, border_width = 1),
line.new(bar_index, high, bar_index, high, color = wickBull),
line.new(bar_index, low, bar_index, low, color = wickBull),
line.new(bar_index, high, bar_index, high, color = noColor),
na)
drawEarlyCisd(bool isBull, float level, int idx, int confirmBar) =>
array<EarlyCisdRecord> target = isBull ? bullEarlyCisdRecords : bearEarlyCisdRecords
if showEarlyCisd
ln = line.new(idx, level, confirmBar + 3, level, color = earlyCisdColor, style = lineStyleFromStr(earlyCisdStyle), width = earlyCisdWidth)
label lbl = na
if showLabels
lbl := label.new(confirmBar + 3, level, isBull ? '+CISD' : '-CISD', xloc = xloc.bar_index, style = label.style_label_left, color = na, textcolor = earlyCisdColor, size = textSize(labelSize), text_font_family = font.family_monospace)
target.push(EarlyCisdRecord.new(ln, lbl, confirmBar))
if showAlerts and showEarlyCisdAlerts
alert((isBull ? 'Bullish' : 'Bearish') + ' Early CISD confirmed on ' + syminfo.ticker, alert.freq_once_per_bar)
pruneEarlyCisd(array<EarlyCisdRecord> recs, int thresholdBar) =>
if not na(thresholdBar)
while recs.size() > 0 and recs.first().barIndex < thresholdBar
evicted = recs.shift()
line.delete(evicted.ln)
if not na(evicted.lbl)
label.delete(evicted.lbl)
consumeLastEarlyCisd(array<EarlyCisdRecord> recs) =>
if recs.size() > 0
evicted = recs.pop()
line.delete(evicted.ln)
if not na(evicted.lbl)
label.delete(evicted.lbl)
parseFloats(string src) =>
var out = array.new<float>()
array.clear(out)
for part in str.split(str.replace_all(src, ' ', ''), ',')
v = str.tonumber(part)
if not na(v)
out.push(v)
out
createStdDevProjections(C2Setup s, int confirmBar) =>
if showStdDev
devs = parseFloats(stdDevInput)
if devs.size() > 0
off = bar_index - s.cisdIndex
useWick = stdDevCalc == 'Wick'
level0 = s.cisdLevel
level1 = s.extreme
if not useWick
for j = 0 to 50
if s.isBull ? low[j] == s.extreme : high[j] == s.extreme
level1 := s.isBull ? math.min(open[j], close[j]) : math.max(open[j], close[j])
break
leftBar = s.cisdIndex
if s.cisdIndex < bar_index
prevBetter = s.isBull
? (useWick ? high[off + 1] > high[off] : math.max(open[off + 1], close[off + 1]) > math.max(open[off], close[off]))
: (useWick ? low[off + 1] < low[off] : math.min(open[off + 1], close[off + 1]) < math.min(open[off], close[off]))
if prevBetter
leftBar := s.cisdIndex - 1
rightBar = confirmBar + 3
priceRange = s.isBull ? level0 - level1 : level1 - level0
lns = array.new<line>()
lbs = array.new<label>()
for dev in devs
devPrice = s.isBull ? level0 - priceRange * dev : level0 + priceRange * dev
lns.push(line.new(leftBar, devPrice, rightBar, devPrice, xloc = xloc.bar_index, color = stdDevColor, width = stdDevWidth, style = lineStyleFromStr(stdDevStyle)))
if showLabels
lbs.push(label.new(rightBar, devPrice, str.tostring(dev), xloc = xloc.bar_index, style = label.style_label_left, color = na, textcolor = stdDevColor, size = textSize(labelSize), text_font_family = font.family_monospace))
s.stdDevData := StdDevData.new(lns, lbs)
confirmCisd(C2Setup s, int confirmBar) =>
s.cisdConfirmed := true
showLine = s.isBull ? showBullCisd : showBearCisd
clr = s.isBull ? cisdBullColor : cisdBearColor
lnStyle = s.isBull ? cisdBullStyle : cisdBearStyle
lnWidth = s.isBull ? cisdBullWidth : cisdBearWidth
if showLine
s.cisdLine := line.new(s.cisdIndex, s.cisdLevel, confirmBar + 3, s.cisdLevel, color = clr, style = lineStyleFromStr(lnStyle), width = lnWidth)
if showLabels
s.cisdLabel := label.new(confirmBar + 3, s.cisdLevel, s.isBull ? '+CISD' : '-CISD', xloc = xloc.bar_index, style = label.style_label_left, color = na, textcolor = clr, size = textSize(labelSize), text_font_family = font.family_monospace)
createStdDevProjections(s, confirmBar)
deleteSetupVisuals(C2Setup s) =>
if not na(s)
if not na(s.c2Lbl)
label.delete(s.c2Lbl)
if not na(s.cisdLine)
line.delete(s.cisdLine)
if not na(s.cisdLabel)
label.delete(s.cisdLabel)
if not na(s.c3Box)
box.delete(s.c3Box)
if not na(s.c3Dash)
line.delete(s.c3Dash)
if not na(s.c4Box)
box.delete(s.c4Box)
if not na(s.c4Dash)
line.delete(s.c4Dash)
if not na(s.c4Lbl)
label.delete(s.c4Lbl)
if not na(s.stdDevData)
if not na(s.stdDevData.lines)
for ln in s.stdDevData.lines
line.delete(ln)
if not na(s.stdDevData.labels)
for lb in s.stdDevData.labels
label.delete(lb)
registerRecord(C2Setup s) =>
setupRecords.push(SetupRecord.new(s, bar_index))
while showHistory > 0 and setupRecords.size() > showHistory
evicted = setupRecords.shift()
deleteSetupVisuals(evicted.setup)
earliestVisibleBar = setupRecords.size() > 0 ? setupRecords.first().creationBar : na
pruneEarlyCisd(bullEarlyCisdRecords, earliestVisibleBar)
pruneEarlyCisd(bearEarlyCisdRecords, earliestVisibleBar)
autoTf = switch modelType
'1m-15m' => '15'
'3m-30m' => '30'
'5m-1H' => '60'
'15m-4H' => '240'
'1H-1D' => '1D'
'4H-1W' => '1W'
'1D-1W' => '1W'
'Custom' => customHTF
=> autoHTF()
validTf = modelType == 'Auto' ? isAutoValidTf(tfMins(curTfCode())) : tfMins(curTfCode()) <= tfMins(ltfCode())
[currentO, currentH, currentL, currentC, prevH1, prevL1] = request.security(syminfo.tickerid, autoTf, [open[0], high[0], low[0], close[0], high[1], low[1]], lookahead = barmerge.lookahead_on)
c2Buy = currentL < prevL1 and currentC > prevL1
c2Sell = currentH > prevH1 and currentC < prevH1
periodChanged = time(autoTf) != time(autoTf)[1]
// Computes the C3/C4 "T-Spot" top/bottom for the CURRENT bar (always runs,
// regardless of whether the setup is currently being drawn on the chart).
computeTopBot() =>
mid = (state.prevHigh + state.prevLow) / 2
top = math.max(open, mid)
bot = math.min(open, mid)
[top, bot]
// Actually draws the T-Spot box/dash/(C4 label) using an already-computed
// top/bot and a given left-edge bar (which may be in the past, i.e. a
// "backdated" / retroactive draw once CISD finally confirms).
drawPhaseVisual(bool isBull, bool isC4, int startBar, float top, float bot) =>
yDash = isBull ? bot : top
bxCol = isBull ? tSpotBullColor : tSpotBearColor
box bx = showTSpot ? box.new(startBar, top, bar_index, bot, border_color = na, bgcolor = bxCol) : na
line dash = showEQ ? line.new(startBar, yDash, bar_index, yDash, color = eqColor, width = eqWidth, style = lineStyleFromStr(eqStyle)) : na
label lbl = na
if isC4 and showTSpot and showLabels and validTf
lbl := directionalLabel(bar_index + 2, isBull ? bot : top, 'C4', isBull)
[bx, dash, lbl]
// Draws everything that has accumulated so far for a setup (C2 label, C3
// box/dash backdated to when C3 actually started, and — if already in C4 —
// the C4 box/dash/label backdated to when C4 actually started). Called the
// moment CISD confirms. No-ops if already revealed, or if the gate is off
// (in which case reveal happens immediately at C2, same as legacy behavior).
method reveal(C2Setup s) =>
if not s.revealed
s.revealed := true
if validTf and showLabels and not na(s.c2Bar)
s.c2Lbl := directionalLabel(s.c2Bar, s.c2Price, 'C2', s.isBull)
if not na(s.sweepFromBar)
drawSweepLine(s.sweepFromBar, s.sweepFromPrice, bar_index)
[bx, dash, _] = drawPhaseVisual(s.isBull, false, s.c3StartBar, s.c3Top, s.c3Bottom)
s.c3Box := bx
s.c3Dash := dash
if s.phase == 2 and not na(s.c4StartBar)
[bx4, dash4, lbl4] = drawPhaseVisual(s.isBull, true, s.c4StartBar, s.c4Top, s.c4Bottom)
s.c4Box := bx4
s.c4Dash := dash4
s.c4Lbl := lbl4
s
newSetup(bool isBull, float extreme, int extremeBar, float extremePrice, float sweepFromPrice, int sweepFromBar, float cisdLevel, int cisdIndex, bool cisdAlreadyConfirmed, int cisdConfirmBar) =>
s = C2Setup.new(isBull, extreme, 1, false)
s.c2Bar := extremeBar
s.c2Price := extremePrice
s.sweepFromPrice := sweepFromPrice
s.sweepFromBar := sweepFromBar
s.c3StartBar := bar_index
[top, bot] = computeTopBot()
s.c3Top := top
s.c3Bottom := bot
s.cisdLevel := cisdLevel
s.cisdIndex := cisdIndex
cisdReady = cisdAlreadyConfirmed and not na(cisdLevel) and not na(cisdIndex) and not na(cisdConfirmBar)
if not requireCisdToShow
// legacy behavior: always visible immediately
s.reveal()
if cisdReady
if requireCisdToShow
s.reveal()
confirmCisd(s, cisdConfirmBar)
registerRecord(s)
s
method advanceToC4(C2Setup s) =>
if not na(s.c3Box)
box.set_right(s.c3Box, bar_index)
line.set_x2(s.c3Dash, bar_index)
s.c4StartBar := bar_index
[top, bot] = computeTopBot()
s.c4Top := top
s.c4Bottom := bot
if s.revealed
[bx, dash, lbl] = drawPhaseVisual(s.isBull, true, s.c4StartBar, top, bot)
s.c4Box := bx
s.c4Dash := dash
s.c4Lbl := lbl
s.phase := 2
s.c4Invalid := false
s
method finish(C2Setup s) =>
if not na(s.c4Box)
box.set_right(s.c4Box, bar_index)
line.set_x2(s.c4Dash, bar_index)
s.phase := 0
s
method deleteStdDev(C2Setup s) =>
if not na(s.stdDevData)
if not na(s.stdDevData.lines)
for ln in s.stdDevData.lines
line.delete(ln)
if not na(s.stdDevData.labels)
for lb in s.stdDevData.labels
label.delete(lb)
s.stdDevData := na
s
method extendAndCheck(C2Setup s) =>
if not na(s) and not s.dead and s.phase != 0
box extBox = s.phase == 1 ? s.c3Box : s.c4Box
line extDash = s.phase == 1 ? s.c3Dash : s.c4Dash
if not na(extBox)
box.set_right(extBox, bar_index)
line.set_x2(extDash, bar_index)
c2Breach = s.isBull ? low < s.extreme : high > s.extreme
if c2Breach
if not na(s.c2Lbl)
label.set_text(s.c2Lbl, 'XC2')
label.set_textcolor(s.c2Lbl, s.phase == 2 ? color.orange : color.red)
if not na(s.c3Box)
box.delete(s.c3Box)
s.c3Box := na
if not na(s.c3Dash)
line.delete(s.c3Dash)
s.c3Dash := na
if not na(s.c4Box)
box.delete(s.c4Box)
s.c4Box := na
if not na(s.c4Dash)
line.delete(s.c4Dash)
s.c4Dash := na
if not na(s.c4Lbl)
label.delete(s.c4Lbl)
s.c4Lbl := na
s.deleteStdDev()
s.dead := true
s.phase := 0
s
// If CISD only ever confirmed while in C4 (i.e. it was never valid back in
// C2/C3), and C4 subsequently gets invalidated, the whole setup — including
// the CISD confirmation itself — is discarded retroactively.
method checkC4Invalid(C2Setup s) =>
if not na(s) and not s.dead and s.phase == 2 and not s.c4Invalid
invalid = s.isBull ? close[1] < s.c4Bottom : close[1] > s.c4Top
if invalid
s.c4Invalid := true
if requireCisdToShow and s.cisdConfirmedInC4
deleteSetupVisuals(s)
s.c2Lbl := na
s.c3Box := na
s.c3Dash := na
s.c4Box := na
s.c4Dash := na
s.c4Lbl := na
s.cisdLine := na
s.cisdLabel := na
s.stdDevData := na
s.cisdConfirmed := false
s.dead := true
s.phase := 0
else
if not na(s.c4Lbl)
label.set_text(s.c4Lbl, 'XC4')
label.set_textcolor(s.c4Lbl, color.red)
if not na(s.c4Dash)
line.set_color(s.c4Dash, color.red)
if not na(s.c4Box)
box.delete(s.c4Box)
s.c4Box := na
s
method checkCisdConfirm(C2Setup s) =>
if not na(s) and not s.dead and not s.cisdConfirmed and s.phase != 0 and not na(s.cisdLevel) and not na(s.cisdIndex)
confirmed = s.isBull ? close[1] > s.cisdLevel : close[1] < s.cisdLevel
if confirmed
if s.phase == 2
s.cisdConfirmedInC4 := true
if requireCisdToShow
s.reveal()
confirmCisd(s, bar_index)
s
processC2(bool trigger, bool allow, bool isBull, float extremePrice, int extremeBar, float sweepFromPrice, int sweepFromBar, float cisdLevel, int cisdIndex, bool cisdAlreadyConfirmed, int cisdConfirmBar, C2Setup existing) =>
C2Setup result = existing
if trigger and allow
result := newSetup(isBull, extremePrice, extremeBar, extremePrice, sweepFromPrice, sweepFromBar, cisdLevel, cisdIndex, cisdAlreadyConfirmed, cisdConfirmBar)
else if periodChanged and not na(existing) and not existing.dead
result := existing.phase == 1 ? existing.advanceToC4() : existing.phase == 2 ? existing.finish() : existing
result
markTrigger(bool trigger, bool allow, int idx, bool isBuy) =>
if trigger and allow and htfCandles.size() > idx
c = htfCandles.get(idx)
if isBuy
c.triggerBuy := true
else
c.triggerSell := true
//#endregion
//#region [LOGIC]
var float o0 = open
var float h0 = high
var float l0 = low
var float o1 = open
var float h1 = high
var float l1 = low
if periodChanged
o1 := o0[1]
h1 := h0[1]
l1 := l0[1]
o0 := open
h0 := high
l0 := low
if high >= h0
h0 := high
if low <= l0
l0 := low
bool up = close > open
bool dw = close < open
bool eq = close == open
var float bullLevelFixed = na
var int bullIndexFixed = na
var float bearLevelFixed = na
var int bearIndexFixed = na
if low == l0 and low < l1
if (dw[0] or eq[0]) and (up[1] or eq[1]) and not (eq[0] and eq[1])
bullLevelFixed := open
bullIndexFixed := bar_index
else
for i = 2 to 10
if low[i] < low
break
if (up[i] or eq[i]) and dw[i - 1]
b = i - 1
bullLevelFixed := open[b]
bullIndexFixed := bar_index - b
for j = b to 0
if open[j] > bullLevelFixed and dw[j]
bullLevelFixed := open[j]
bullIndexFixed := bar_index - j
if bullLevelFixed < open
bullLevelFixed := close > open ? high : open
bullIndexFixed := bar_index
break
if high == h0 and high > h1
if (up[0] or eq[0]) and (dw[1] or eq[1]) and not (eq[0] and eq[1])
bearLevelFixed := open
bearIndexFixed := bar_index
else
for i = 2 to 10
if high[i] > high
break
if (dw[i] or eq[i]) and up[i - 1]
yb = i - 1
bearLevelFixed := open[yb]
bearIndexFixed := bar_index - yb
for j = yb to 0
if open[j] < bearLevelFixed and up[j]
bearLevelFixed := open[j]
bearIndexFixed := bar_index - j
if bearLevelFixed > open
bearLevelFixed := close < open ? low : open
bearIndexFixed := bar_index
break
var bool bullReclaimed = false
var int bullReclaimBar = na
var bool bearReclaimed = false
var int bearReclaimBar = na
bullPeriodSweep = l0 < l1
bearPeriodSweep = h0 > h1
newBullLevel = not na(bullIndexFixed) and (na(bullIndexFixed[1]) or bullIndexFixed != bullIndexFixed[1])
if newBullLevel
bullReclaimed := false
bullReclaimBar := na
if not na(bullLevelFixed) and not bullReclaimed and close[1] > bullLevelFixed
bullReclaimed := true
bullReclaimBar := bar_index
if bullPeriodSweep
drawEarlyCisd(true, bullLevelFixed, bullIndexFixed, bar_index)
newBearLevel = not na(bearIndexFixed) and (na(bearIndexFixed[1]) or bearIndexFixed != bearIndexFixed[1])
if newBearLevel
bearReclaimed := false
bearReclaimBar := na
if not na(bearLevelFixed) and not bearReclaimed and close[1] < bearLevelFixed
bearReclaimed := true
bearReclaimBar := bar_index
if bearPeriodSweep
drawEarlyCisd(false, bearLevelFixed, bearIndexFixed, bar_index)
candleOffset = candleOffsetIn + 10
if periodChanged
if htfCandles.size() > 0
newest = htfCandles.first()
state.prevHigh := newest.h
state.prevLow := newest.l
state.prevHighBar := newest.hBar
state.prevLowBar := newest.lBar
if htfCandles.size() > 1
prior = htfCandles.get(1)
state.prevPrevHigh := prior.h
state.prevPrevLow := prior.l
state.prevPrevHighBar := prior.hBar
state.prevPrevLowBar := prior.lBar
htfCandles.unshift(newHtfCandle())
while htfCandles.size() > htfCandleCount
htfCandles.pop().delete()
htfOpenBar := bar_index
if showLH
if hLines.size() > 0
line.set_x2(hLines.first(), bar_index)
line.set_x2(lLines.first(), bar_index)
hLines.unshift(newLHLine(high))
lLines.unshift(newLHLine(low))
while hLines.size() > htfCandleCount
line.delete(hLines.pop())
while lLines.size() > htfCandleCount
line.delete(lLines.pop())
else if htfCandles.size() > 0
htfCandles.set(0, htfCandles.first().update(high, low, close, bar_index))
if showLH and htfCandles.size() > 0 and hLines.size() > 0
curCandle = htfCandles.first()
updateLHLine(hLines.first(), curCandle.h)
updateLHLine(lLines.first(), curCandle.l)
if periodChanged
state.prevC2Buy := c2Buy[1]
state.prevC2Sell := c2Sell[1]
c2BuyTrigger = periodChanged and state.prevC2Buy
c2SellTrigger = periodChanged and state.prevC2Sell
if c2BuyTrigger and allowBuy and bullReclaimed[1]
consumeLastEarlyCisd(bullEarlyCisdRecords)
if c2SellTrigger and allowSell and bearReclaimed[1]
consumeLastEarlyCisd(bearEarlyCisdRecords)
buySetup := processC2(c2BuyTrigger, allowBuy, true, state.prevLow, state.prevLowBar, state.prevPrevLow, state.prevPrevLowBar, bullLevelFixed[1], bullIndexFixed[1], bullReclaimed[1], bullReclaimBar[1], buySetup)
sellSetup := processC2(c2SellTrigger, allowSell, false, state.prevHigh, state.prevHighBar, state.prevPrevHigh, state.prevPrevHighBar, bearLevelFixed[1], bearIndexFixed[1], bearReclaimed[1], bearReclaimBar[1], sellSetup)
markTrigger(c2BuyTrigger, allowBuy, 2, true)
markTrigger(c2SellTrigger, allowSell, 2, false)
buySetup := buySetup.extendAndCheck()
sellSetup := sellSetup.extendAndCheck()
buySetup := buySetup.checkC4Invalid()
sellSetup := sellSetup.checkC4Invalid()
buySetup := buySetup.checkCisdConfirm()
sellSetup := sellSetup.checkCisdConfirm()
if showAlerts
if c2BuyTrigger and allowBuy
alert('Bullish C2 formed on ' + syminfo.ticker, alert.freq_once_per_bar)
if c2SellTrigger and allowSell
alert('Bearish C2 formed on ' + syminfo.ticker, alert.freq_once_per_bar)
if showVlines and periodChanged
vLines.unshift(line.new(bar_index, low - ta.tr, bar_index, high + ta.tr, xloc.bar_index, extend.both, lineColor, lineStyleFromStr(lineStyle), lineWidth))
while vLines.size() > htfCandleCount
line.delete(vLines.pop())
//#endregion
//#region [PLOT]
if (barstate.islast or barstate.isrealtime) and validTf and htfCandles.size() > 0 and not hideHtf
n = htfCandles.size()
for i = n - 1 to 0
htfCandles.get(i).draw(bar_index + candleOffset + candleSpacing * (n - 1 - i))
if showHtfOpen and not na(htfOpenBar)
curOpenX2 = box.get_right(htfCandles.first().body)
if na(htfOpenLn)
htfOpenLn := line.new(htfOpenBar, currentO, curOpenX2, currentO, xloc.bar_index, extend.none, htfOpenColor, lineStyleFromStr(htfOpenStyle), htfOpenWidth)
else
line.set_xy1(htfOpenLn, htfOpenBar, currentO)
line.set_xy2(htfOpenLn, curOpenX2, currentO)
line.set_color(htfOpenLn, htfOpenColor)
line.set_style(htfOpenLn, lineStyleFromStr(htfOpenStyle))
line.set_width(htfOpenLn, htfOpenWidth)
for i = n - 1 to 0
c = htfCandles.get(i)
if i > 0
nextC = htfCandles.get(i - 1)
line.set_x2(c.eqLine, nextC.midX)
if showSweep and (c.triggerBuy or c.triggerSell) and i > 0
nextC = htfCandles.get(i - 1)
price = c.triggerBuy ? c.l : c.h
if na(c.sweepLine)
c.sweepLine := line.new(c.midX, price, nextC.midX, price, color = sweepColor, style = lineStyleFromStr(sweepStyle), width = sweepWidth)
else
line.set_xy1(c.sweepLine, c.midX, price)
line.set_xy2(c.sweepLine, nextC.midX, price)
line.set_color(c.sweepLine, sweepColor)
line.set_style(c.sweepLine, lineStyleFromStr(sweepStyle))
line.set_width(c.sweepLine, sweepWidth)
if barstate.islast and boolTable
var table wm = table.new(tablePosition(tableVert, tableHorz), 1, 4,
bgcolor = color.new(#000000, 100),
border_color = dashTextColor,
border_width = 1,
frame_color = dashTextColor,
frame_width = 1)
ltfDisp = tfLabel(ltfCode())
htfDisp = tfLabel(autoTf)
table.cell(wm, 0, 0, 'Open Source Fractal', text_color = dashBgColor, bgcolor = dashTextColor, text_size = size.normal, text_halign = text.align_center, text_font_family = font.family_default)
alignBiasStr = 'Alignment : ' + ltfDisp + '-' + htfDisp + '\n\nBias : ' + biasFilter
table.cell(wm, 0, 1, alignBiasStr, text_color = dashTextColor, bgcolor = dashBgColor, text_size = size.small, text_halign = text.align_center, text_font_family = font.family_default)
dateStr = str.format_time(time_close, 'dd MMM yyyy', syminfo.timezone)
pairDateStr = syminfo.ticker + ' | ' + dateStr
table.cell(wm, 0, 2, pairDateStr, text_color = dashTextColor, bgcolor = dashBgColor, text_size = size.small, text_halign = text.align_center, text_font_family = font.family_default)
if not validTf
curTfStr = tfLabel(curTfCode())
warnMsg = '⚠ Switch to ' + ltfDisp + ' or lower (now: ' + curTfStr + ')'
table.cell(wm, 0, 3, warnMsg, text_color = dashBgColor, bgcolor = color.new(#cc0000, 0), text_size = size.small, text_halign = text.align_center, text_font_family = font.family_default)
//#endregion
Overview
Open Source Fractal overlays custom higher-timeframe candle previews, open prices, and time markers directly on the price chart. Users can filter setups using a directional bias, select preset or custom lower-timeframe to higher-timeframe pairings, toggle early CISD alerts, and control how many historical setups remain drawn on the screen.
Liquidity sweep and CISD level identification
Higher timeframe candle overlay and open line tracking
Preset and custom multi-timeframe alignment options
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Alerts?
bool
true
showAlerts
Early CISD Alerts?
bool
false
showEarlyCisdAlerts
History
int
1
showHistory
Bias
string
Neutral
biasFilter
Fractal
string
Auto
modelType
Early CISD
bool
true
showEarlyCisd
Custom LTF
timeframe
5
customLTF
customHTF
timeframe
60
customHTF
HTF Candles
int
4
htfCandleCount
Hide?
bool
false
hideHtf
Offset
int
0
candleOffsetIn
Body
color
color.new(color.green, 0)
bullBody
Show all 40 settings
Setting
Type
Default
Variable
bearBody
color
color.new(#000000, 0)
bearBody
Borders
color
color.new(#000000, 0)
borderBull
borderBear
color
color.new(#000000, 0)
borderBear
Wick
color
color.new(#000000, 0)
wickBull
wickBear
color
color.new(#000000, 0)
wickBear
HTF Open
bool
true
showHtfOpen
htfOpenColor
color
color.new(#1a1a1a, 30)
htfOpenColor
htfOpenStyle
string
Solid
htfOpenStyle
htfOpenWidth
int
1
htfOpenWidth
O/C Time
bool
true
showVlines
lineColor
color
color.new(color.gray, 60)
lineColor
lineStyle
string
Solid
lineStyle
lineWidth
int
1
lineWidth
L/H Lines
bool
false
showLH
lhColor
color
color.new(#1a1a1a, 30)
lhColor
lhStyle
string
Solid
lhStyle
lhWidth
int
1
lhWidth
Labels?
bool
true
showLabels
labelSize
string
Small
labelSize
Candle 1 Sweep
bool
false
showSweep
sweepColor
color
color.new(#000000, 0)
sweepColor
sweepStyle
string
Solid
sweepStyle
sweepWidth
int
1
sweepWidth
Early CISD
color
color.new(color.blue, 0)
earlyCisdColor
earlyCisdStyle
string
Dashed
earlyCisdStyle
earlyCisdWidth
int
1
earlyCisdWidth
Bullish CISD
bool
true
showBullCisd
cisdBullColor
color
color.new(color.blue, 0)
cisdBullColor
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 indicator detects equal highs and lows by comparing prices within user-defined left and right bars using a tolerance percentage. It plots horizontal lines on the price chart for equal highs in one color and lows in another. A screener allows input of mult
This Pine Script indicator plots a ZigZag on the price chart. It identifies reversal points when price moves more than a set percentage from the last pivot. The script calculates forecasts using zigzag data and labels detected geometric patterns including harm
This indicator plots market structure on the price chart by identifying swing highs and lows. It connects these points with lines to visualize trend direction and detect Break of Structure (BOS) when price breaks a prior swing in the current trend. Change of C
BigBeluga Smart Money Concepts identifies and overlays market structure and volumetric order blocks directly on the price chart. It analyzes market swings, trend sweeps, and mapping structure using customizable pivot lengths and calculation modes. The indicato
Apex Trend & Liquidity Master calculates a trend cloud using customizable moving averages expanded by a volatility multiplier directly on the main price chart. It identifies dynamic support and resistance liquidity zones from swing high and low pivots, extendi
Copilot is an order flow range-reversal indicator designed to overlay trading setups directly on the price chart. It evaluates four criteria: market context (sideways vs trending regime based on ATR height), structure (double top or double bottom retests), abs