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.
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.
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
- For each candidate A–B, try EVERY remaining pivot as point C:
- 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
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() // just small peak and troughs
var longPivots = array.new() // major peak and troughs
var longPeaks = array.new() // major peaks
var longThroughs = array.new() // major troughs
var highestPeaks = array.new() // L highest long peaks
var lowestTroughs = array.new() // L lowest long troughs
var channelCandidates = array.new()
// === 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 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

Volume HeatMap Divergence Indicator – Strategy
Draws volume as a percentile-colored “heatmap” directly on the price chart, while detecting divergence between VOLUME peaks (not an oscillator) and the corresponding price extremes at the same location.

Multi Pivot Trend Indicator – Strategy
An indicator that runs 10 independent breakout detectors in parallel using 10 different pivot lengths, averages them into a single continuous “trend consensus” score from -1 to +1, then colors the entire candle in a gradient matching that exact score.

Directional Imbalance Index Indicator – Strategy
Counts how many times price made a rolling-window highest high / lowest low over a recent lookback period, then represents the balance between the two sides as a bull/bear count and a dominance %.


