Add all project files
This commit is contained in:
684
auto_trendlines.pine
Normal file
684
auto_trendlines.pine
Normal file
@@ -0,0 +1,684 @@
|
|||||||
|
//@version=5
|
||||||
|
indicator("Auto Trend Lines - Long Period", overlay=true, max_lines_count=500)
|
||||||
|
|
||||||
|
// Input parameters
|
||||||
|
lookback_period = input.int(50, "Lookback Period for Pivot Detection", minval=10, maxval=200)
|
||||||
|
min_touches = input.int(2, "Minimum Touches for Valid Trend Line", minval=2, maxval=5)
|
||||||
|
max_lines = input.int(20, "Maximum Number of Trend Lines", minval=5, maxval=50)
|
||||||
|
max_bars_back = input.int(500, "Maximum Bars Back for Lines", minval=100, maxval=2000)
|
||||||
|
merge_similar_lines = input.bool(true, "Merge Similar Lines")
|
||||||
|
slope_tolerance = input.float(0.1, "Slope Tolerance for Merging (%)", minval=0.01, maxval=1.0)
|
||||||
|
price_tolerance = input.float(0.5, "Price Tolerance for Merging (%)", minval=0.1, maxval=2.0)
|
||||||
|
line_extend = input.string("right", "Line Extension", options=["none", "left", "right", "both"])
|
||||||
|
show_support = input.bool(true, "Show Support Lines")
|
||||||
|
show_resistance = input.bool(true, "Show Resistance Lines")
|
||||||
|
|
||||||
|
// Enhanced K-line features
|
||||||
|
show_close_based_lines = input.bool(true, "Show Close-Based Trend Lines (Thick)")
|
||||||
|
show_wick_based_lines = input.bool(true, "Show Wick-Based Trend Lines (Thin)")
|
||||||
|
show_current_hl = input.bool(true, "Show Current Bar High/Low Points")
|
||||||
|
|
||||||
|
// Volume filtering
|
||||||
|
use_volume_filter = input.bool(true, "Enable Volume Filtering")
|
||||||
|
volume_threshold_multiplier = input.float(1.5, "Volume Threshold Multiplier", minval=0.5, maxval=5.0)
|
||||||
|
volume_lookback = input.int(20, "Volume Average Lookback", minval=5, maxval=100)
|
||||||
|
|
||||||
|
// Line styling
|
||||||
|
support_color = input.color(color.green, "Support Line Color")
|
||||||
|
resistance_color = input.color(color.red, "Resistance Line Color")
|
||||||
|
close_support_color = input.color(color.lime, "Close-Based Support Color")
|
||||||
|
close_resistance_color = input.color(color.maroon, "Close-Based Resistance Color")
|
||||||
|
thin_line_width = input.int(2, "Thin Line Width (Wick-Based)", minval=1, maxval=5)
|
||||||
|
thick_line_width = input.int(5, "Thick Line Width (Close-Based)", minval=3, maxval=15)
|
||||||
|
line_style = input.string("solid", "Line Style", options=["solid", "dashed", "dotted"])
|
||||||
|
current_hl_color = input.color(color.yellow, "Current High/Low Point Color")
|
||||||
|
|
||||||
|
// Convert line style
|
||||||
|
get_line_style() =>
|
||||||
|
switch line_style
|
||||||
|
"solid" => line.style_solid
|
||||||
|
"dashed" => line.style_dashed
|
||||||
|
"dotted" => line.style_dotted
|
||||||
|
=> line.style_solid
|
||||||
|
|
||||||
|
// Pivot detection
|
||||||
|
pivot_high = ta.pivothigh(high, lookback_period, lookback_period)
|
||||||
|
pivot_low = ta.pivotlow(low, lookback_period, lookback_period)
|
||||||
|
|
||||||
|
// Arrays to store pivot points with time
|
||||||
|
var array<float> high_prices = array.new<float>()
|
||||||
|
var array<int> high_bars = array.new<int>()
|
||||||
|
var array<int> high_times = array.new<int>()
|
||||||
|
var array<float> low_prices = array.new<float>()
|
||||||
|
var array<int> low_bars = array.new<int>()
|
||||||
|
var array<int> low_times = array.new<int>()
|
||||||
|
|
||||||
|
// Arrays to store close-based pivot points
|
||||||
|
var array<float> high_close_prices = array.new<float>()
|
||||||
|
var array<int> high_close_bars = array.new<int>()
|
||||||
|
var array<float> low_close_prices = array.new<float>()
|
||||||
|
var array<int> low_close_bars = array.new<int>()
|
||||||
|
|
||||||
|
// Arrays to store volume data for pivots
|
||||||
|
var array<float> high_volumes = array.new<float>()
|
||||||
|
var array<float> low_volumes = array.new<float>()
|
||||||
|
var array<float> high_close_volumes = array.new<float>()
|
||||||
|
var array<float> low_close_volumes = array.new<float>()
|
||||||
|
|
||||||
|
// Calculate volume average for filtering
|
||||||
|
volume_avg = ta.sma(volume, volume_lookback)
|
||||||
|
|
||||||
|
// Store pivot points (high/low based) with volume data
|
||||||
|
if not na(pivot_high)
|
||||||
|
pivot_bar = bar_index - lookback_period
|
||||||
|
pivot_time = math.round(time[lookback_period])
|
||||||
|
pivot_volume = volume[lookback_period]
|
||||||
|
// Only store if within reasonable distance
|
||||||
|
if bar_index - pivot_bar <= max_bars_back
|
||||||
|
array.push(high_prices, pivot_high)
|
||||||
|
array.push(high_bars, pivot_bar)
|
||||||
|
array.push(high_times, pivot_time)
|
||||||
|
array.push(high_volumes, pivot_volume)
|
||||||
|
// Keep only recent pivots to manage memory
|
||||||
|
if array.size(high_prices) > 50
|
||||||
|
array.shift(high_prices)
|
||||||
|
array.shift(high_bars)
|
||||||
|
array.shift(high_times)
|
||||||
|
array.shift(high_volumes)
|
||||||
|
|
||||||
|
if not na(pivot_low)
|
||||||
|
pivot_bar = bar_index - lookback_period
|
||||||
|
pivot_time = math.round(time[lookback_period])
|
||||||
|
pivot_volume = volume[lookback_period]
|
||||||
|
// Only store if within reasonable distance
|
||||||
|
if bar_index - pivot_bar <= max_bars_back
|
||||||
|
array.push(low_prices, pivot_low)
|
||||||
|
array.push(low_bars, pivot_bar)
|
||||||
|
array.push(low_times, pivot_time)
|
||||||
|
array.push(low_volumes, pivot_volume)
|
||||||
|
// Keep only recent pivots to manage memory
|
||||||
|
if array.size(low_prices) > 50
|
||||||
|
array.shift(low_prices)
|
||||||
|
array.shift(low_bars)
|
||||||
|
array.shift(low_times)
|
||||||
|
array.shift(low_volumes)
|
||||||
|
|
||||||
|
// Store close-based pivot points (always calculate for close-based trend lines) with volume data
|
||||||
|
pivot_high_close = ta.pivothigh(close, lookback_period, lookback_period)
|
||||||
|
pivot_low_close = ta.pivotlow(close, lookback_period, lookback_period)
|
||||||
|
|
||||||
|
if not na(pivot_high_close)
|
||||||
|
pivot_bar = bar_index - lookback_period
|
||||||
|
pivot_volume = volume[lookback_period]
|
||||||
|
if bar_index - pivot_bar <= max_bars_back
|
||||||
|
array.push(high_close_prices, pivot_high_close)
|
||||||
|
array.push(high_close_bars, pivot_bar)
|
||||||
|
array.push(high_close_volumes, pivot_volume)
|
||||||
|
if array.size(high_close_prices) > 50
|
||||||
|
array.shift(high_close_prices)
|
||||||
|
array.shift(high_close_bars)
|
||||||
|
array.shift(high_close_volumes)
|
||||||
|
|
||||||
|
if not na(pivot_low_close)
|
||||||
|
pivot_bar = bar_index - lookback_period
|
||||||
|
pivot_volume = volume[lookback_period]
|
||||||
|
if bar_index - pivot_bar <= max_bars_back
|
||||||
|
array.push(low_close_prices, pivot_low_close)
|
||||||
|
array.push(low_close_bars, pivot_bar)
|
||||||
|
array.push(low_close_volumes, pivot_volume)
|
||||||
|
if array.size(low_close_prices) > 50
|
||||||
|
array.shift(low_close_prices)
|
||||||
|
array.shift(low_close_bars)
|
||||||
|
array.shift(low_close_volumes)
|
||||||
|
|
||||||
|
// Function to calculate line slope and y-intercept
|
||||||
|
get_line_params(x1, y1, x2, y2) =>
|
||||||
|
slope = (y2 - y1) / (x2 - x1)
|
||||||
|
intercept = y1 - slope * x1
|
||||||
|
[slope, intercept]
|
||||||
|
|
||||||
|
// Function to get y value at given x using line equation
|
||||||
|
get_y_at_x(slope, intercept, x) =>
|
||||||
|
slope * x + intercept
|
||||||
|
|
||||||
|
// Structure to store trend line data
|
||||||
|
type TrendLineData
|
||||||
|
float slope
|
||||||
|
float intercept
|
||||||
|
int bar1
|
||||||
|
float price1
|
||||||
|
int bar2
|
||||||
|
float price2
|
||||||
|
int touches
|
||||||
|
bool is_resistance
|
||||||
|
|
||||||
|
// Function to check if two lines are similar and should be merged
|
||||||
|
lines_are_similar(line1, line2, slope_tol, price_tol) =>
|
||||||
|
// Check slope similarity
|
||||||
|
slope_diff = math.abs(line1.slope - line2.slope)
|
||||||
|
avg_slope = (math.abs(line1.slope) + math.abs(line2.slope)) / 2
|
||||||
|
slope_similar = avg_slope == 0 ? slope_diff < 0.001 : (slope_diff / avg_slope) < (slope_tol / 100)
|
||||||
|
|
||||||
|
// Check price level similarity at a common point
|
||||||
|
mid_bar = math.round((line1.bar1 + line1.bar2 + line2.bar1 + line2.bar2) / 4)
|
||||||
|
price1_at_mid = get_y_at_x(line1.slope, line1.intercept, mid_bar)
|
||||||
|
price2_at_mid = get_y_at_x(line2.slope, line2.intercept, mid_bar)
|
||||||
|
price_diff = math.abs(price1_at_mid - price2_at_mid)
|
||||||
|
avg_price = (price1_at_mid + price2_at_mid) / 2
|
||||||
|
price_similar = (price_diff / avg_price) < (price_tol / 100)
|
||||||
|
|
||||||
|
slope_similar and price_similar
|
||||||
|
|
||||||
|
// Function to merge two similar trend lines
|
||||||
|
merge_lines(line1, line2) =>
|
||||||
|
// Choose the line with more touches, or the longer one
|
||||||
|
if line1.touches > line2.touches
|
||||||
|
line1
|
||||||
|
else if line2.touches > line1.touches
|
||||||
|
line2
|
||||||
|
else
|
||||||
|
// If same touches, choose the longer line
|
||||||
|
length1 = math.abs(line1.bar2 - line1.bar1)
|
||||||
|
length2 = math.abs(line2.bar2 - line2.bar1)
|
||||||
|
length1 >= length2 ? line1 : line2
|
||||||
|
|
||||||
|
// Function to check if pivot has sufficient volume
|
||||||
|
has_sufficient_volume(pivot_volume, avg_volume) =>
|
||||||
|
not use_volume_filter or pivot_volume >= (avg_volume * volume_threshold_multiplier)
|
||||||
|
|
||||||
|
// Function to count touches for wick-based trend lines with volume filtering
|
||||||
|
count_wick_touches(slope, intercept, start_bar, end_bar, is_resistance, tolerance_pct = 0.5) =>
|
||||||
|
touches = 0
|
||||||
|
current_bar = bar_index
|
||||||
|
pivot_prices = is_resistance ? high_prices : low_prices
|
||||||
|
pivot_bars_array = is_resistance ? high_bars : low_bars
|
||||||
|
pivot_volumes_array = is_resistance ? high_volumes : low_volumes
|
||||||
|
|
||||||
|
if array.size(pivot_prices) > 0
|
||||||
|
for i = 0 to array.size(pivot_prices) - 1
|
||||||
|
bar_idx = array.get(pivot_bars_array, i)
|
||||||
|
price = array.get(pivot_prices, i)
|
||||||
|
pivot_volume = array.get(pivot_volumes_array, i)
|
||||||
|
|
||||||
|
if bar_idx >= start_bar and bar_idx <= end_bar and (current_bar - bar_idx) <= max_bars_back
|
||||||
|
// Check volume filter
|
||||||
|
if has_sufficient_volume(pivot_volume, volume_avg)
|
||||||
|
expected_price = get_y_at_x(slope, intercept, bar_idx)
|
||||||
|
tolerance = expected_price * tolerance_pct / 100
|
||||||
|
|
||||||
|
if is_resistance
|
||||||
|
if math.abs(price - expected_price) <= tolerance and price >= expected_price - tolerance
|
||||||
|
touches += 1
|
||||||
|
else
|
||||||
|
if math.abs(price - expected_price) <= tolerance and price <= expected_price + tolerance
|
||||||
|
touches += 1
|
||||||
|
touches
|
||||||
|
|
||||||
|
// Function to count touches for close-based trend lines with volume filtering
|
||||||
|
count_close_touches(slope, intercept, start_bar, end_bar, is_resistance, tolerance_pct = 0.5) =>
|
||||||
|
touches = 0
|
||||||
|
current_bar = bar_index
|
||||||
|
close_prices = is_resistance ? high_close_prices : low_close_prices
|
||||||
|
close_bars_array = is_resistance ? high_close_bars : low_close_bars
|
||||||
|
close_volumes_array = is_resistance ? high_close_volumes : low_close_volumes
|
||||||
|
|
||||||
|
if array.size(close_prices) > 0
|
||||||
|
for i = 0 to array.size(close_prices) - 1
|
||||||
|
bar_idx = array.get(close_bars_array, i)
|
||||||
|
price = array.get(close_prices, i)
|
||||||
|
pivot_volume = array.get(close_volumes_array, i)
|
||||||
|
|
||||||
|
if bar_idx >= start_bar and bar_idx <= end_bar and (current_bar - bar_idx) <= max_bars_back
|
||||||
|
// Check volume filter
|
||||||
|
if has_sufficient_volume(pivot_volume, volume_avg)
|
||||||
|
expected_price = get_y_at_x(slope, intercept, bar_idx)
|
||||||
|
tolerance = expected_price * tolerance_pct / 100
|
||||||
|
|
||||||
|
if is_resistance
|
||||||
|
if math.abs(price - expected_price) <= tolerance and price >= expected_price - tolerance
|
||||||
|
touches += 1
|
||||||
|
else
|
||||||
|
if math.abs(price - expected_price) <= tolerance and price <= expected_price + tolerance
|
||||||
|
touches += 1
|
||||||
|
touches
|
||||||
|
|
||||||
|
// Function to draw trend lines
|
||||||
|
draw_trend_lines() =>
|
||||||
|
var array<line> wick_resistance_lines = array.new<line>()
|
||||||
|
var array<line> wick_support_lines = array.new<line>()
|
||||||
|
var array<line> close_resistance_lines = array.new<line>()
|
||||||
|
var array<line> close_support_lines = array.new<line>()
|
||||||
|
|
||||||
|
// Clear old lines
|
||||||
|
for line_obj in wick_resistance_lines
|
||||||
|
line.delete(line_obj)
|
||||||
|
for line_obj in wick_support_lines
|
||||||
|
line.delete(line_obj)
|
||||||
|
for line_obj in close_resistance_lines
|
||||||
|
line.delete(line_obj)
|
||||||
|
for line_obj in close_support_lines
|
||||||
|
line.delete(line_obj)
|
||||||
|
array.clear(wick_resistance_lines)
|
||||||
|
array.clear(wick_support_lines)
|
||||||
|
array.clear(close_resistance_lines)
|
||||||
|
array.clear(close_support_lines)
|
||||||
|
|
||||||
|
current_bar = bar_index
|
||||||
|
|
||||||
|
// Arrays to store trend line candidates
|
||||||
|
var array<TrendLineData> wick_resistance_candidates = array.new<TrendLineData>()
|
||||||
|
var array<TrendLineData> wick_support_candidates = array.new<TrendLineData>()
|
||||||
|
var array<TrendLineData> close_resistance_candidates = array.new<TrendLineData>()
|
||||||
|
var array<TrendLineData> close_support_candidates = array.new<TrendLineData>()
|
||||||
|
array.clear(wick_resistance_candidates)
|
||||||
|
array.clear(wick_support_candidates)
|
||||||
|
array.clear(close_resistance_candidates)
|
||||||
|
array.clear(close_support_candidates)
|
||||||
|
|
||||||
|
// Collect WICK-based resistance line candidates (thin lines)
|
||||||
|
if show_wick_based_lines and show_resistance and array.size(high_prices) >= 2
|
||||||
|
for i = 0 to array.size(high_prices) - 2
|
||||||
|
for j = i + 1 to array.size(high_prices) - 1
|
||||||
|
bar1 = array.get(high_bars, i)
|
||||||
|
price1 = array.get(high_prices, i)
|
||||||
|
volume1 = array.get(high_volumes, i)
|
||||||
|
bar2 = array.get(high_bars, j)
|
||||||
|
price2 = array.get(high_prices, j)
|
||||||
|
volume2 = array.get(high_volumes, j)
|
||||||
|
|
||||||
|
if bar2 - bar1 < lookback_period or (current_bar - bar1) > max_bars_back or (current_bar - bar2) > max_bars_back
|
||||||
|
continue
|
||||||
|
|
||||||
|
// Apply volume filter to both pivot points
|
||||||
|
if use_volume_filter and (not has_sufficient_volume(volume1, volume_avg) or not has_sufficient_volume(volume2, volume_avg))
|
||||||
|
continue
|
||||||
|
|
||||||
|
[slope, intercept] = get_line_params(bar1, price1, bar2, price2)
|
||||||
|
touches = count_wick_touches(slope, intercept, bar1, current_bar, true)
|
||||||
|
|
||||||
|
if touches >= min_touches
|
||||||
|
trend_line = TrendLineData.new(slope, intercept, bar1, price1, bar2, price2, touches, true)
|
||||||
|
array.push(wick_resistance_candidates, trend_line)
|
||||||
|
|
||||||
|
// Collect CLOSE-based resistance line candidates (thick lines)
|
||||||
|
if show_close_based_lines and show_resistance and array.size(high_close_prices) >= 2
|
||||||
|
for i = 0 to array.size(high_close_prices) - 2
|
||||||
|
for j = i + 1 to array.size(high_close_prices) - 1
|
||||||
|
bar1 = array.get(high_close_bars, i)
|
||||||
|
price1 = array.get(high_close_prices, i)
|
||||||
|
volume1 = array.get(high_close_volumes, i)
|
||||||
|
bar2 = array.get(high_close_bars, j)
|
||||||
|
price2 = array.get(high_close_prices, j)
|
||||||
|
volume2 = array.get(high_close_volumes, j)
|
||||||
|
|
||||||
|
if bar2 - bar1 < lookback_period or (current_bar - bar1) > max_bars_back or (current_bar - bar2) > max_bars_back
|
||||||
|
continue
|
||||||
|
|
||||||
|
// Apply volume filter to both pivot points
|
||||||
|
if use_volume_filter and (not has_sufficient_volume(volume1, volume_avg) or not has_sufficient_volume(volume2, volume_avg))
|
||||||
|
continue
|
||||||
|
|
||||||
|
[slope, intercept] = get_line_params(bar1, price1, bar2, price2)
|
||||||
|
touches = count_close_touches(slope, intercept, bar1, current_bar, true)
|
||||||
|
|
||||||
|
if touches >= min_touches
|
||||||
|
trend_line = TrendLineData.new(slope, intercept, bar1, price1, bar2, price2, touches, true)
|
||||||
|
array.push(close_resistance_candidates, trend_line)
|
||||||
|
|
||||||
|
// Collect WICK-based support line candidates (thin lines)
|
||||||
|
if show_wick_based_lines and show_support and array.size(low_prices) >= 2
|
||||||
|
for i = 0 to array.size(low_prices) - 2
|
||||||
|
for j = i + 1 to array.size(low_prices) - 1
|
||||||
|
bar1 = array.get(low_bars, i)
|
||||||
|
price1 = array.get(low_prices, i)
|
||||||
|
volume1 = array.get(low_volumes, i)
|
||||||
|
bar2 = array.get(low_bars, j)
|
||||||
|
price2 = array.get(low_prices, j)
|
||||||
|
volume2 = array.get(low_volumes, j)
|
||||||
|
|
||||||
|
if bar2 - bar1 < lookback_period or (current_bar - bar1) > max_bars_back or (current_bar - bar2) > max_bars_back
|
||||||
|
continue
|
||||||
|
|
||||||
|
// Apply volume filter to both pivot points
|
||||||
|
if use_volume_filter and (not has_sufficient_volume(volume1, volume_avg) or not has_sufficient_volume(volume2, volume_avg))
|
||||||
|
continue
|
||||||
|
|
||||||
|
[slope, intercept] = get_line_params(bar1, price1, bar2, price2)
|
||||||
|
touches = count_wick_touches(slope, intercept, bar1, current_bar, false)
|
||||||
|
|
||||||
|
if touches >= min_touches
|
||||||
|
trend_line = TrendLineData.new(slope, intercept, bar1, price1, bar2, price2, touches, false)
|
||||||
|
array.push(wick_support_candidates, trend_line)
|
||||||
|
|
||||||
|
// Collect CLOSE-based support line candidates (thick lines)
|
||||||
|
if show_close_based_lines and show_support and array.size(low_close_prices) >= 2
|
||||||
|
for i = 0 to array.size(low_close_prices) - 2
|
||||||
|
for j = i + 1 to array.size(low_close_prices) - 1
|
||||||
|
bar1 = array.get(low_close_bars, i)
|
||||||
|
price1 = array.get(low_close_prices, i)
|
||||||
|
volume1 = array.get(low_close_volumes, i)
|
||||||
|
bar2 = array.get(low_close_bars, j)
|
||||||
|
price2 = array.get(low_close_prices, j)
|
||||||
|
volume2 = array.get(low_close_volumes, j)
|
||||||
|
|
||||||
|
if bar2 - bar1 < lookback_period or (current_bar - bar1) > max_bars_back or (current_bar - bar2) > max_bars_back
|
||||||
|
continue
|
||||||
|
|
||||||
|
// Apply volume filter to both pivot points
|
||||||
|
if use_volume_filter and (not has_sufficient_volume(volume1, volume_avg) or not has_sufficient_volume(volume2, volume_avg))
|
||||||
|
continue
|
||||||
|
|
||||||
|
[slope, intercept] = get_line_params(bar1, price1, bar2, price2)
|
||||||
|
touches = count_close_touches(slope, intercept, bar1, current_bar, false)
|
||||||
|
|
||||||
|
if touches >= min_touches
|
||||||
|
trend_line = TrendLineData.new(slope, intercept, bar1, price1, bar2, price2, touches, false)
|
||||||
|
array.push(close_support_candidates, trend_line)
|
||||||
|
|
||||||
|
// Process and draw lines separately for wick-based and close-based
|
||||||
|
if merge_similar_lines
|
||||||
|
// Process WICK-based resistance lines (thin)
|
||||||
|
if array.size(wick_resistance_candidates) > 0
|
||||||
|
merged_wick_resistance = array.new<TrendLineData>()
|
||||||
|
|
||||||
|
for i = 0 to array.size(wick_resistance_candidates) - 1
|
||||||
|
current_line = array.get(wick_resistance_candidates, i)
|
||||||
|
should_merge = false
|
||||||
|
|
||||||
|
if array.size(merged_wick_resistance) > 0
|
||||||
|
for j = 0 to array.size(merged_wick_resistance) - 1
|
||||||
|
existing_line = array.get(merged_wick_resistance, j)
|
||||||
|
if lines_are_similar(current_line, existing_line, slope_tolerance, price_tolerance)
|
||||||
|
merged_line = merge_lines(current_line, existing_line)
|
||||||
|
array.set(merged_wick_resistance, j, merged_line)
|
||||||
|
should_merge := true
|
||||||
|
break
|
||||||
|
|
||||||
|
if not should_merge
|
||||||
|
array.push(merged_wick_resistance, current_line)
|
||||||
|
|
||||||
|
// Draw wick-based resistance lines (thin)
|
||||||
|
lines_drawn = 0
|
||||||
|
if array.size(merged_wick_resistance) > 0
|
||||||
|
for i = 0 to array.size(merged_wick_resistance) - 1
|
||||||
|
if lines_drawn >= max_lines
|
||||||
|
break
|
||||||
|
|
||||||
|
trend_line = array.get(merged_wick_resistance, i)
|
||||||
|
extend_type = switch line_extend
|
||||||
|
"left" => extend.left
|
||||||
|
"right" => extend.right
|
||||||
|
"both" => extend.both
|
||||||
|
=> extend.none
|
||||||
|
|
||||||
|
line_obj = line.new(trend_line.bar1, trend_line.price1, trend_line.bar2, trend_line.price2,
|
||||||
|
color=resistance_color,
|
||||||
|
width=thin_line_width,
|
||||||
|
style=get_line_style(),
|
||||||
|
extend=extend_type)
|
||||||
|
array.push(wick_resistance_lines, line_obj)
|
||||||
|
lines_drawn += 1
|
||||||
|
|
||||||
|
// Process CLOSE-based resistance lines (thick)
|
||||||
|
if array.size(close_resistance_candidates) > 0
|
||||||
|
merged_close_resistance = array.new<TrendLineData>()
|
||||||
|
|
||||||
|
for i = 0 to array.size(close_resistance_candidates) - 1
|
||||||
|
current_line = array.get(close_resistance_candidates, i)
|
||||||
|
should_merge = false
|
||||||
|
|
||||||
|
if array.size(merged_close_resistance) > 0
|
||||||
|
for j = 0 to array.size(merged_close_resistance) - 1
|
||||||
|
existing_line = array.get(merged_close_resistance, j)
|
||||||
|
if lines_are_similar(current_line, existing_line, slope_tolerance, price_tolerance)
|
||||||
|
merged_line = merge_lines(current_line, existing_line)
|
||||||
|
array.set(merged_close_resistance, j, merged_line)
|
||||||
|
should_merge := true
|
||||||
|
break
|
||||||
|
|
||||||
|
if not should_merge
|
||||||
|
array.push(merged_close_resistance, current_line)
|
||||||
|
|
||||||
|
// Draw close-based resistance lines (thick)
|
||||||
|
lines_drawn = 0
|
||||||
|
if array.size(merged_close_resistance) > 0
|
||||||
|
for i = 0 to array.size(merged_close_resistance) - 1
|
||||||
|
if lines_drawn >= max_lines
|
||||||
|
break
|
||||||
|
|
||||||
|
trend_line = array.get(merged_close_resistance, i)
|
||||||
|
extend_type = switch line_extend
|
||||||
|
"left" => extend.left
|
||||||
|
"right" => extend.right
|
||||||
|
"both" => extend.both
|
||||||
|
=> extend.none
|
||||||
|
|
||||||
|
line_obj = line.new(trend_line.bar1, trend_line.price1, trend_line.bar2, trend_line.price2,
|
||||||
|
color=close_resistance_color,
|
||||||
|
width=thick_line_width,
|
||||||
|
style=get_line_style(),
|
||||||
|
extend=extend_type)
|
||||||
|
array.push(close_resistance_lines, line_obj)
|
||||||
|
lines_drawn += 1
|
||||||
|
|
||||||
|
// Process WICK-based support lines (thin)
|
||||||
|
if array.size(wick_support_candidates) > 0
|
||||||
|
merged_wick_support = array.new<TrendLineData>()
|
||||||
|
|
||||||
|
for i = 0 to array.size(wick_support_candidates) - 1
|
||||||
|
current_line = array.get(wick_support_candidates, i)
|
||||||
|
should_merge = false
|
||||||
|
|
||||||
|
if array.size(merged_wick_support) > 0
|
||||||
|
for j = 0 to array.size(merged_wick_support) - 1
|
||||||
|
existing_line = array.get(merged_wick_support, j)
|
||||||
|
if lines_are_similar(current_line, existing_line, slope_tolerance, price_tolerance)
|
||||||
|
merged_line = merge_lines(current_line, existing_line)
|
||||||
|
array.set(merged_wick_support, j, merged_line)
|
||||||
|
should_merge := true
|
||||||
|
break
|
||||||
|
|
||||||
|
if not should_merge
|
||||||
|
array.push(merged_wick_support, current_line)
|
||||||
|
|
||||||
|
// Draw wick-based support lines (thin)
|
||||||
|
lines_drawn = 0
|
||||||
|
if array.size(merged_wick_support) > 0
|
||||||
|
for i = 0 to array.size(merged_wick_support) - 1
|
||||||
|
if lines_drawn >= max_lines
|
||||||
|
break
|
||||||
|
|
||||||
|
trend_line = array.get(merged_wick_support, i)
|
||||||
|
extend_type = switch line_extend
|
||||||
|
"left" => extend.left
|
||||||
|
"right" => extend.right
|
||||||
|
"both" => extend.both
|
||||||
|
=> extend.none
|
||||||
|
|
||||||
|
line_obj = line.new(trend_line.bar1, trend_line.price1, trend_line.bar2, trend_line.price2,
|
||||||
|
color=support_color,
|
||||||
|
width=thin_line_width,
|
||||||
|
style=get_line_style(),
|
||||||
|
extend=extend_type)
|
||||||
|
array.push(wick_support_lines, line_obj)
|
||||||
|
lines_drawn += 1
|
||||||
|
|
||||||
|
// Process CLOSE-based support lines (thick)
|
||||||
|
if array.size(close_support_candidates) > 0
|
||||||
|
merged_close_support = array.new<TrendLineData>()
|
||||||
|
|
||||||
|
for i = 0 to array.size(close_support_candidates) - 1
|
||||||
|
current_line = array.get(close_support_candidates, i)
|
||||||
|
should_merge = false
|
||||||
|
|
||||||
|
if array.size(merged_close_support) > 0
|
||||||
|
for j = 0 to array.size(merged_close_support) - 1
|
||||||
|
existing_line = array.get(merged_close_support, j)
|
||||||
|
if lines_are_similar(current_line, existing_line, slope_tolerance, price_tolerance)
|
||||||
|
merged_line = merge_lines(current_line, existing_line)
|
||||||
|
array.set(merged_close_support, j, merged_line)
|
||||||
|
should_merge := true
|
||||||
|
break
|
||||||
|
|
||||||
|
if not should_merge
|
||||||
|
array.push(merged_close_support, current_line)
|
||||||
|
|
||||||
|
// Draw close-based support lines (thick)
|
||||||
|
lines_drawn = 0
|
||||||
|
if array.size(merged_close_support) > 0
|
||||||
|
for i = 0 to array.size(merged_close_support) - 1
|
||||||
|
if lines_drawn >= max_lines
|
||||||
|
break
|
||||||
|
|
||||||
|
trend_line = array.get(merged_close_support, i)
|
||||||
|
extend_type = switch line_extend
|
||||||
|
"left" => extend.left
|
||||||
|
"right" => extend.right
|
||||||
|
"both" => extend.both
|
||||||
|
=> extend.none
|
||||||
|
|
||||||
|
line_obj = line.new(trend_line.bar1, trend_line.price1, trend_line.bar2, trend_line.price2,
|
||||||
|
color=close_support_color,
|
||||||
|
width=thick_line_width,
|
||||||
|
style=get_line_style(),
|
||||||
|
extend=extend_type)
|
||||||
|
array.push(close_support_lines, line_obj)
|
||||||
|
lines_drawn += 1
|
||||||
|
else
|
||||||
|
// Original behavior without merging - draw all valid candidates
|
||||||
|
// Draw wick-based resistance lines (thin)
|
||||||
|
lines_drawn = 0
|
||||||
|
if array.size(wick_resistance_candidates) > 0
|
||||||
|
for i = 0 to array.size(wick_resistance_candidates) - 1
|
||||||
|
if lines_drawn >= max_lines
|
||||||
|
break
|
||||||
|
trend_line = array.get(wick_resistance_candidates, i)
|
||||||
|
extend_type = switch line_extend
|
||||||
|
"left" => extend.left
|
||||||
|
"right" => extend.right
|
||||||
|
"both" => extend.both
|
||||||
|
=> extend.none
|
||||||
|
|
||||||
|
line_obj = line.new(trend_line.bar1, trend_line.price1, trend_line.bar2, trend_line.price2,
|
||||||
|
color=resistance_color,
|
||||||
|
width=thin_line_width,
|
||||||
|
style=get_line_style(),
|
||||||
|
extend=extend_type)
|
||||||
|
array.push(wick_resistance_lines, line_obj)
|
||||||
|
lines_drawn += 1
|
||||||
|
|
||||||
|
// Draw close-based resistance lines (thick)
|
||||||
|
lines_drawn := 0
|
||||||
|
if array.size(close_resistance_candidates) > 0
|
||||||
|
for i = 0 to array.size(close_resistance_candidates) - 1
|
||||||
|
if lines_drawn >= max_lines
|
||||||
|
break
|
||||||
|
trend_line = array.get(close_resistance_candidates, i)
|
||||||
|
extend_type = switch line_extend
|
||||||
|
"left" => extend.left
|
||||||
|
"right" => extend.right
|
||||||
|
"both" => extend.both
|
||||||
|
=> extend.none
|
||||||
|
|
||||||
|
line_obj = line.new(trend_line.bar1, trend_line.price1, trend_line.bar2, trend_line.price2,
|
||||||
|
color=close_resistance_color,
|
||||||
|
width=thick_line_width,
|
||||||
|
style=get_line_style(),
|
||||||
|
extend=extend_type)
|
||||||
|
array.push(close_resistance_lines, line_obj)
|
||||||
|
lines_drawn += 1
|
||||||
|
|
||||||
|
// Draw wick-based support lines (thin)
|
||||||
|
lines_drawn := 0
|
||||||
|
if array.size(wick_support_candidates) > 0
|
||||||
|
for i = 0 to array.size(wick_support_candidates) - 1
|
||||||
|
if lines_drawn >= max_lines
|
||||||
|
break
|
||||||
|
trend_line = array.get(wick_support_candidates, i)
|
||||||
|
extend_type = switch line_extend
|
||||||
|
"left" => extend.left
|
||||||
|
"right" => extend.right
|
||||||
|
"both" => extend.both
|
||||||
|
=> extend.none
|
||||||
|
|
||||||
|
line_obj = line.new(trend_line.bar1, trend_line.price1, trend_line.bar2, trend_line.price2,
|
||||||
|
color=support_color,
|
||||||
|
width=thin_line_width,
|
||||||
|
style=get_line_style(),
|
||||||
|
extend=extend_type)
|
||||||
|
array.push(wick_support_lines, line_obj)
|
||||||
|
lines_drawn += 1
|
||||||
|
|
||||||
|
// Draw close-based support lines (thick)
|
||||||
|
lines_drawn := 0
|
||||||
|
if array.size(close_support_candidates) > 0
|
||||||
|
for i = 0 to array.size(close_support_candidates) - 1
|
||||||
|
if lines_drawn >= max_lines
|
||||||
|
break
|
||||||
|
trend_line = array.get(close_support_candidates, i)
|
||||||
|
extend_type = switch line_extend
|
||||||
|
"left" => extend.left
|
||||||
|
"right" => extend.right
|
||||||
|
"both" => extend.both
|
||||||
|
=> extend.none
|
||||||
|
|
||||||
|
line_obj = line.new(trend_line.bar1, trend_line.price1, trend_line.bar2, trend_line.price2,
|
||||||
|
color=close_support_color,
|
||||||
|
width=thick_line_width,
|
||||||
|
style=get_line_style(),
|
||||||
|
extend=extend_type)
|
||||||
|
array.push(close_support_lines, line_obj)
|
||||||
|
lines_drawn += 1
|
||||||
|
|
||||||
|
// Execute trend line drawing every 10 bars to optimize performance
|
||||||
|
if bar_index % 10 == 0
|
||||||
|
draw_trend_lines()
|
||||||
|
|
||||||
|
// Show current bar high/low points
|
||||||
|
if show_current_hl
|
||||||
|
var line current_high_line = na
|
||||||
|
var line current_low_line = na
|
||||||
|
|
||||||
|
// Delete previous lines
|
||||||
|
if not na(current_high_line)
|
||||||
|
line.delete(current_high_line)
|
||||||
|
if not na(current_low_line)
|
||||||
|
line.delete(current_low_line)
|
||||||
|
|
||||||
|
// Draw current bar high/low points
|
||||||
|
current_high_line := line.new(bar_index, high, bar_index, high, color=current_hl_color, width=3, style=line.style_solid)
|
||||||
|
current_low_line := line.new(bar_index, low, bar_index, low, color=current_hl_color, width=3, style=line.style_solid)
|
||||||
|
|
||||||
|
// Plot pivot points for reference (optional)
|
||||||
|
plot_pivots = input.bool(false, "Show Pivot Points")
|
||||||
|
plotshape(plot_pivots and not na(pivot_high), style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small)
|
||||||
|
plotshape(plot_pivots and not na(pivot_low), style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small)
|
||||||
|
|
||||||
|
// Plot close-based pivot points (moved to global scope)
|
||||||
|
plotshape(plot_pivots and not na(pivot_high_close), style=shape.diamond, location=location.abovebar, color=color.orange, size=size.small)
|
||||||
|
plotshape(plot_pivots and not na(pivot_low_close), style=shape.diamond, location=location.belowbar, color=color.blue, size=size.small)
|
||||||
|
|
||||||
|
// Volume filtering visualization
|
||||||
|
show_volume_info = input.bool(false, "Show Volume Information")
|
||||||
|
if show_volume_info
|
||||||
|
// Plot volume threshold line
|
||||||
|
volume_threshold = volume_avg * volume_threshold_multiplier
|
||||||
|
|
||||||
|
// Create a table to show volume information
|
||||||
|
var table volume_table = table.new(position.top_right, 2, 4, bgcolor=color.white, border_width=1)
|
||||||
|
if barstate.islast
|
||||||
|
table.cell(volume_table, 0, 0, "Volume Info", text_color=color.black, text_size=size.small)
|
||||||
|
table.cell(volume_table, 1, 0, "", text_color=color.black, text_size=size.small)
|
||||||
|
table.cell(volume_table, 0, 1, "Avg Volume:", text_color=color.black, text_size=size.small)
|
||||||
|
table.cell(volume_table, 1, 1, str.tostring(math.round(volume_avg)), text_color=color.black, text_size=size.small)
|
||||||
|
table.cell(volume_table, 0, 2, "Threshold:", text_color=color.black, text_size=size.small)
|
||||||
|
table.cell(volume_table, 1, 2, str.tostring(math.round(volume_threshold)), text_color=color.black, text_size=size.small)
|
||||||
|
table.cell(volume_table, 0, 3, "Current:", text_color=color.black, text_size=size.small)
|
||||||
|
table.cell(volume_table, 1, 3, str.tostring(math.round(volume)), text_color=volume >= volume_threshold ? color.green : color.red, text_size=size.small)
|
||||||
|
|
||||||
|
// Alert conditions
|
||||||
|
resistance_break = ta.crossover(close, ta.highest(high, 5))
|
||||||
|
support_break = ta.crossunder(close, ta.lowest(low, 5))
|
||||||
|
|
||||||
|
alertcondition(resistance_break, title="Resistance Break", message="Price broke above resistance level")
|
||||||
|
alertcondition(support_break, title="Support Break", message="Price broke below support level")
|
||||||
Reference in New Issue
Block a user