// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/ // © LuxAlgo //@version=5 indicator('Smart Money Concepts [LuxAlgo]', 'LuxAlgo - Smart Money Concepts', overlay = true, max_labels_count = 500, max_lines_count = 500, max_boxes_count = 500) //---------------------------------------------------------------------------------------------------------------------} //CONSTANTS & STRINGS & INPUTS //---------------------------------------------------------------------------------------------------------------------{ BULLISH_LEG = 1 BEARISH_LEG = 0 BULLISH = +1 BEARISH = -1 GREEN = #089981 RED = #F23645 BLUE = #2157f3 GRAY = #878b94 MONO_BULLISH = #b2b5be MONO_BEARISH = #5d606b HISTORICAL = 'Historical' PRESENT = 'Present' COLORED = 'Colored' MONOCHROME = 'Monochrome' ALL = 'All' BOS = 'BOS' CHOCH = 'CHoCH' TINY = size.tiny SMALL = size.small NORMAL = size.normal ATR = 'Atr' RANGE = 'Cumulative Mean Range' CLOSE = 'Close' HIGHLOW = 'High/Low' SOLID = '⎯⎯⎯' DASHED = '----' DOTTED = '····' SMART_GROUP = 'Smart Money Concepts' INTERNAL_GROUP = 'Real Time Internal Structure' SWING_GROUP = 'Real Time Swing Structure' BLOCKS_GROUP = 'Order Blocks' EQUAL_GROUP = 'EQH/EQL' GAPS_GROUP = 'Fair Value Gaps' LEVELS_GROUP = 'Highs & Lows MTF' ZONES_GROUP = 'Premium & Discount Zones' modeTooltip = 'Allows to display historical Structure or only the recent ones' styleTooltip = 'Indicator color theme' showTrendTooltip = 'Display additional candles with a color reflecting the current trend detected by structure' showInternalsTooltip = 'Display internal market structure' internalFilterConfluenceTooltip = 'Filter non significant internal structure breakouts' showStructureTooltip = 'Display swing market Structure' showSwingsTooltip = 'Display swing point as labels on the chart' showHighLowSwingsTooltip = 'Highlight most recent strong and weak high/low points on the chart' showInternalOrderBlocksTooltip = 'Display internal order blocks on the chart\n\nNumber of internal order blocks to display on the chart' showSwingOrderBlocksTooltip = 'Display swing order blocks on the chart\n\nNumber of internal swing blocks to display on the chart' orderBlockFilterTooltip = 'Method used to filter out volatile order blocks \n\nIt is recommended to use the cumulative mean range method when a low amount of data is available' orderBlockMitigationTooltip = 'Select what values to use for order block mitigation' showEqualHighsLowsTooltip = 'Display equal highs and equal lows on the chart' equalHighsLowsLengthTooltip = 'Number of bars used to confirm equal highs and equal lows' equalHighsLowsThresholdTooltip = 'Sensitivity threshold in a range (0, 1) used for the detection of equal highs & lows\n\nLower values will return fewer but more pertinent results' showFairValueGapsTooltip = 'Display fair values gaps on the chart' fairValueGapsThresholdTooltip = 'Filter out non significant fair value gaps' fairValueGapsTimeframeTooltip = 'Fair value gaps timeframe' fairValueGapsExtendTooltip = 'Determine how many bars to extend the Fair Value Gap boxes on chart' showPremiumDiscountZonesTooltip = 'Display premium, discount, and equilibrium zones on chart' modeInput = input.string( HISTORICAL, 'Mode', group = SMART_GROUP, tooltip = modeTooltip, options = [HISTORICAL, PRESENT]) styleInput = input.string( COLORED, 'Style', group = SMART_GROUP, tooltip = styleTooltip,options = [COLORED, MONOCHROME]) showTrendInput = input( false, 'Color Candles', group = SMART_GROUP, tooltip = showTrendTooltip) showInternalsInput = input( true, 'Show Internal Structure', group = INTERNAL_GROUP, tooltip = showInternalsTooltip) showInternalBullInput = input.string( ALL, 'Bullish Structure', group = INTERNAL_GROUP, inline = 'ibull', options = [ALL,BOS,CHOCH]) internalBullColorInput = input( GREEN, '', group = INTERNAL_GROUP, inline = 'ibull') showInternalBearInput = input.string( ALL, 'Bearish Structure' , group = INTERNAL_GROUP, inline = 'ibear', options = [ALL,BOS,CHOCH]) internalBearColorInput = input( RED, '', group = INTERNAL_GROUP, inline = 'ibear') internalFilterConfluenceInput = input( false, 'Confluence Filter', group = INTERNAL_GROUP, tooltip = internalFilterConfluenceTooltip) internalStructureSize = input.string( TINY, 'Internal Label Size', group = INTERNAL_GROUP, options = [TINY,SMALL,NORMAL]) showStructureInput = input( true, 'Show Swing Structure', group = SWING_GROUP, tooltip = showStructureTooltip) showSwingBullInput = input.string( ALL, 'Bullish Structure', group = SWING_GROUP, inline = 'bull', options = [ALL,BOS,CHOCH]) swingBullColorInput = input( GREEN, '', group = SWING_GROUP, inline = 'bull') showSwingBearInput = input.string( ALL, 'Bearish Structure', group = SWING_GROUP, inline = 'bear', options = [ALL,BOS,CHOCH]) swingBearColorInput = input( RED, '', group = SWING_GROUP, inline = 'bear') swingStructureSize = input.string( SMALL, 'Swing Label Size', group = SWING_GROUP, options = [TINY,SMALL,NORMAL]) showSwingsInput = input( false, 'Show Swings Points', group = SWING_GROUP, tooltip = showSwingsTooltip,inline = 'swings') swingsLengthInput = input.int( 50, '', group = SWING_GROUP, minval = 10, inline = 'swings') showHighLowSwingsInput = input( true, 'Show Strong/Weak High/Low',group = SWING_GROUP, tooltip = showHighLowSwingsTooltip) showInternalOrderBlocksInput = input( true, 'Internal Order Blocks' , group = BLOCKS_GROUP, tooltip = showInternalOrderBlocksTooltip, inline = 'iob') internalOrderBlocksSizeInput = input.int( 5, '', group = BLOCKS_GROUP, minval = 1, maxval = 20, inline = 'iob') showSwingOrderBlocksInput = input( false, 'Swing Order Blocks', group = BLOCKS_GROUP, tooltip = showSwingOrderBlocksTooltip, inline = 'ob') swingOrderBlocksSizeInput = input.int( 5, '', group = BLOCKS_GROUP, minval = 1, maxval = 20, inline = 'ob') orderBlockFilterInput = input.string( 'Atr', 'Order Block Filter', group = BLOCKS_GROUP, tooltip = orderBlockFilterTooltip, options = [ATR, RANGE]) orderBlockMitigationInput = input.string( HIGHLOW, 'Order Block Mitigation', group = BLOCKS_GROUP, tooltip = orderBlockMitigationTooltip, options = [CLOSE,HIGHLOW]) internalBullishOrderBlockColor = input.color(color.new(#3179f5, 80), 'Internal Bullish OB', group = BLOCKS_GROUP) internalBearishOrderBlockColor = input.color(color.new(#f77c80, 80), 'Internal Bearish OB', group = BLOCKS_GROUP) swingBullishOrderBlockColor = input.color(color.new(#1848cc, 80), 'Bullish OB', group = BLOCKS_GROUP) swingBearishOrderBlockColor = input.color(color.new(#b22833, 80), 'Bearish OB', group = BLOCKS_GROUP) showEqualHighsLowsInput = input( true, 'Equal High/Low', group = EQUAL_GROUP, tooltip = showEqualHighsLowsTooltip) equalHighsLowsLengthInput = input.int( 3, 'Bars Confirmation', group = EQUAL_GROUP, tooltip = equalHighsLowsLengthTooltip, minval = 1) equalHighsLowsThresholdInput = input.float( 0.1, 'Threshold', group = EQUAL_GROUP, tooltip = equalHighsLowsThresholdTooltip, minval = 0, maxval = 0.5, step = 0.1) equalHighsLowsSizeInput = input.string( TINY, 'Label Size', group = EQUAL_GROUP, options = [TINY,SMALL,NORMAL]) showFairValueGapsInput = input( false, 'Fair Value Gaps', group = GAPS_GROUP, tooltip = showFairValueGapsTooltip) fairValueGapsThresholdInput = input( true, 'Auto Threshold', group = GAPS_GROUP, tooltip = fairValueGapsThresholdTooltip) fairValueGapsTimeframeInput = input.timeframe('', 'Timeframe', group = GAPS_GROUP, tooltip = fairValueGapsTimeframeTooltip) fairValueGapsBullColorInput = input.color(color.new(#00ff68, 70), 'Bullish FVG' , group = GAPS_GROUP) fairValueGapsBearColorInput = input.color(color.new(#ff0008, 70), 'Bearish FVG' , group = GAPS_GROUP) fairValueGapsExtendInput = input.int( 1, 'Extend FVG', group = GAPS_GROUP, tooltip = fairValueGapsExtendTooltip, minval = 0) showDailyLevelsInput = input( false, 'Daily', group = LEVELS_GROUP, inline = 'daily') dailyLevelsStyleInput = input.string( SOLID, '', group = LEVELS_GROUP, inline = 'daily', options = [SOLID,DASHED,DOTTED]) dailyLevelsColorInput = input( BLUE, '', group = LEVELS_GROUP, inline = 'daily') showWeeklyLevelsInput = input( false, 'Weekly', group = LEVELS_GROUP, inline = 'weekly') weeklyLevelsStyleInput = input.string( SOLID, '', group = LEVELS_GROUP, inline = 'weekly', options = [SOLID,DASHED,DOTTED]) weeklyLevelsColorInput = input( BLUE, '', group = LEVELS_GROUP, inline = 'weekly') showMonthlyLevelsInput = input( false, 'Monthly', group = LEVELS_GROUP, inline = 'monthly') monthlyLevelsStyleInput = input.string( SOLID, '', group = LEVELS_GROUP, inline = 'monthly', options = [SOLID,DASHED,DOTTED]) monthlyLevelsColorInput = input( BLUE, '', group = LEVELS_GROUP, inline = 'monthly') showPremiumDiscountZonesInput = input( false, 'Premium/Discount Zones', group = ZONES_GROUP , tooltip = showPremiumDiscountZonesTooltip) premiumZoneColorInput = input.color( RED, 'Premium Zone', group = ZONES_GROUP) equilibriumZoneColorInput = input.color( GRAY, 'Equilibrium Zone', group = ZONES_GROUP) discountZoneColorInput = input.color( GREEN, 'Discount Zone', group = ZONES_GROUP) //---------------------------------------------------------------------------------------------------------------------} //DATA STRUCTURES & VARIABLES //---------------------------------------------------------------------------------------------------------------------{ // @type UDT representing alerts as bool fields // @field internalBullishBOS internal structure custom alert // @field internalBearishBOS internal structure custom alert // @field internalBullishCHoCH internal structure custom alert // @field internalBearishCHoCH internal structure custom alert // @field swingBullishBOS swing structure custom alert // @field swingBearishBOS swing structure custom alert // @field swingBullishCHoCH swing structure custom alert // @field swingBearishCHoCH swing structure custom alert // @field internalBullishOrderBlock internal order block custom alert // @field internalBearishOrderBlock internal order block custom alert // @field swingBullishOrderBlock swing order block custom alert // @field swingBearishOrderBlock swing order block custom alert // @field equalHighs equal high low custom alert // @field equalLows equal high low custom alert // @field bullishFairValueGap fair value gap custom alert // @field bearishFairValueGap fair value gap custom alert type alerts bool internalBullishBOS = false bool internalBearishBOS = false bool internalBullishCHoCH = false bool internalBearishCHoCH = false bool swingBullishBOS = false bool swingBearishBOS = false bool swingBullishCHoCH = false bool swingBearishCHoCH = false bool internalBullishOrderBlock = false bool internalBearishOrderBlock = false bool swingBullishOrderBlock = false bool swingBearishOrderBlock = false bool equalHighs = false bool equalLows = false bool bullishFairValueGap = false bool bearishFairValueGap = false // @type UDT representing last swing extremes (top & bottom) // @field top last top swing price // @field bottom last bottom swing price // @field barTime last swing bar time // @field barIndex last swing bar index // @field lastTopTime last top swing time // @field lastBottomTime last bottom swing time type trailingExtremes float top float bottom int barTime int barIndex int lastTopTime int lastBottomTime // @type UDT representing Fair Value Gaps // @field top top price // @field bottom bottom price // @field bias bias (BULLISH or BEARISH) // @field topBox top box // @field bottomBox bottom box type fairValueGap float top float bottom int bias box topBox box bottomBox // @type UDT representing trend bias // @field bias BULLISH or BEARISH type trend int bias // @type UDT representing Equal Highs Lows display // @field l_ine displayed line // @field l_abel displayed label type equalDisplay line l_ine = na label l_abel = na // @type UDT representing a pivot point (swing point) // @field currentLevel current price level // @field lastLevel last price level // @field crossed true if price level is crossed // @field barTime bar time // @field barIndex bar index type pivot float currentLevel float lastLevel bool crossed int barTime = time int barIndex = bar_index // @type UDT representing an order block // @field barHigh bar high // @field barLow bar low // @field barTime bar time // @field bias BULLISH or BEARISH type orderBlock float barHigh float barLow int barTime int bias // @variable current swing pivot high var pivot swingHigh = pivot.new(na,na,false) // @variable current swing pivot low var pivot swingLow = pivot.new(na,na,false) // @variable current internal pivot high var pivot internalHigh = pivot.new(na,na,false) // @variable current internal pivot low var pivot internalLow = pivot.new(na,na,false) // @variable current equal high pivot var pivot equalHigh = pivot.new(na,na,false) // @variable current equal low pivot var pivot equalLow = pivot.new(na,na,false) // @variable swing trend bias var trend swingTrend = trend.new(0) // @variable internal trend bias var trend internalTrend = trend.new(0) // @variable equal high display var equalDisplay equalHighDisplay = equalDisplay.new() // @variable equal low display var equalDisplay equalLowDisplay = equalDisplay.new() // @variable storage for fairValueGap UDTs var array fairValueGaps = array.new() // @variable storage for parsed highs var array parsedHighs = array.new() // @variable storage for parsed lows var array parsedLows = array.new() // @variable storage for raw highs var array highs = array.new() // @variable storage for raw lows var array lows = array.new() // @variable storage for bar time values var array times = array.new() // @variable last trailing swing high and low var trailingExtremes trailing = trailingExtremes.new() // @variable storage for orderBlock UDTs (swing order blocks) var array swingOrderBlocks = array.new() // @variable storage for orderBlock UDTs (internal order blocks) var array internalOrderBlocks = array.new() // @variable storage for swing order blocks boxes var array swingOrderBlocksBoxes = array.new() // @variable storage for internal order blocks boxes var array internalOrderBlocksBoxes = array.new() // @variable color for swing bullish structures var swingBullishColor = styleInput == MONOCHROME ? MONO_BULLISH : swingBullColorInput // @variable color for swing bearish structures var swingBearishColor = styleInput == MONOCHROME ? MONO_BEARISH : swingBearColorInput // @variable color for bullish fair value gaps var fairValueGapBullishColor = styleInput == MONOCHROME ? color.new(MONO_BULLISH,70) : fairValueGapsBullColorInput // @variable color for bearish fair value gaps var fairValueGapBearishColor = styleInput == MONOCHROME ? color.new(MONO_BEARISH,70) : fairValueGapsBearColorInput // @variable color for premium zone var premiumZoneColor = styleInput == MONOCHROME ? MONO_BEARISH : premiumZoneColorInput // @variable color for discount zone var discountZoneColor = styleInput == MONOCHROME ? MONO_BULLISH : discountZoneColorInput // @variable bar index on current script iteration varip int currentBarIndex = bar_index // @variable bar index on last script iteration varip int lastBarIndex = bar_index // @variable alerts in current bar alerts currentAlerts = alerts.new() // @variable time at start of chart var initialTime = time // we create the needed boxes for displaying order blocks at the first execution if barstate.isfirst if showSwingOrderBlocksInput for index = 1 to swingOrderBlocksSizeInput swingOrderBlocksBoxes.push(box.new(na,na,na,na,xloc = xloc.bar_time,extend = extend.right)) if showInternalOrderBlocksInput for index = 1 to internalOrderBlocksSizeInput internalOrderBlocksBoxes.push(box.new(na,na,na,na,xloc = xloc.bar_time,extend = extend.right)) // @variable source to use in bearish order blocks mitigation bearishOrderBlockMitigationSource = orderBlockMitigationInput == CLOSE ? close : high // @variable source to use in bullish order blocks mitigation bullishOrderBlockMitigationSource = orderBlockMitigationInput == CLOSE ? close : low // @variable default volatility measure atrMeasure = ta.atr(200) // @variable parsed volatility measure by user settings volatilityMeasure = orderBlockFilterInput == ATR ? atrMeasure : ta.cum(ta.tr)/bar_index // @variable true if current bar is a high volatility bar highVolatilityBar = (high - low) >= (2 * volatilityMeasure) // @variable parsed high parsedHigh = highVolatilityBar ? low : high // @variable parsed low parsedLow = highVolatilityBar ? high : low // we store current values into the arrays at each bar parsedHighs.push(parsedHigh) parsedLows.push(parsedLow) highs.push(high) lows.push(low) times.push(time) //---------------------------------------------------------------------------------------------------------------------} //USER-DEFINED FUNCTIONS //---------------------------------------------------------------------------------------------------------------------{ // @function Get the value of the current leg, it can be 0 (bearish) or 1 (bullish) // @returns int leg(int size) => var leg = 0 newLegHigh = high[size] > ta.highest( size) newLegLow = low[size] < ta.lowest( size) if newLegHigh leg := BEARISH_LEG else if newLegLow leg := BULLISH_LEG leg // @function Identify whether the current value is the start of a new leg (swing) // @param leg (int) Current leg value // @returns bool startOfNewLeg(int leg) => ta.change(leg) != 0 // @function Identify whether the current level is the start of a new bearish leg (swing) // @param leg (int) Current leg value // @returns bool startOfBearishLeg(int leg) => ta.change(leg) == -1 // @function Identify whether the current level is the start of a new bullish leg (swing) // @param leg (int) Current leg value // @returns bool startOfBullishLeg(int leg) => ta.change(leg) == +1 // @function create a new label // @param labelTime bar time coordinate // @param labelPrice price coordinate // @param tag text to display // @param labelColor text color // @param labelStyle label style // @returns label ID drawLabel(int labelTime, float labelPrice, string tag, color labelColor, string labelStyle) => var label l_abel = na if modeInput == PRESENT l_abel.delete() l_abel := label.new(chart.point.new(labelTime,na,labelPrice),tag,xloc.bar_time,color=color(na),textcolor=labelColor,style = labelStyle,size = size.small) // @function create a new line and label representing an EQH or EQL // @param p_ivot starting pivot // @param level price level of current pivot // @param size how many bars ago was the current pivot detected // @param equalHigh true for EQH, false for EQL // @returns label ID drawEqualHighLow(pivot p_ivot, float level, int size, bool equalHigh) => equalDisplay e_qualDisplay = equalHigh ? equalHighDisplay : equalLowDisplay string tag = 'EQL' color equalColor = swingBullishColor string labelStyle = label.style_label_up if equalHigh tag := 'EQH' equalColor := swingBearishColor labelStyle := label.style_label_down if modeInput == PRESENT line.delete( e_qualDisplay.l_ine) label.delete( e_qualDisplay.l_abel) e_qualDisplay.l_ine := line.new(chart.point.new(p_ivot.barTime,na,p_ivot.currentLevel), chart.point.new(time[size],na,level), xloc = xloc.bar_time, color = equalColor, style = line.style_dotted) labelPosition = math.round(0.5*(p_ivot.barIndex + bar_index - size)) e_qualDisplay.l_abel := label.new(chart.point.new(na,labelPosition,level), tag, xloc.bar_index, color = color(na), textcolor = equalColor, style = labelStyle, size = equalHighsLowsSizeInput) // @function store current structure and trailing swing points, and also display swing points and equal highs/lows // @param size (int) structure size // @param equalHighLow (bool) true for displaying current highs/lows // @param internal (bool) true for getting internal structures // @returns label ID getCurrentStructure(int size,bool equalHighLow = false, bool internal = false) => currentLeg = leg(size) newPivot = startOfNewLeg(currentLeg) pivotLow = startOfBullishLeg(currentLeg) pivotHigh = startOfBearishLeg(currentLeg) if newPivot if pivotLow pivot p_ivot = equalHighLow ? equalLow : internal ? internalLow : swingLow if equalHighLow and math.abs(p_ivot.currentLevel - low[size]) < equalHighsLowsThresholdInput * atrMeasure drawEqualHighLow(p_ivot, low[size], size, false) p_ivot.lastLevel := p_ivot.currentLevel p_ivot.currentLevel := low[size] p_ivot.crossed := false p_ivot.barTime := time[size] p_ivot.barIndex := bar_index[size] if not equalHighLow and not internal trailing.bottom := p_ivot.currentLevel trailing.barTime := p_ivot.barTime trailing.barIndex := p_ivot.barIndex trailing.lastBottomTime := p_ivot.barTime if showSwingsInput and not internal and not equalHighLow drawLabel(time[size], p_ivot.currentLevel, p_ivot.currentLevel < p_ivot.lastLevel ? 'LL' : 'HL', swingBullishColor, label.style_label_up) else pivot p_ivot = equalHighLow ? equalHigh : internal ? internalHigh : swingHigh if equalHighLow and math.abs(p_ivot.currentLevel - high[size]) < equalHighsLowsThresholdInput * atrMeasure drawEqualHighLow(p_ivot,high[size],size,true) p_ivot.lastLevel := p_ivot.currentLevel p_ivot.currentLevel := high[size] p_ivot.crossed := false p_ivot.barTime := time[size] p_ivot.barIndex := bar_index[size] if not equalHighLow and not internal trailing.top := p_ivot.currentLevel trailing.barTime := p_ivot.barTime trailing.barIndex := p_ivot.barIndex trailing.lastTopTime := p_ivot.barTime if showSwingsInput and not internal and not equalHighLow drawLabel(time[size], p_ivot.currentLevel, p_ivot.currentLevel > p_ivot.lastLevel ? 'HH' : 'LH', swingBearishColor, label.style_label_down) // @function draw line and label representing a structure // @param p_ivot base pivot point // @param tag test to display // @param structureColor base color // @param lineStyle line style // @param labelStyle label style // @param labelSize text size // @returns label ID drawStructure(pivot p_ivot, string tag, color structureColor, string lineStyle, string labelStyle, string labelSize) => var line l_ine = line.new(na,na,na,na,xloc = xloc.bar_time) var label l_abel = label.new(na,na) if modeInput == PRESENT l_ine.delete() l_abel.delete() l_ine := line.new(chart.point.new(p_ivot.barTime,na,p_ivot.currentLevel), chart.point.new(time,na,p_ivot.currentLevel), xloc.bar_time, color=structureColor, style=lineStyle) l_abel := label.new(chart.point.new(na,math.round(0.5*(p_ivot.barIndex+bar_index)),p_ivot.currentLevel), tag, xloc.bar_index, color=color(na), textcolor=structureColor, style=labelStyle, size = labelSize) // @function delete order blocks // @param internal true for internal order blocks // @returns orderBlock ID deleteOrderBlocks(bool internal = false) => array orderBlocks = internal ? internalOrderBlocks : swingOrderBlocks for [index,eachOrderBlock] in orderBlocks bool crossedOderBlock = false if bearishOrderBlockMitigationSource > eachOrderBlock.barHigh and eachOrderBlock.bias == BEARISH crossedOderBlock := true if internal currentAlerts.internalBearishOrderBlock := true else currentAlerts.swingBearishOrderBlock := true else if bullishOrderBlockMitigationSource < eachOrderBlock.barLow and eachOrderBlock.bias == BULLISH crossedOderBlock := true if internal currentAlerts.internalBullishOrderBlock := true else currentAlerts.swingBullishOrderBlock := true if crossedOderBlock orderBlocks.remove(index) // @function fetch and store order blocks // @param p_ivot base pivot point // @param internal true for internal order blocks // @param bias BULLISH or BEARISH // @returns void storeOrdeBlock(pivot p_ivot,bool internal = false,int bias) => if (not internal and showSwingOrderBlocksInput) or (internal and showInternalOrderBlocksInput) array a_rray = na int parsedIndex = na if bias == BEARISH a_rray := parsedHighs.slice(p_ivot.barIndex,bar_index) parsedIndex := p_ivot.barIndex + a_rray.indexof(a_rray.max()) else a_rray := parsedLows.slice(p_ivot.barIndex,bar_index) parsedIndex := p_ivot.barIndex + a_rray.indexof(a_rray.min()) orderBlock o_rderBlock = orderBlock.new(parsedHighs.get(parsedIndex), parsedLows.get(parsedIndex), times.get(parsedIndex),bias) array orderBlocks = internal ? internalOrderBlocks : swingOrderBlocks if orderBlocks.size() >= 100 orderBlocks.pop() orderBlocks.unshift(o_rderBlock) // @function draw order blocks as boxes // @param internal true for internal order blocks // @returns void drawOrderBlocks(bool internal = false) => array orderBlocks = internal ? internalOrderBlocks : swingOrderBlocks orderBlocksSize = orderBlocks.size() if orderBlocksSize > 0 maxOrderBlocks = internal ? internalOrderBlocksSizeInput : swingOrderBlocksSizeInput array parsedOrdeBlocks = orderBlocks.slice(0, math.min(maxOrderBlocks,orderBlocksSize)) array b_oxes = internal ? internalOrderBlocksBoxes : swingOrderBlocksBoxes for [index,eachOrderBlock] in parsedOrdeBlocks orderBlockColor = styleInput == MONOCHROME ? (eachOrderBlock.bias == BEARISH ? color.new(MONO_BEARISH,80) : color.new(MONO_BULLISH,80)) : internal ? (eachOrderBlock.bias == BEARISH ? internalBearishOrderBlockColor : internalBullishOrderBlockColor) : (eachOrderBlock.bias == BEARISH ? swingBearishOrderBlockColor : swingBullishOrderBlockColor) box b_ox = b_oxes.get(index) b_ox.set_top_left_point( chart.point.new(eachOrderBlock.barTime,na,eachOrderBlock.barHigh)) b_ox.set_bottom_right_point(chart.point.new(last_bar_time,na,eachOrderBlock.barLow)) b_ox.set_border_color( internal ? na : orderBlockColor) b_ox.set_bgcolor( orderBlockColor) // @function detect and draw structures, also detect and store order blocks // @param internal true for internal structures or order blocks // @returns void displayStructure(bool internal = false) => var bullishBar = true var bearishBar = true if internalFilterConfluenceInput bullishBar := high - math.max(close, open) > math.min(close, open - low) bearishBar := high - math.max(close, open) < math.min(close, open - low) pivot p_ivot = internal ? internalHigh : swingHigh trend t_rend = internal ? internalTrend : swingTrend lineStyle = internal ? line.style_dashed : line.style_solid labelSize = internal ? internalStructureSize : swingStructureSize extraCondition = internal ? internalHigh.currentLevel != swingHigh.currentLevel and bullishBar : true bullishColor = styleInput == MONOCHROME ? MONO_BULLISH : internal ? internalBullColorInput : swingBullColorInput if ta.crossover(close,p_ivot.currentLevel) and not p_ivot.crossed and extraCondition string tag = t_rend.bias == BEARISH ? CHOCH : BOS if internal currentAlerts.internalBullishCHoCH := tag == CHOCH currentAlerts.internalBullishBOS := tag == BOS else currentAlerts.swingBullishCHoCH := tag == CHOCH currentAlerts.swingBullishBOS := tag == BOS p_ivot.crossed := true t_rend.bias := BULLISH displayCondition = internal ? showInternalsInput and (showInternalBullInput == ALL or (showInternalBullInput == BOS and tag != CHOCH) or (showInternalBullInput == CHOCH and tag == CHOCH)) : showStructureInput and (showSwingBullInput == ALL or (showSwingBullInput == BOS and tag != CHOCH) or (showSwingBullInput == CHOCH and tag == CHOCH)) if displayCondition drawStructure(p_ivot,tag,bullishColor,lineStyle,label.style_label_down,labelSize) if (internal and showInternalOrderBlocksInput) or (not internal and showSwingOrderBlocksInput) storeOrdeBlock(p_ivot,internal,BULLISH) p_ivot := internal ? internalLow : swingLow extraCondition := internal ? internalLow.currentLevel != swingLow.currentLevel and bearishBar : true bearishColor = styleInput == MONOCHROME ? MONO_BEARISH : internal ? internalBearColorInput : swingBearColorInput if ta.crossunder(close,p_ivot.currentLevel) and not p_ivot.crossed and extraCondition string tag = t_rend.bias == BULLISH ? CHOCH : BOS if internal currentAlerts.internalBearishCHoCH := tag == CHOCH currentAlerts.internalBearishBOS := tag == BOS else currentAlerts.swingBearishCHoCH := tag == CHOCH currentAlerts.swingBearishBOS := tag == BOS p_ivot.crossed := true t_rend.bias := BEARISH displayCondition = internal ? showInternalsInput and (showInternalBearInput == ALL or (showInternalBearInput == BOS and tag != CHOCH) or (showInternalBearInput == CHOCH and tag == CHOCH)) : showStructureInput and (showSwingBearInput == ALL or (showSwingBearInput == BOS and tag != CHOCH) or (showSwingBearInput == CHOCH and tag == CHOCH)) if displayCondition drawStructure(p_ivot,tag,bearishColor,lineStyle,label.style_label_up,labelSize) if (internal and showInternalOrderBlocksInput) or (not internal and showSwingOrderBlocksInput) storeOrdeBlock(p_ivot,internal,BEARISH) // @function draw one fair value gap box (each fair value gap has two boxes) // @param leftTime left time coordinate // @param rightTime right time coordinate // @param topPrice top price level // @param bottomPrice bottom price level // @param boxColor box color // @returns box ID fairValueGapBox(leftTime,rightTime,topPrice,bottomPrice,boxColor) => box.new(chart.point.new(leftTime,na,topPrice),chart.point.new(rightTime + fairValueGapsExtendInput * (time-time[1]),na,bottomPrice), xloc=xloc.bar_time, border_color = boxColor, bgcolor = boxColor) // @function delete fair value gaps // @returns fairValueGap ID deleteFairValueGaps() => for [index,eachFairValueGap] in fairValueGaps if (low < eachFairValueGap.bottom and eachFairValueGap.bias == BULLISH) or (high > eachFairValueGap.top and eachFairValueGap.bias == BEARISH) eachFairValueGap.topBox.delete() eachFairValueGap.bottomBox.delete() fairValueGaps.remove(index) // @function draw fair value gaps // @returns fairValueGap ID drawFairValueGaps() => [lastClose, lastOpen, lastTime, currentHigh, currentLow, currentTime, last2High, last2Low] = request.security(syminfo.tickerid, fairValueGapsTimeframeInput, [close[1], open[1], time[1], high[0], low[0], time[0], high[2], low[2]],lookahead = barmerge.lookahead_on) barDeltaPercent = (lastClose - lastOpen) / (lastOpen * 100) newTimeframe = timeframe.change(fairValueGapsTimeframeInput) threshold = fairValueGapsThresholdInput ? ta.cum(math.abs(newTimeframe ? barDeltaPercent : 0)) / bar_index * 2 : 0 bullishFairValueGap = currentLow > last2High and lastClose > last2High and barDeltaPercent > threshold and newTimeframe bearishFairValueGap = currentHigh < last2Low and lastClose < last2Low and -barDeltaPercent > threshold and newTimeframe if bullishFairValueGap currentAlerts.bullishFairValueGap := true fairValueGaps.unshift(fairValueGap.new(currentLow,last2High,BULLISH,fairValueGapBox(lastTime,currentTime,currentLow,math.avg(currentLow,last2High),fairValueGapBullishColor),fairValueGapBox(lastTime,currentTime,math.avg(currentLow,last2High),last2High,fairValueGapBullishColor))) if bearishFairValueGap currentAlerts.bearishFairValueGap := true fairValueGaps.unshift(fairValueGap.new(currentHigh,last2Low,BEARISH,fairValueGapBox(lastTime,currentTime,currentHigh,math.avg(currentHigh,last2Low),fairValueGapBearishColor),fairValueGapBox(lastTime,currentTime,math.avg(currentHigh,last2Low),last2Low,fairValueGapBearishColor))) // @function get line style from string // @param style line style // @returns string getStyle(string style) => switch style SOLID => line.style_solid DASHED => line.style_dashed DOTTED => line.style_dotted // @function draw MultiTimeFrame levels // @param timeframe base timeframe // @param sameTimeframe true if chart timeframe is same as base timeframe // @param style line style // @param levelColor line and text color // @returns void drawLevels(string timeframe, bool sameTimeframe, string style, color levelColor) => [topLevel, bottomLevel, leftTime, rightTime] = request.security(syminfo.tickerid, timeframe, [high[1], low[1], time[1], time],lookahead = barmerge.lookahead_on) float parsedTop = sameTimeframe ? high : topLevel float parsedBottom = sameTimeframe ? low : bottomLevel int parsedLeftTime = sameTimeframe ? time : leftTime int parsedRightTime = sameTimeframe ? time : rightTime int parsedTopTime = time int parsedBottomTime = time if not sameTimeframe int leftIndex = times.binary_search_rightmost(parsedLeftTime) int rightIndex = times.binary_search_rightmost(parsedRightTime) array timeArray = times.slice(leftIndex,rightIndex) array topArray = highs.slice(leftIndex,rightIndex) array bottomArray = lows.slice(leftIndex,rightIndex) parsedTopTime := timeArray.size() > 0 ? timeArray.get(topArray.indexof(topArray.max())) : initialTime parsedBottomTime := timeArray.size() > 0 ? timeArray.get(bottomArray.indexof(bottomArray.min())) : initialTime var line topLine = line.new(na, na, na, na, xloc = xloc.bar_time, color = levelColor, style = getStyle(style)) var line bottomLine = line.new(na, na, na, na, xloc = xloc.bar_time, color = levelColor, style = getStyle(style)) var label topLabel = label.new(na, na, xloc = xloc.bar_time, text = str.format('P{0}H',timeframe), color=color(na), textcolor = levelColor, size = size.small, style = label.style_label_left) var label bottomLabel = label.new(na, na, xloc = xloc.bar_time, text = str.format('P{0}L',timeframe), color=color(na), textcolor = levelColor, size = size.small, style = label.style_label_left) topLine.set_first_point( chart.point.new(parsedTopTime,na,parsedTop)) topLine.set_second_point( chart.point.new(last_bar_time + 20 * (time-time[1]),na,parsedTop)) topLabel.set_point( chart.point.new(last_bar_time + 20 * (time-time[1]),na,parsedTop)) bottomLine.set_first_point( chart.point.new(parsedBottomTime,na,parsedBottom)) bottomLine.set_second_point(chart.point.new(last_bar_time + 20 * (time-time[1]),na,parsedBottom)) bottomLabel.set_point( chart.point.new(last_bar_time + 20 * (time-time[1]),na,parsedBottom)) // @function true if chart timeframe is higher than provided timeframe // @param timeframe timeframe to check // @returns bool higherTimeframe(string timeframe) => timeframe.in_seconds() > timeframe.in_seconds(timeframe) // @function update trailing swing points // @returns int updateTrailingExtremes() => trailing.top := math.max(high,trailing.top) trailing.lastTopTime := trailing.top == high ? time : trailing.lastTopTime trailing.bottom := math.min(low,trailing.bottom) trailing.lastBottomTime := trailing.bottom == low ? time : trailing.lastBottomTime // @function draw trailing swing points // @returns void drawHighLowSwings() => var line topLine = line.new(na, na, na, na, color = swingBearishColor, xloc = xloc.bar_time) var line bottomLine = line.new(na, na, na, na, color = swingBullishColor, xloc = xloc.bar_time) var label topLabel = label.new(na, na, color=color(na), textcolor = swingBearishColor, xloc = xloc.bar_time, style = label.style_label_down, size = size.tiny) var label bottomLabel = label.new(na, na, color=color(na), textcolor = swingBullishColor, xloc = xloc.bar_time, style = label.style_label_up, size = size.tiny) rightTimeBar = last_bar_time + 20 * (time - time[1]) topLine.set_first_point( chart.point.new(trailing.lastTopTime, na, trailing.top)) topLine.set_second_point( chart.point.new(rightTimeBar, na, trailing.top)) topLabel.set_point( chart.point.new(rightTimeBar, na, trailing.top)) topLabel.set_text( swingTrend.bias == BEARISH ? 'Strong High' : 'Weak High') bottomLine.set_first_point( chart.point.new(trailing.lastBottomTime, na, trailing.bottom)) bottomLine.set_second_point(chart.point.new(rightTimeBar, na, trailing.bottom)) bottomLabel.set_point( chart.point.new(rightTimeBar, na, trailing.bottom)) bottomLabel.set_text( swingTrend.bias == BULLISH ? 'Strong Low' : 'Weak Low') // @function draw a zone with a label and a box // @param labelLevel price level for label // @param labelIndex bar index for label // @param top top price level for box // @param bottom bottom price level for box // @param tag text to display // @param zoneColor base color // @param style label style // @returns void drawZone(float labelLevel, int labelIndex, float top, float bottom, string tag, color zoneColor, string style) => var label l_abel = label.new(na,na,text = tag, color=color(na),textcolor = zoneColor, style = style, size = size.small) var box b_ox = box.new(na,na,na,na,bgcolor = color.new(zoneColor,80),border_color = color(na), xloc = xloc.bar_time) b_ox.set_top_left_point( chart.point.new(trailing.barTime,na,top)) b_ox.set_bottom_right_point(chart.point.new(last_bar_time,na,bottom)) l_abel.set_point( chart.point.new(na,labelIndex,labelLevel)) // @function draw premium/discount zones // @returns void drawPremiumDiscountZones() => drawZone(trailing.top, math.round(0.5*(trailing.barIndex + last_bar_index)), trailing.top, 0.95*trailing.top + 0.05*trailing.bottom, 'Premium', premiumZoneColor, label.style_label_down) equilibriumLevel = math.avg(trailing.top, trailing.bottom) drawZone(equilibriumLevel, last_bar_index, 0.525*trailing.top + 0.475*trailing.bottom, 0.525*trailing.bottom + 0.475*trailing.top, 'Equilibrium', equilibriumZoneColorInput, label.style_label_left) drawZone(trailing.bottom, math.round(0.5*(trailing.barIndex + last_bar_index)), 0.95*trailing.bottom + 0.05*trailing.top, trailing.bottom, 'Discount', discountZoneColor, label.style_label_up) //---------------------------------------------------------------------------------------------------------------------} //MUTABLE VARIABLES & EXECUTION //---------------------------------------------------------------------------------------------------------------------{ parsedOpen = showTrendInput ? open : na candleColor = internalTrend.bias == BULLISH ? swingBullishColor : swingBearishColor plotcandle(parsedOpen,high,low,close,color = candleColor, wickcolor = candleColor, bordercolor = candleColor) if showHighLowSwingsInput or showPremiumDiscountZonesInput updateTrailingExtremes() if showHighLowSwingsInput drawHighLowSwings() if showPremiumDiscountZonesInput drawPremiumDiscountZones() if showFairValueGapsInput deleteFairValueGaps() getCurrentStructure(swingsLengthInput,false) getCurrentStructure(5,false,true) if showEqualHighsLowsInput getCurrentStructure(equalHighsLowsLengthInput,true) if showInternalsInput or showInternalOrderBlocksInput or showTrendInput displayStructure(true) if showStructureInput or showSwingOrderBlocksInput or showHighLowSwingsInput displayStructure() if showInternalOrderBlocksInput deleteOrderBlocks(true) if showSwingOrderBlocksInput deleteOrderBlocks() if showFairValueGapsInput drawFairValueGaps() if barstate.islastconfirmedhistory or barstate.islast if showInternalOrderBlocksInput drawOrderBlocks(true) if showSwingOrderBlocksInput drawOrderBlocks() lastBarIndex := currentBarIndex currentBarIndex := bar_index newBar = currentBarIndex != lastBarIndex if barstate.islastconfirmedhistory or (barstate.isrealtime and newBar) if showDailyLevelsInput and not higherTimeframe('D') drawLevels('D',timeframe.isdaily,dailyLevelsStyleInput,dailyLevelsColorInput) if showWeeklyLevelsInput and not higherTimeframe('W') drawLevels('W',timeframe.isweekly,weeklyLevelsStyleInput,weeklyLevelsColorInput) if showMonthlyLevelsInput and not higherTimeframe('M') drawLevels('M',timeframe.ismonthly,monthlyLevelsStyleInput,monthlyLevelsColorInput) //---------------------------------------------------------------------------------------------------------------------} //ALERTS //---------------------------------------------------------------------------------------------------------------------{ alertcondition(currentAlerts.internalBullishBOS, 'Internal Bullish BOS', 'Internal Bullish BOS formed') alertcondition(currentAlerts.internalBullishCHoCH, 'Internal Bullish CHoCH', 'Internal Bullish CHoCH formed') alertcondition(currentAlerts.internalBearishBOS, 'Internal Bearish BOS', 'Internal Bearish BOS formed') alertcondition(currentAlerts.internalBearishCHoCH, 'Internal Bearish CHoCH', 'Internal Bearish CHoCH formed') alertcondition(currentAlerts.swingBullishBOS, 'Bullish BOS', 'Internal Bullish BOS formed') alertcondition(currentAlerts.swingBullishCHoCH, 'Bullish CHoCH', 'Internal Bullish CHoCH formed') alertcondition(currentAlerts.swingBearishBOS, 'Bearish BOS', 'Bearish BOS formed') alertcondition(currentAlerts.swingBearishCHoCH, 'Bearish CHoCH', 'Bearish CHoCH formed') alertcondition(currentAlerts.internalBullishOrderBlock, 'Bullish Internal OB Breakout', 'Price broke bullish internal OB') alertcondition(currentAlerts.internalBearishOrderBlock, 'Bearish Internal OB Breakout', 'Price broke bearish internal OB') alertcondition(currentAlerts.swingBullishOrderBlock, 'Bullish Swing OB Breakout', 'Price broke bullish swing OB') alertcondition(currentAlerts.swingBearishOrderBlock, 'Bearish Swing OB Breakout', 'Price broke bearish swing OB') alertcondition(currentAlerts.equalHighs, 'Equal Highs', 'Equal highs detected') alertcondition(currentAlerts.equalLows, 'Equal Lows', 'Equal lows detected') alertcondition(currentAlerts.bullishFairValueGap, 'Bullish FVG', 'Bullish FVG formed') alertcondition(currentAlerts.bearishFairValueGap, 'Bearish FVG', 'Bearish FVG formed') //---------------------------------------------------------------------------------------------------------------------}