Isolated Scroll Container

1. Bitgak Indicator Description (Oblique Angle)

A strategy to detect reversals by drawing parallel lines through key highs/lows.

1.1 Indicator Concept

Bitgak (번각 — Korean for “Oblique Angle”) is a strategy widely used by professional Korean traders, sometimes even preferred over Fibonacci Retracement.

Core principle: When you connect two or more reversal points (pivot points) on a chart and create equally spaced parallel lines, those parallels have a high probability of touching other future reversals. It’s as if the market follows a “hidden geometric structure”—when price touches these lines, it tends to reverse or react strongly.

Why does it work? Traders believe price doesn’t move randomly but tends to respect support/resistance levels that share the same “tilt” (slope). When many past highs/lows align on the same system of parallel lines, the remaining lines in that system become “magnet” prices—levels where price is likely to react.

💡 Real-world example: If you draw a line connecting two swing lows and then create equally spaced parallels from that base line, you’ll often see past swing highs sit right on those parallels. This suggests the market is “dancing” on a tilted grid with rules.

1.2 Indicator Features

This indicator automatically:

  • 🔍 Detects reversal points: Automatically scans and marks all significant peaks and troughs on the chart. Two types:
    • Short-term pivots (green/red): Smaller highs/lows, more sensitive to price swings
    • Long-term pivots (orange): Major highs/lows, marking structural reversals only
  • 🧮 Computes and brute-forces thousands of setups: Uses a brute-force approach to find the MOST OPTIMAL system of parallels—the one with the most pivot “hits.”
  • 📊 Draws the optimal Bitgak line system: After finding the best configuration, the indicator draws:
    • Orange parallel lines from -4.0 to +4.0 with 0.5 spacing
    • Each line is labeled (e.g., -2.0, -1.5, -1.0, 0, 1.0, 1.5, 2.0...) so you know its position in the system
  • 🎯 Highlights pivots that touch a line: Pivots that fall within the allowed tolerance of a Bitgak line are highlighted with an orange circle and labeled with the line index they touch.

🔔 Alerts:

  • Currently the indicator is visual-only on the chart and doesn’t auto-create sound or push alerts. You need to watch when price approaches Bitgak lines.
  • You can set your own Price Alerts at the levels where Bitgak lines pass to be notified when price reaches them.

1.3 How to Use the Indicator

Recommended timeframes:

  • ✅ Best on Daily and Weekly — lower noise, clearer structure
  • ⚠️ Not recommended on very short timeframes (5m, 15m) — too noisy to capture meaningful patterns

How to apply in live trading:

  • 1. Identify potential reversal zones: When price moves toward a Bitgak line (especially -2.0, -1.0, 0, 1.0, 2.0...), expect a high-probability reaction zone (bounce or rejection).
  • 2. Wait for confirmation: Don’t enter immediately on touch. Wait for:
    • A reversal candlestick at the Bitgak line
    • Confluence with other indicators (RSI overbought/oversold, volume spike, etc.)
  • 3. Entries:
    • Long (buy): When price touches a lower Bitgak line (support) and shows bullish reversal signals
    • Short (sell): When price touches an upper Bitgak line (resistance) and shows bearish reversal signals
  • 4. Stop Loss: Place SL beyond the next Bitgak line
    • Example: if you Long at line 0, set SL below -0.5 or -1.0
  • 5. Take Profit: Set TP at the next Bitgak lines
    • Example: if you Long at line 0, TP1 at 1.0, TP2 at 2.0
    • Or trail along Bitgak lines: when price breaks 1.0, move SL up to 0.5
  • 6. Assess line strength: If many past pivots hit a specific line, that line is “stronger,” with higher reaction odds. The indicator already chooses the system with the most hits, but you should still observe which lines were tested most.
💡 Important note: Bitgak is NOT a holy grail with 100% accuracy. It’s a support tool to identify HIGH-PROBABILITY reaction zones. Always combine with risk management and other analyses.

1.4 How the Indicator Works

🔢 Inputs & roles

  • OHLC price data (Open/High/Low/Close): Needed to detect highs/lows of each candle
  • Parameter shortPivotMargin (default = 7): Number of candles before/after to confirm a short-term pivot. Smaller = more sensitive, finds more minor pivots
  • Parameter longPivotMargin (default = 17): Candles required to confirm a long-term (major) pivot. Larger = only large structural reversals
  • Parameter lookback (default = 300): Number of historical candles to analyze. More data = slower but richer
  • Parameter L (default = 1): Count of highest highs and lowest lows to prioritize in scoring. L=1 means the algorithm prioritizes systems that hit 1 highest high and 1 lowest low
  • Parameter hit_epsilon (default = 0.01 = 1%): Allowed tolerance. If a pivot price is within ±1% of a Bitgak line, it counts as a “hit.”

⚙️ Main logic blocks

  • STEP 1 — Detect Pivots (f_detect_pivot):
    • For each candle, check if it’s a high/low by comparing with margin candles to the left and right
    • If high[margin] is greater than all highs within the window → it’s a HIGH pivot
    • If low[margin] is lower than all lows within the window → it’s a LOW pivot
    • Store the pivot in shortPivots or longPivots accordingly
  • STEP 2 — Classify & sort (f_find_lowest_highest_pivots):
    • Split longPivots into longPeaks (highs) and longThroughs (lows)
    • Find the top L highs → store in highestPeaks
    • Find the bottom L lows → store in lowestTroughs
    • Role: These pivots are given higher weight because they are the most significant levels in the analyzed window
  • STEP 3 — Generate channel candidates:
    • Support line: Combine EVERY PAIR of lows in longThroughs to form base line A–B
    • Resistance line: Combine EVERY PAIR of highs in longPeaks to form base line A–B
    • Each pair (A, B) creates a ChannelCandidate
    • Role: This enumerates all possible slopes of the Bitgak system
  • STEP 4 — Find the optimal configuration (f_best_fit):
    • For each candidate A–B, try EVERY remaining pivot as point C:
      • If A–B is support → try highs (peaks) as C
      • If A–B is resistance → try lows (troughs) as C
    • For each C, compute perpendicular distance from C to A–B: distC
    • Try multipliers from 0.5 to 4.0 with 0.5 steps:
      • Assume C sits on line mult (e.g., mult = 2.0)
      • → Unit spacing between parallels = distC / mult
      • → Draw the whole system from -4.0 to +4.0 using that spacing
    • Score by counting how many pivots “hit” the Bitgak lines:
      • Each shortPivot hit: +1 point
      • Each longPivot hit: +2 points (more important)
      • If it hits the highest high or lowest low: +20 points (very important!)
    • Record the (C, multiplier) with the highest score
    • Role: This is the heart of the algorithm—finding the Bitgak system that best “fits” the most pivots in history
  • STEP 5 — Select & draw the best channel (f_draw_best_channel):
    • Compare scores of ALL candidates and pick the highest
    • Recompute slope and unit spacing of the winner
    • Draw parallels from -4.0 to +4.0 (step 0.5) extending from past to future
    • Label each line (e.g., -2.0, -1.5, -1.0, 0, 1.0...)
  • STEP 6 — Mark pivot hits (f_mark_hitting_pivots):
    • Scan all pivots (short and long)
    • For each pivot, check whether it lies within hit_epsilon of any Bitgak line
    • If yes → draw an orange circle at that pivot and print the index of the line it hits
    • Role: Helps traders visually see which pivots “tested” Bitgak lines in the past

📤 Outputs & how to use them

  • Orange Bitgak parallels: The system of lines to watch. When price approaches them in the future, reactions are likely.
  • Numeric labels on each line (e.g., -2.0, 0, 1.5, 3.0): Lets you refer to levels consistently. For example, “price is testing line 2.0” or “SL below -1.0.”
  • Orange circles marking pivots: Show how price reacted to these lines historically. A line with many circles → very strong.
  • Small green/red pivots: (if enabled) Short-term reversals—useful for precise entries
  • Large orange pivots: (if enabled) Long-term reversals—the foundation of the Bitgak system

📘 Example with concrete prices:

Suppose on Bitcoin, Daily timeframe:

  • Inputs: Last 300 candles, shortPivotMargin=7, longPivotMargin=17
  • Step 1 logic: Detects 50 short pivots and 12 long pivots. Among them, peak A ($68,000) and peak B ($72,000) are two long peaks
  • Step 2 logic: Highest high is $72,000 (peak B), lowest low is $52,000 (trough X)
  • Step 3 logic: Create a candidate with A–B connecting $68k and $72k. Slope = ($72k - $68k) / (candle distance) ≈ slightly upward
  • Step 4 logic: Try trough X ($52k) as point C. Distance from X to A–B is $18,000. Try multiplier = 3.0 → unit spacing = $18k / 3.0 = $6,000. Draw the system with $6k spacing. Count 35 pivot hits → Score = 35 + 20 (highest high hit) + 20 (lowest low hit) = 75 points
  • Step 5 logic: After testing all, configuration (A–B–X with multiplier 3.0) scores the highest → Choose it!
  • Outputs: Draw lines: ... line -1.0 at $60k, line 0 at $66k, line 1.0 at $72k, line 2.0 at $78k, line 3.0 at $84k... Price is currently $75k near line 1.5 → you may wait for a reaction there to consider a short if a reversal signal appears
Zoom/Pan Layered Image
Background Overlay
− +

Use the analysis above + the code below to instruct AI to modify the indicator and turn it into a trading bot—no coding required!

How to do it here -> 👉ZERO2HERO👈

				
					// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © osprey0                                                
//              ██████╗ ███████╗██████╗ ██████╗ ███████╗██╗   ██╗
//            ██╔═══██╗██╔════╝██╔══██╗██╔══██╗██╔════╝╚██╗ ██╔╝
//            ██║   ██║███████╗██████╔╝██████╔╝█████╗   ╚████╔╝ 
//            ██║   ██║╚════██║██╔═══╝ ██╔══██╗██╔══╝    ╚██╔╝  
//            ╚██████╔╝███████║██║     ██║  ██║███████╗   ██║   
//             ╚═════╝ ╚══════╝╚═╝     ╚═╝  ╚═╝╚══════╝   ╚═╝                                                                              
//                                                                  ░░          
//       ░▒░░▒░                                                 ░▒▓▒░░░▒▓░      
//       ░█████▓▒░                                            ░▒░   ░▓░░░░      
//         ░▒▓▓▓▓▒         █████▓▒                    ░░▒            ░▓▒░       
//            ░░░           ▓███████              ░▒█▓██▓░░░                    
//                        ░░░ ░▒▓███▓░          ░▒▓███▒  ░░       ░░            
//                           ░▓████▓░▒         ▓▒██▓▒░░░░                       
//                       ░░▒▓██████▒░▒░      ░▓░▓████▒░                         
//                       ░░▓███████▓░░▒      ▓░▓████▓█▒▒░ ░░                    
//                         ░▓██████▓▒ ░░    ░▓▒███████▓▓░░░                     
//                           ███████▓▒ ░   ░█░▒▓███░                            
//                           ▒████▓█▓░  ▒  ▓█░▒█░                               
//                           ░▒█████▓█▒ ▒████▒▒                                 
//                            ░▓▒▒████▒░▒█▒                                     
//                               ░░░▒▓░ ▒▓▓▒▓▒                                  
//                                  ░  ░░▒▒░░▓░                                 
//                                 ░▒░░░░░░ ░                                   
//                              ░░▒▒░░░░▒░ ░▒░░░                                
//                           ░░░░▒▒▒░▓▓▒▒░░░▒▓▓░░                               
//                          ░▒▓▓▒▒▒▒ ▓▓▒▒ ▓░░░                                  
//                             ▒▓▓░▒▓▓░░░░                                      
// to L.L.

//@version=6
indicator("Bitgak [Osprey]", overlay=true, max_labels_count=500)                                                                          

// TYPE DEFINITIONS AND CONSTANTS


// ===== TYPES =====
type Point
    int time
    float price

type Pivot
    float   price
    int     index
    string  kind     // "HIGH" or "LOW"
    float   prominence

type ChannelCandidate
    Pivot   pivotA
    Pivot   pivotB
    string  kind                // "SUPPORT" or "RESISTANCE"
    int     score               // how many pivots this channel hits
    Pivot   pivotC              // This is the top pivot (if A,B are support) or bottom pivot (if A,B are resistance)
    float   pivotC_multiplier   // This is \in [-6.0, -5.5, ..., 0, ..., 5.5, 6.0]. It's the multiplier where that line is the line including pivotC


// ===== INPUTS =====
// Pivot Detection Settings
shortPivotMargin    = input.int(7, "Short-term Pivot Bars", minval=1, maxval=50, tooltip="Number of bars on each side to confirm a short-term pivot. Lower = more sensitive, more pivots detected.", group="Pivot Detection")
longPivotMargin     = input.int(17, "Long-term Pivot Bars", minval=1, maxval=200,tooltip="Number of bars on each side to confirm a major pivot. Higher = only significant peaks/troughs detected.",group="Pivot Detection")

// Bitgak Heuristics
// automatic_lookback  = input.bool(true, "Auto Lookback", tooltip="Automatically use all available chart history. Disable to set custom lookback period.",group="Bitgak Heuristics")
lookback            = input.int(300, "Manual Lookback Bars", minval=100, maxval=5000,tooltip="Only used when Auto Lookback is disabled. Number of historical bars to analyze.",group="Bitgak Heuristics")
L                   = input.int(1, "Top Anchors to Test (L)", minval=1, maxval=5,tooltip="Lines adjust to touch 'L' highest peaks and 'L' lowest peaks. Higher L isn't necessarily better. If L = 1, the line will try to fit the highest and the lowest pivot",group="Bitgak Heuristics")
hit_epsilon         = input.float(0.01, "Hit Tolerance (%)", minval=0.001, maxval=0.1, step=0.001,tooltip="How close a pivot must be to a line to count as a 'hit'. 0.02 = within 2% of price. Lower = stricter.",group="Bitgak Heuristics")

// Channel Line Settings
f_min               = input.float(-4.0, "Min Line Multiplier", minval=-10.0, maxval=0.0, step=0.5,tooltip="Lowest channel line to draw (e.g., -5.0 means 5 lines below base)",group="Channel Lines")
f_max               = input.float(4.0, "Max Line Multiplier", minval=0.0, maxval=10.0, step=0.5,tooltip="Highest channel line to draw (e.g., 5.0 means 5 lines above base)",group="Channel Lines")
f_step              = input.float(0.5, "Line Spacing", minval=0.1, maxval=1.0, step=0.1,tooltip="Distance between lines. 0.5 = draw at 0.5, 1.0, 1.5, etc. Smaller = more lines.",group="Channel Lines")

// Visualization Group
draw_lines          = input.bool(true, "Show Bitgak Lines", tooltip="Display the lines", group="Display Options")
draw_pivots         = input.bool(false, "Show All Pivots", tooltip="Display markers for all detected pivots (can be cluttered on busy charts)",group="Display Options")
draw_touch_pivots   = input.bool(true, "Highlight Touching Pivots",tooltip="Mark pivots that touch any Bitgak channel line with colored circles",group="Display Options")

// ===== DESIGN & PREFS =====
color   ORANGE              = input.color(color.rgb(255, 123, 34), "Main Color", group = "Color Settings")
color   PIVOT_TOUCH_COLOR   = input.color(color.rgb(255, 122, 45), "Major Pivot Touch Color", group = "Color Settings")
color   SUBCOLOR            = color.white
color   HIGH_COLOR          = input.color(color.rgb(76, 175, 80), "Small Peak Pivot", group = "Color Settings")     // Green for peaks
color   LOW_COLOR           = input.color(color.rgb(255, 82, 82), "Small Trough Pivot", group = "Color Settings")   // Red for troughs


// ===== VARIABLES =====

var line_ns = (f_max - f_min) / f_step      // # of bitgak lines

var shortPivots     = array.new<Pivot>()    // just small peak and troughs
var longPivots      = array.new<Pivot>()    // major peak and troughs
var longPeaks       = array.new<Pivot>()    // major peaks
var longThroughs    = array.new<Pivot>()    // major troughs
var highestPeaks    = array.new<Pivot>()    // L highest long peaks
var lowestTroughs   = array.new<Pivot>()    // L lowest long troughs


var channelCandidates = array.new<ChannelCandidate>()


// === FUNCTIONS ===
f_detect_pivot(margin, pivot_arr) =>
    // Only attempt detection once we have enough history
    if bar_index > 2 * margin
        // the candidate pivot is the bar at position `rightBars` (confirmed)
        candIndex = bar_index - margin
        candHigh  = high[margin]
        candLow   = low[margin]

        // check left and right neighbors
        isHigh = true
        isLow  = true

        // check left side
        for i = 1 to margin
            if high[margin + i] >= candHigh
                isHigh := false
            if low[margin + i] <= candLow
                isLow := false

        // check right side
        for i = 1 to margin
            if high[margin - i] >= candHigh
                isHigh := false
            if low[margin - i] <= candLow
                isLow := false

        // when we find a pivot, store values AND create a label exactly once
        if isHigh
            newPivot = Pivot.new(candHigh, candIndex, "HIGH", na)
            array.push(pivot_arr, newPivot)
            if draw_pivots
                if margin == shortPivotMargin
                    label.new(x = newPivot.index,y = newPivot.price,text = "H",style = label.style_circle,textcolor = color.white,color = HIGH_COLOR,size = size.tiny)
                else if margin == longPivotMargin
                    label.new(x = newPivot.index,y = newPivot.price,text = "H",style = label.style_circle,textcolor = color.white,color = ORANGE,size = size.tiny)

        if isLow
            newPivot = Pivot.new(candLow, candIndex, "LOW", na)
            array.push(pivot_arr, newPivot)
            if draw_pivots
                if margin == shortPivotMargin
                    label.new(x = newPivot.index,y = newPivot.price,text = "L",style = label.style_circle,textcolor = color.white,color = LOW_COLOR,size = size.tiny)
                else if margin == longPivotMargin
                    label.new(x = newPivot.index,y = newPivot.price,text = "L",style = label.style_circle,textcolor = color.white,color = ORANGE,size = size.tiny)



// Function to calculate perpendicular distance from point to line
f_perpendicular_distance(pivotE, pivotA, slope, intercept) =>
    // Line equation: y = slope * x + intercept
    // Point: (pivotE.index, pivotE.price)
    // Perpendicular distance: |slope*x - y + intercept| / sqrt(slope^2 + 1)
    numerator = math.abs(slope * pivotE.index - pivotE.price + intercept)
    denominator = math.sqrt(slope * slope + 1)
    numerator / denominator

// Function to calculate vertical distance from a point to line
f_vertical_distance(pivotE, slope, intercept) =>
    verticalDistance = pivotE.price - (pivotE.index * slope + intercept)
    math.abs(verticalDistance)

// Function to calculate line intercept given slope and a point
f_line_intercept(pivot, slope) =>
    // y = slope * x + intercept
    // intercept = y - slope * x
    pivot.price - slope * pivot.index

// Tests if pivotE is within any of the bitgak lines by testing all lines with different multipliers
f_pivot_in_bitgak(pivotE, slope, intercept, unitSpacing) =>
    bool found = false

    // test for all bitgak lines
    for mult = f_min to f_max by f_step
        bitgak_intercept = intercept + unitSpacing * mult
        bitgak_slope = slope

        if f_vertical_distance(pivotE, bitgak_slope, bitgak_intercept) <= pivotE.price * hit_epsilon
            found := true
            break
    found

f_find_lowest_highest_pivots(longPivots, lowestTroughs, highestPeaks) =>
    // Build sorted arrays by inserting pivots in sorted order
    for i = 0 to array.size(longPivots) - 1
        pivot = array.get(longPivots, i)
        
        if pivot.kind == "HIGH"
            // Insert into highestPeaks maintaining descending order
            inserted = false
            
            // Only loop if array is not empty
            if array.size(highestPeaks) > 0
                for j = 0 to array.size(highestPeaks) - 1
                    if pivot.price > array.get(highestPeaks, j).price
                        array.insert(highestPeaks, j, pivot)
                        inserted := true
                        break
            
            // If not inserted and we have room, add to end
            if not inserted and array.size(highestPeaks) < L
                array.push(highestPeaks, pivot)
            
            // Keep only top L
            if array.size(highestPeaks) > L
                array.pop(highestPeaks)
        
        else if pivot.kind == "LOW"
            // Insert into lowestTroughs maintaining ascending order
            inserted = false
            
            // Only loop if array is not empty
            if array.size(lowestTroughs) > 0
                for j = 0 to array.size(lowestTroughs) - 1
                    if pivot.price < array.get(lowestTroughs, j).price
                        array.insert(lowestTroughs, j, pivot)
                        inserted := true
                        break
            
            // If not inserted and we have room, add to end
            if not inserted and array.size(lowestTroughs) < L
                array.push(lowestTroughs, pivot)
            
            // Keep only top L
            if array.size(lowestTroughs) > L
                array.pop(lowestTroughs)

// given candidate, test all the different Bitgak lines with all possible pivotC and record the best result
f_best_fit(ChannelCandidate candidate) =>
    bestScore = 0
    bestPivotC = Pivot.new(na, na, na, na)
    bestMultiplier = 0.0
    
    // Determine which pivot array to use for pivotC based on channel kind
    pivotsToTest = candidate.kind == "SUPPORT" ? longPeaks : longThroughs
    
    // Try each potential pivotC
    for c = 0 to array.size(pivotsToTest) - 1
        testPivotC = array.get(pivotsToTest, c)
        
        // Skip if pivotC is same as pivotA or pivotB
        if testPivotC.index == candidate.pivotA.index or testPivotC.index == candidate.pivotB.index
            continue
        
        // Calculate base slope from pivotA to pivotB
        baseSlope = (candidate.pivotB.price - candidate.pivotA.price) / (candidate.pivotB.index - candidate.pivotA.index)
        baseIntercept = f_line_intercept(candidate.pivotA, baseSlope)
        
        // Calculate vertical distance from pivotC to base line (A-B line)
        distC = f_vertical_distance(testPivotC, baseSlope, baseIntercept)

        // Try different multipliers for pivotC line (0.5, 1.0, 1.5, ..., 5.0)
        for mult = 1.0 to f_max by f_step
            // Calculate what the unit spacing would be if pivotC is on line mult
            unitSpacing = distC / mult
            
            // Now test all hits by iterating over all pivots and checking if any of them are within
            // V_e(line.slope * x + line.intercept) of the line with the given slope and intercept
            totalHits = 0
            
            // score shortPivot hits
            for p = 0 to array.size(shortPivots) - 1
                pivotE = array.get(shortPivots, p)
                if f_pivot_in_bitgak(pivotE, baseSlope, baseIntercept, unitSpacing)
                    totalHits += 1
            
            // score longPivot hits
            for p = 0 to array.size(longPivots) - 1
                pivotE = array.get(longPivots, p)
                if f_pivot_in_bitgak(pivotE, baseSlope, baseIntercept, unitSpacing)
                    totalHits += 2
            
            // some heuristics for better Bitgak lines:
            // 1. if there are more than 2 pivots in one line, it's a strong line.
            // TODO: possibly implement in the next version update

            // 2. if it encompasses the topmost or bottommost pivot, it is good.
            for pivotE in highestPeaks
                
                if f_pivot_in_bitgak(pivotE, baseSlope, baseIntercept, unitSpacing)
                    totalHits += 20

            for pivotE in lowestTroughs
                if f_pivot_in_bitgak(pivotE, baseSlope, baseIntercept, unitSpacing)
                    totalHits += 20
            
            // If this configuration scores better, update best
            if totalHits > bestScore
                bestScore := totalHits
                bestPivotC := testPivotC
                bestMultiplier := mult
    
    
    // Update the candidate with best results
    candidate.score := bestScore
    candidate.pivotC := bestPivotC
    candidate.pivotC_multiplier := bestMultiplier
    
    [bestScore, bestPivotC, bestMultiplier]


f_mark_hitting_pivots(bestCandidate, slope, intercept, unitSpacing) =>
    
    // Check all shortPivots
    for i = 0 to array.size(shortPivots) - 1
        pivot = array.get(shortPivots, i)
        
        // Test if this pivot hits any bitgak line
        for mult = f_min to f_max by f_step
            bitgak_intercept = intercept + unitSpacing * mult
            dist = f_vertical_distance(pivot, slope, bitgak_intercept)
            threshold = pivot.price * hit_epsilon
            
            if dist <= threshold
                // Mark with the multiplier number
                label.new(x = pivot.index,y = pivot.price,text = str.tostring(mult, "#.#"),style = label.style_circle,color = PIVOT_TOUCH_COLOR,textcolor = color.white,size = size.tiny)
                break
    
    // Check all longPivots
    for i = 0 to array.size(longPivots) - 1
        pivot = array.get(longPivots, i)
        
        // Test if this pivot hits any bitgak line
        for mult = f_min to f_max by f_step
            bitgak_intercept = intercept + unitSpacing * mult
            dist = f_vertical_distance(pivot, slope, bitgak_intercept)
            threshold = pivot.price * hit_epsilon
            
            if dist <= threshold
                // Mark with the multiplier number
                label.new(x = pivot.index,y = pivot.price,text = str.tostring(mult, "#.#"),style = label.style_circle,color = PIVOT_TOUCH_COLOR,textcolor = color.white,size = size.tiny)
                break


// Function to draw the best channel - simple version
f_draw_best_channel(array<ChannelCandidate> candidates) =>
    if array.size(candidates) == 0
        na
    else
        // Find best candidate
        bestCandidate = array.get(candidates, 0)
        for i = 1 to array.size(candidates) - 1
            candidate = array.get(candidates, i)
            if candidate.score > bestCandidate.score
                bestCandidate := candidate

        log.info("Best candidate has score of " + str.tostring(bestCandidate.score) + " with A = " + str.tostring(bestCandidate.pivotA.price) + "and B = " + str.tostring(bestCandidate.pivotB.price) + " and C = " + str.tostring(bestCandidate.pivotC.price) + " with multiplier = " + str.tostring(bestCandidate.pivotC_multiplier))
        
        // Calculate geometry
        slope = (bestCandidate.pivotB.price - bestCandidate.pivotA.price) / (bestCandidate.pivotB.index - bestCandidate.pivotA.index)
        intercept = bestCandidate.pivotA.price - slope * bestCandidate.pivotA.index
        
        // Calculate unit spacing
        distC = f_vertical_distance(bestCandidate.pivotC, slope, intercept)
        unitSpacing = distC / bestCandidate.pivotC_multiplier

        // Draw lines
        endBar = bar_index + 50
        startBar = math.min(bestCandidate.pivotA.index, bestCandidate.pivotB.index, bestCandidate.pivotC.index) - 100

        for mult = f_min to f_max by f_step
            y1 = slope * startBar + (intercept + mult * unitSpacing)
            y2 = slope * endBar + (intercept + mult * unitSpacing)
            
            if draw_lines
                line.new(startBar, y1, endBar, y2, color=ORANGE, style = line.style_dashed, width= 1)
            
                // Add label at the end of each line showing the multiplier
                label.new(x = endBar+2, y = y2,text = str.tostring(mult, "#.#"),style = label.style_none,textcolor = ORANGE,size = size.small,textalign = text.align_left)

        // draw marks for pivots that touch any line
        if draw_touch_pivots
            f_mark_hitting_pivots(bestCandidate, slope, intercept, unitSpacing)



// TODO: fix this
// === FIND THE BEST LOOKBACK PERIOD
// if automatic_lookback
//     lookback := 300


// === PIVOT DETECTION ===
// For the lookback period, find the pivots
if bar_index >= last_bar_index - lookback
    f_detect_pivot(shortPivotMargin, shortPivots)
    f_detect_pivot(longPivotMargin, longPivots)

// TODO: possible next update - add the recent pivots that aren't checked because of detection margin.
// if bar_index + lookback < last_bar_index
//     recent_lowest = Pivot.new(na, na, na, na)
//     recent_highest = Pivot.new(na, na, na, na)

//     if barstate.islast
//         array.push(longPivots, recent_lowest)
//         array.push(longPivots, recent_highest)
        
    


// === CORE MECHANISM (ran once in the last bar)
if barstate.islast
    
    // Separate longPivots into Peaks and Troughs
    array.clear(longPeaks)
    array.clear(longThroughs)

    for i = 0 to array.size(longPivots) - 1
        pivot = array.get(longPivots, i)
        
        if pivot.kind == "HIGH"
            array.push(longPeaks, pivot)
        else if pivot.kind == "LOW"
            array.push(longThroughs, pivot)

    // find L highest peaks and lowest troughs
    array.clear(highestPeaks)
    array.clear(lowestTroughs)
    
    f_find_lowest_highest_pivots(longPivots, lowestTroughs, highestPeaks)


    // ===== GENERATE CANDIDATES FOR SUPPORT / RESISTANCE LINE =====
    for i = 0 to array.size(longThroughs) - 1
        pivA = array.get(longThroughs, i)
        for j = 0 to i
            pivB = array.get(longThroughs, j)
            if pivA.index == pivB.index
                continue
            // Create ChannelCandidate for support line
            channelCandidate = ChannelCandidate.new(pivA, pivB, "SUPPORT", 0, na, 0.0)
            array.push(channelCandidates, channelCandidate)
    
    for i = 0 to array.size(longPeaks) - 1
        pivA = array.get(longPeaks, i)
        for j = 0 to i
            pivB = array.get(longPeaks, j)
            if pivA.index == pivB.index
                continue
            // Create ChannelCandidate for support line
            channelCandidate = ChannelCandidate.new(pivA, pivB, "RESISTANCE", 0, na, 0.0)
            array.push(channelCandidates, channelCandidate)
    
    // ===== TEST THE BITGAK LINES =====
    for i = 0 to array.size(channelCandidates) - 1
        candidate = array.get(channelCandidates, i)
        f_best_fit(candidate)
        log.info("PivA: " + str.tostring(candidate.pivotA.price) + "PivB: " + str.tostring(candidate.pivotB.price) + "PivC " + str.tostring(candidate.pivotC.price) + "mult: " + str.tostring(candidate.pivotC_multiplier) + "Score: " + str.tostring(candidate.score))
    
        
    // ===== DRAW THE BEST BIGGAK LINE =====
    f_draw_best_channel(channelCandidates)







				
			

Các chỉ báo khác

VWAP Volume Profile Indicator – Strategy

Một hồ sơ khối lượng hai chiều dựng trên đường VWAP thay vì trên giá: khối lượng của mỗi nến được gắn dấu cộng hoặc trừ tuỳ VWAP đang lên hay xuống, rồi gom lại thành biểu đồ cột nằm ngang chìa ra cả hai phía ở mép phải chart.

Market Shift Levels Indicator – Strategy

Every time the HMA moving average flips its slope, the indicator locks in the high or the low of that exact candle as a “boundary level”, colors every candle relative to it, and flags the moments price pokes through the level and immediately fails.

Dynamic Swing Anchored VWAP Indicator – Strategy

Automatically anchors a VWAP line to the most recent swing reversal point, rebuilds the whole line from that anchor every time market structure flips, and tags each anchor with an HH / HL / LH / LL structure label.