Ghost Tangent Crossings
Updated
March 26, 2024
TradingView

For free use on the TradingView platform

Ghost Tangent Crossings (ChartPrime) is a revolutionary way to visualize pivot points and zig-zag patterns that utilizes ellipses. This indicator makes sure that each pivot is plotted from high to low, ensuring a correct zig-zag wave pattern. Before a zig-zag is confirmed Ghost Tangent Crossings (ChartPrime) plots an estimate of the next valid move allowing you to plan well ahead of time. Once it is confirmed, the indicator will fill in the plot with a solid color and print a break label.

Unlike other zig-zag or pivot point indicators, Ghost Tangent Crossings (ChartPrime) only has a pivot lookforward input. This is because the lookback is automatically adjusted based on the last known zig-zag. This allows the indicator to dynamically look for the most recent valid market movement. The equipoint is calculated as the point along the ellipse with an equal change in price on either side. From this point we plot a line with the slope at that location and when the price breaks this level a break label is plotted. Alternatively you can plot this point as a horizontal line. This area works as support and resistance for the market as its the point where the balance in movement is found. We feel that this is a simple and elegant solution to connected zig-zag patterns that utilizes a novel method of visualization that many traders will find useful. With its simple controls and intuitive style, we believe that Ghost Tangent Crossings (ChartPrime) will find a home on most traders charts.


// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ChartPrime

//@version=5
indicator("Ghost Tangent Crossings [ChartPrime]", overlay = true, max_lines_count = 100, max_polylines_count = 100, max_labels_count = 500, max_bars_back = 1000)

type pivot
    float current = na
    int current_idx = na
    float previous = na
    int previous_idx = na

pivothigh(float source, int back, int forward)=>
    var pivot pivot_high = pivot.new(na, na, na, na)
    bool ph = not na(ta.pivothigh(source, back, forward))
    if ph
        pivot_high := pivot.new(source[forward], bar_index - forward, pivot_high.current, pivot_high.current_idx)
    [pivot_high, ph]

pivotlow(float source, int back, int forward)=>
    var pivot pivot_low = pivot.new(na, na, na, na)
    bool pl = not na(ta.pivotlow(source, back, forward))
    if pl
        pivot_low := pivot.new(source[forward], bar_index - forward, pivot_low.current, pivot_low.current_idx)
    [pivot_low, pl]

generate_ellipse(int start_x, int end_x, float start_y, float end_y)=>
    chart.point[] points = array.new()
    float a = end_x - start_x
    float b = end_y - start_y

    int x = na

    if a > 1
        for i = 0 to 90
            int new_x = int(a * math.cos(math.toradians(i)))
            float y = b * math.sin(math.toradians(i))
            if x != new_x
                points.push(chart.point.new(na, start_x + x, start_y + y))
            x := new_x

        points.push(chart.point.new(na, start_x, end_y))

    else
        points.unshift(chart.point.new(na, end_x, start_y))
        points.push(chart.point.new(na, start_x, end_y))

    points

ellipse_slope(int start_x, int end_x, float start_y, float end_y, chart.point[] ellipse_points)=>
    if ellipse_points.size() > 2
        float[] dy = array.new()
        float[] centers = array.new()
        int[] idx = array.new()
        
        for i = 0 to ellipse_points.size() - 2
            float delta = math.abs(ellipse_points.get(i + 1).price - ellipse_points.get(i).price)
            dy.push(delta)

        for i = 1 to dy.size() - 1
            idx.push(i)
            float left = dy.copy().slice(0, i + 1).sum()
            float right = dy.copy().slice(i - 1, dy.size()).sum()
            centers.push(math.abs(left - right))

        int mid = idx.get(centers.indexof(centers.min()))
        chart.point tangent = ellipse_points.get(mid)

        float a = end_x - start_x
        float b = end_y - start_y
        float x = tangent.index - start_x
        float y =  tangent.price - start_y
        float slope = -(math.pow(b, 2) * x) / (math.pow(a, 2) * y)

        [tangent, slope]

    else
        [ellipse_points.first(), -(ellipse_points.last().price - ellipse_points.first().price)]

check_b(int start_x, float start_y, int forward_length, float slope, bool polarity)=>
    int i = forward_length - 1
    bool found = false

    int start_idx = bar_index - start_x
    int current_idx = start_idx
    float end_price = na

    while not found and current_idx >= 0
        i += 1

        float check_price = start_y + slope * i
        current_idx := start_idx - i

        if current_idx < 0
            break

        if polarity
            end_price := high[current_idx]
            if end_price < check_price
                found := true
        else
            end_price := low[current_idx]
            if end_price > check_price
                found := true
    
    if found
        [found, end_price, bar_index - current_idx]
    else
        [found, float(na), int(na)]

generate_zig_zag(
 int start_x
 , int end_x
 , float start_y
 , float end_y
 , bool show_elliptical_zig
 , string equipoint_style
 , bool show_break
 , bool extend_line
 , color up_color
 , color down_color
 , color text_color
 , label[] zig_zag_points
 , polyline[] zig_zags
 , line[] equipoints
 , label[] break_labels
 , bool polarity
 , bool ghost = false)=>

    color bullish_color = polarity ? up_color : down_color
    color bearish_color = polarity ? down_color : up_color

    chart.point[] ellipse_points = generate_ellipse(start_x, end_x, start_y, end_y)
    [tangent, slope] = ellipse_slope(start_x, end_x, start_y, end_y, ellipse_points)

    int length = end_x - start_x
    int back_length = tangent.index - start_x
    int forward_length = length - back_length

    if show_elliptical_zig
        zig_zag_points.push(label.new(ellipse_points.last(), na, color = bullish_color, style = label.style_circle, size = size.auto))
        zig_zags.push(polyline.new(ellipse_points, false, false, xloc.bar_index, bullish_color, line_width = 2))

    if equipoint_style == "Directional"
        if extend_line
            equipoints.push(line.new(tangent.index, tangent.price, tangent.index + 1, tangent.price + slope, xloc.bar_index, extend.both, bullish_color, line.style_dashed))
        else
            equipoints.push(line.new(tangent.index - back_length, tangent.price - slope * back_length, tangent.index + back_length, tangent.price + slope * back_length, xloc.bar_index, extend.none, bullish_color, line.style_dashed))

    if equipoint_style == "Horizontal"
        if extend_line
            equipoints.push(line.new(tangent.index, tangent.price, tangent.index + 1, tangent.price, xloc.bar_index, extend.right, bullish_color, line.style_dashed))
        else
            equipoints.push(line.new(tangent.index, tangent.price, tangent.index + length * 2, tangent.price, xloc.bar_index, extend.none, bullish_color, line.style_dashed))

    if show_break and not ghost
        [found, found_price, found_idx] = check_b(tangent.index, tangent.price, forward_length, slope, polarity)
        if found
            break_labels.push(label.new(found_idx, found_price, "B",  xloc.bar_index, polarity ? yloc.abovebar : yloc.belowbar, polarity ? down_color :up_color, polarity ? label.style_label_down : label.style_label_up, text_color))
        else
            break_labels.push(label.new(na, na, na))

dump_ghost(polyline[] ghost_zig_zags, label[] ghost_zig_zag_points, line[] ghost_equipoints)=>
    if ghost_zig_zags.size() > 0
        for i = ghost_zig_zags.size() - 1 to 0
            ghost_zig_zags.pop().delete()
    if ghost_zig_zag_points.size() > 0
        for i = ghost_zig_zag_points.size() - 1 to 0
            ghost_zig_zag_points.pop().delete()
    if ghost_equipoints.size() > 0
        for i = ghost_equipoints.size() - 1 to 0
            ghost_equipoints.pop().delete()

const string settings = "Settings"
string pivot_type = input.string("Wick", "Pivot Style", ["Wick", "Body"], group = settings)
int pivot_forward = input.int(25, "Pivot Lookforward", minval = 1, group = settings)
const string visual = "Visuals"
bool show_elliptical_zig = input.bool(true, "Show Elliptical Zig-Zag", group = visual)
bool show_ghosts = input.bool(true, "Show Ghost Elliptical Zig-Zag", group = visual)
bool show_break = input.bool(true, "Show Break", group = visual)
int max_zig = input.int(10, "Max Zig-Zags", minval = 0, maxval = 100, group = visual)
string equipoint_style = input.string("Directional", "Equipoint Style", ["Directional", "Horizontal", "None"], group = visual)
bool extend_line = input.bool(false, "Extend Lines", group = visual)
color up_color = input.color(#1bcf66, "Up Color", group = visual)
color down_color = input.color(#ee2d2d, "Down Color", group = visual)
color ghost_up_color = input.color(#1bcf663a, "Ghost Up Color", group = visual)
color ghost_down_color = input.color(#ee2d2d3a, "Ghost Down Color", group = visual)
color text_color = input.color(#EEEEEE, "Text Color", group = visual)

var polyline[] zig_zags = array.new()
var label[] zig_zag_points = array.new

Get access to our Exclusive
premium indicators