Untitled
//@version=5 indicator("S&D ZONES" , overlay = true , max_labels_count = 500 , max_lines_count = 500 , max_boxes_count = 500 , max_bars_back = 500) max_bars_back(time, 500) //-----------------------------------------------------------------------------{ //Constants //-----------------------------------------------------------------------------{ int DEMAND_ZONE = 1 int SUPPLY_ZONE = 2 int TBD_ZONE = 0 //-----------------------------------------------------------------------------{ //Settings //-----------------------------------------------------------------------------{ // -- Indecision ---- indecision_factor = input.int(2, 'Min. Factor', minval = 1, group = "Indecision") volume_periods = input.int(26, 'Previous Periods', minval = 1, group = "Indecision") volume_chg = input.int(10, 'Volume Change (%)', minval = 1, group = "Indecision") confirm_bars = input.int(2, 'Confirmation Bars', minval = 1, group = 'Indecision') invalidation = input.string('Wick', 'Invalidation Method', options = ['Close', 'Wick'], group = "Indecision") // -- Zones ---- showlast = input.int(10, 'Show Last', minval = 1, group = 'Zones') demand_zone_css = input.color(color.new(#66bb6a, 70), 'Demand', inline = "Zones", group = 'Zones') supply_zone_css = input.color(color.new(#f77c80, 70), 'Supply', inline = "Zones", group = 'Zones') indecision_zone_css = input.color(color.new(#777575, 70), 'Indecision', inline = "Zones",group = 'Zones') text_size = input.string('Small', 'Text Size', options = ['Tiny', 'Small', 'Normal'], group = "Zones") //Global variables //-----------------------------------------------------------------------------{ var zone_boxes = array.new_box(0) var zone_labels = array.new_label(0) type Zone float top float bottom float volume int left int type = 0 color css int reached = 0 int bars_outside = 0 var zones = array.new<Zone>(0) var last_zone_reached = Zone.new() bool demand_zone_confirmed = false bool supply_zone_confirmed = false bool price_enter_supply_zone = false bool price_enter_demand_zone = false var label_size = text_size == 'Tiny' ? size.tiny: text_size == 'Small' ? size.small : size.normal //------------------------------------------------------------------ // FUNCTIONS //----------------------------------------------------------------- get_volume(include_current_bar, bars) => past_vol = array.new_float(0) for i = include_current_bar ? 0: 1 to bars array.push(past_vol, volume[i]) array.avg(past_vol) is_indecision_bar() => //Is an indecision candle? float high_change = 0. float low_change = 0. float body_change = math.abs((close - open) / open) if close > open high_change := math.abs((high - close) / close) low_change := math.abs((low - open) / open) else high_change := math.abs((high - open) / open) low_change := math.abs((low - close) / close) float factor = math.floor(math.max(high_change, low_change) / body_change) factor := na(factor) or factor > 999999 ? 999999 : factor bool add_as_new_zone = false if factor >= indecision_factor add_as_new_zone := true for zone in zones //if the candle its include in some previous candle don't add if (high < zone.top and high > zone.bottom) and (low < zone.top and low > zone.bottom) add_as_new_zone := false break else if zone.type == DEMAND_ZONE and (low < zone.top and low > zone.bottom) and (high >= zone.top) add_as_new_zone := false break else if zone.type == SUPPLY_ZONE and (high < zone.top and high > zone.bottom) and (low <= zone.bottom) add_as_new_zone := false break add_as_new_zone valid_zone(zone, bclose, bhigh, blow) => if zone.type == DEMAND_ZONE demand_bar_price = invalidation == 'Close' ? bclose : blow demand_bar_price >= zone.bottom //and bopen > zone.bottom else if zone.type == SUPPLY_ZONE supply_bar_price = invalidation == 'Close' ? bclose : bhigh supply_bar_price <= zone.top //and bopen < zone.top else true bar_reached_zone(zone, wick) => (wick >= zone.bottom and wick <= zone.top) or (close >= zone.bottom and close <= zone.top) or (open >= zone.bottom and open <= zone.top) is_lateral_movement(zone, candles) => int inside = 0 for i = 1 to candles if (close[i] < zone.top and close[i] > zone.bottom) and (open[i] < zone.top and open[i] > zone.bottom) inside += 1 inside >= candles confirm_zone(zone) => valid_zone = true new_volume = get_volume(true, confirm_bars) zone.volume := math.round((new_volume - zone.volume) / zone.volume * 100, 0) if math.max(open, close) > zone.top zone.type := DEMAND_ZONE zone.css := demand_zone_css else if math.min(open, close) < zone.bottom zone.type := SUPPLY_ZONE zone.css := supply_zone_css if math.abs(zone.volume) >= volume_chg and zone.type != TBD_ZONE and not is_lateral_movement(zone, confirm_bars) for i = 0 to confirm_bars - 1 if not valid_zone(zone, close[i], high[i], low[i]) valid_zone := false break else valid_zone := false valid_zone display_zones(boxes, labels, dzones, show_last, zsize) => for i = 0 to math.min(show_last - 1, zsize - 1) zindex = zsize - (1 + i) get_box = array.get(boxes, i) get_label = array.get(labels, i) zone = array.get(dzones, zindex) box.set_lefttop(get_box, zone.left, zone.top) box.set_rightbottom(get_box, zone.left, zone.bottom) box.set_border_color(get_box, zone.css) box.set_bgcolor(get_box, zone.css) label.set_textcolor(get_label, color.new(zone.css, 0)) label.set_xy(get_label, bar_index + 5, zone.bottom) if zone.type == TBD_ZONE label.set_text(get_label, "") else label.set_text(get_label, "Volume change: " + str.tostring(zone.volume) + "%\nReached: " + str.tostring(zone.reached)) //------------------------------------------------------------------ // CALCULATE ZONES //------------------------------------------------------------------ if barstate.isfirst for i = 0 to showlast - 1 array.push(zone_boxes, box.new(na,na,na,na, xloc = xloc.bar_time , extend = extend.right)) array.push(zone_labels, label.new(na,na,na, size = label_size, style = label.style_none)) if barstate.isconfirmed if is_indecision_bar() period_volume = get_volume(include_current_bar = false, bars = volume_periods) array.push(zones, Zone.new(top = high, bottom = low, volume = period_volume, left = time, css= indecision_zone_css)) for zone in zones index = array.indexof(zones, zone) if zone.type == TBD_ZONE and zone.left == time[confirm_bars] // Confirm zone o delete it if confirm_zone(zone) demand_zone_confirmed := zone.type == DEMAND_ZONE supply_zone_confirmed := zone.type == SUPPLY_ZONE for i = 1 to confirm_bars pindex = index + 1 if pindex < array.size(zones) array.remove(zones, pindex) else array.remove(zones, index) break for zone in zones index = array.indexof(zones, zone) if not valid_zone(zone, close, high, low) if last_zone_reached.left == zone.left last_zone_reached := Zone.new() array.remove(zones, index) else // Check if price reached any zone if zone.type == DEMAND_ZONE and bar_reached_zone(zone, low) zone.reached += 1 price_enter_demand_zone := true if zone.type == SUPPLY_ZONE and bar_reached_zone(zone, high) zone.reached += 1 price_enter_supply_zone := true if price_enter_demand_zone or price_enter_supply_zone zone.bars_outside := 0 last_zone_reached := zone if last_zone_reached.type == DEMAND_ZONE and close > last_zone_reached.top //and open > last_zone_reached.top last_zone_reached.bars_outside += 1 else if last_zone_reached.type == SUPPLY_ZONE and close < last_zone_reached.bottom //and open < last_zone_reached.bottom last_zone_reached.bars_outside += 1 if barstate.islast int zones_size = array.size(zones) if zones_size > 0 display_zones(zone_boxes, zone_labels, zones, showlast, zones_size) //------------
Leave a Comment