Smart Money Breakout Channels [AlgoAlpha]

AlgoAlpha · study · 279 行 · 点赞 31,313 · TradingView 原页

本页源码来自 TradingView 公开发布的开源脚本,版权归原作者所有, 请遵循其原始许可(Pine 脚本常见 CC BY-NC-SA / MPL-2.0 / MIT)。 本项目仅用于研究检索与许可范围内的移植。

Pine Script

// © AlgoAlpha
// This Source Code is subject to the Mozilla Public License 2.0
// https://mozilla.org/MPL/2.0/
//@version=6
indicator("Smart Money Breakout Channels [AlgoAlpha]", "AlgoAlpha - Breakout Channels",  overlay=true, max_boxes_count = 500)
import TradingView/ta/10
 
// ============================================================================
// INPUTS
// ============================================================================
 
overlap = input.bool(false, "Nested Channels", group = "Main Settings", tooltip="When enabled, allows multiple channels to overlap. When disabled, only one channel can exist at a time. Overlapping channels can show multiple breakout levels simultaneously.")
strong = input.bool(true, "Strong Closes Only", "When enabled, breakouts only trigger when more than 50% of the candle body is outside the channel. This reduces false signals from wicks. When disabled, any price movement outside the channel triggers a breakout.", group = "Main Settings")
length_ = input.int(100, title="Normalization Length", minval=1, group = "Main Settings", tooltip="The number of bars used to calculate the highest high and lowest low for price normalization. Higher values create more stable normalization but may be less responsive to recent price changes.")
length = input.int(14, "Box Detection Length", minval=1, group = "Main Settings", tooltip="The number of bars used to detect channel formation patterns. Lower values create more frequent channels but may be more sensitive to noise. Higher values create fewer but potentially more significant channels.")
 
shw_vol = input.bool(true, "Show Volume Analysis", "When enabled, displays volume analysis as candle-like bars within the channel. This helps identify volume patterns that may precede breakouts.", group = "Volume Analysis")
vol_mode = input.string("Comparison", "Volume Display Mode", options=["Volume", "Comparison", "Delta"], group="Volume Analysis", tooltip="Volume: Shows total volume as symmetrical bars. Comparison: Shows up volume above midline, down volume below. Delta: Shows net volume delta (positive above, negative below midline).")
tf = input.timeframe("1", "Volume Delta Timeframe Source", group = "Volume Analysis", tooltip="The timeframe used to calculate volume delta data. Lower timeframes provide more granular volume analysis but may be noisier.")
vol_scale = input.float(0.5, "Volume Scale", minval=0.1, maxval=2.0, step=0.1, group="Volume Analysis", tooltip="Adjusts the height of volume bars relative to channel size. Higher values make volume bars more prominent, lower values make them more subtle.")
 
green = input.color(#00ffbb, title = "Bullish Colour", group = "Appearance", tooltip = "Primary colour for bullish visual elements. Adjust for preferred palette – affects bars, fills, and labels when momentum is positive.")
red   = input.color(#ff1100, title = "Bearish Colour", group = "Appearance", tooltip = "Primary colour for bearish visual elements. Adjust for preferred palette – affects bars, fills, and labels when momentum is negative.")
 
// ============================================================================
// VARIABLES
// ============================================================================
 
var boxes = array.new_box()
var boxes_u = array.new_box()
var boxes_l = array.new_box()
 
var line[] gaugeLines = array.new<line>()
var label gaugeLabel = na
var line[] centerLines = array.new<line>()
 
// ============================================================================
// FUNCTIONS
// ============================================================================
 
f_can_create(float tNew, float bNew) =>
    ok = true
    if boxes.size() > 0
        for j = 0 to boxes.size()-1
            if (tNew > boxes.get(j).get_bottom()) and (bNew < boxes.get(j).get_top())
                ok := false
                break
    ok
 
getVolumeTransparency(float vol, float smoothed_vol) =>
    if vol_mode == "Volume"
        float vol_ratio = vol / smoothed_vol
        float transparency = math.max(20, math.min(80, 80 - (vol_ratio - 0.5) * 40))
        transparency
    else
        0
 
// ============================================================================
// CALCULATIONS
// ============================================================================
 
lowestLow = ta.lowest(low, length_)
highestHigh = ta.highest(high, length_)
normalizedPrice = (close - lowestLow) / (highestHigh - lowestLow)
vol = ta.stdev(normalizedPrice, 14)
 
upper = (ta.highestbars(vol, length + 1) + length)/length
lower = (ta.lowestbars(vol, length + 1) + length)/length
 
upbreak = 0.0
downbreak = 0.0
 
duration = math.max(nz(ta.barssince(ta.crossover(lower,upper))), 1)
h = ta.highest(duration)
l = ta.lowest(duration)
 
[uv, dv, vold] = ta.requestUpAndDownVolume(tf)
 
var hvold = vold
var lvold = vold
 
if ta.crossover(lower, upper)
    hvold := vold
    lvold := vold
 
if vold > hvold
    hvold := vold
 
if vold < lvold
    lvold := vold
 
smoothedvol = ta.sma(volume, 20)
vola = ta.atr(length)/2
 
bool newChannelFormed = false
bool bullishBreakout = false
bool bearishBreakout = false
 
if ta.crossover(upper, lower) and duration > 10
    if overlap or f_can_create(h, l)
        boxes.unshift(box.new(bar_index-duration, h, bar_index, l, bgcolor = color.new(chart.fg_color, 90), border_color = na))
        boxes_u.unshift(box.new(bar_index-duration, h, bar_index, h-vola, bgcolor = color.new(red, 70), border_color = na))
        boxes_l.unshift(box.new(bar_index-duration, l+vola, bar_index, l, bgcolor = color.new(green, 70), border_color = na))
        
        float centerY = (h + l) / 2
        centerLines.unshift(line.new(bar_index-duration, centerY, bar_index, centerY, color = color.new(chart.fg_color, 50), width = 1, style = line.style_dashed))
        newChannelFormed := true
 
if boxes.size() > 0
    for i = 0 to boxes.size()-1
        if ((strong ? math.avg(close, open) : close) > boxes.get(i).get_top())
            upbreak := boxes.get(i).get_bottom()
            boxes.remove(i)
            boxes_u.remove(i)
            boxes_l.remove(i)
            centerLines.remove(i)
            bullishBreakout := true
            
        else if ((strong ? math.avg(close, open) : close) < boxes.get(i).get_bottom())
            downbreak := boxes.get(i).get_top()
            boxes.remove(i)
            boxes_u.remove(i)
            boxes_l.remove(i)
            centerLines.remove(i)
            bearishBreakout := true
                  
        else
            boxes.get(i).set_right(bar_index)
            boxes_u.get(i).set_right(bar_index)
            boxes_l.get(i).set_right(bar_index)
            centerLines.get(i).set_x2(bar_index)
 
float currentMidline = na
float channelHeight = na
 
if boxes.size() > 0
    float topBound = boxes.get(0).get_top()
    float bottomBound = boxes.get(0).get_bottom()
    currentMidline := (topBound + bottomBound) / 2
    channelHeight := (topBound - bottomBound) * vol_scale
 
float vol_upper_open = na
float vol_upper_high = na  
float vol_upper_low = na
float vol_upper_close = na
 
float vol_lower_open = na
float vol_lower_high = na
float vol_lower_low = na  
float vol_lower_close = na
 
if not na(currentMidline) and not na(channelHeight) and shw_vol
    if vol_mode == "Volume"
        float vol_height = (volume / smoothedvol) * (channelHeight / 4)
        vol_upper_open := currentMidline
        vol_upper_close := currentMidline + vol_height
        vol_upper_high := currentMidline + vol_height
        vol_upper_low := currentMidline
        
        vol_lower_open := currentMidline
        vol_lower_close := currentMidline - vol_height
        vol_lower_high := currentMidline
        vol_lower_low := currentMidline - vol_height
        
    else if vol_mode == "Comparison"
        float uv_height = na(uv) ? 0 : (uv / smoothedvol) * (channelHeight / 4)
        float dv_height = na(dv) ? 0 : (dv / smoothedvol) * (channelHeight / 4)
        
        vol_upper_open := currentMidline
        vol_upper_close := currentMidline + uv_height  
        vol_upper_high := currentMidline + uv_height
        vol_upper_low := currentMidline
        
        vol_lower_open := currentMidline
        vol_lower_close := currentMidline + dv_height
        vol_lower_high := currentMidline  
        vol_lower_low := currentMidline + dv_height
        
    else if vol_mode == "Delta" 
        float delta_height = na(vold) ? 0 : math.abs(vold / smoothedvol) * (channelHeight / 4)
        
        if vold >= 0
            vol_upper_open := currentMidline
            vol_upper_close := currentMidline + delta_height
            vol_upper_high := currentMidline + delta_height  
            vol_upper_low := currentMidline
            
            vol_lower_open := currentMidline
            vol_lower_close := currentMidline
            vol_lower_high := currentMidline
            vol_lower_low := currentMidline
        else
            vol_upper_open := currentMidline
            vol_upper_close := currentMidline
            vol_upper_high := currentMidline
            vol_upper_low := currentMidline
            
            vol_lower_open := currentMidline
            vol_lower_close := currentMidline - delta_height  
            vol_lower_high := currentMidline
            vol_lower_low := currentMidline - delta_height
 
color upperColor = na
color lowerColor = na
 
if vol_mode == "Volume"
    float transparency = getVolumeTransparency(volume, smoothedvol)
    upperColor := color.new(chart.fg_color, transparency)
    lowerColor := color.new(chart.fg_color, transparency)
else
    upperColor := green
    lowerColor := red
 
// ============================================================================
// VISUALS
// ============================================================================
 
plotshape(upbreak != 0 ? upbreak : na, "Bullish Breakout Signal", shape.labelup, location.absolute, green, text = "▲", textcolor = chart.fg_color)
plotshape(downbreak != 0 ? downbreak : na, "Bearish Breakout Signal", shape.labeldown, location.absolute, red, text = "▼", textcolor = chart.fg_color)
 
plotcandle(vol_upper_open, vol_upper_high, vol_upper_low, vol_upper_close, color=upperColor, wickcolor=upperColor, bordercolor=upperColor)
plotcandle(vol_lower_open, vol_lower_high, vol_lower_low, vol_lower_close, color=lowerColor, wickcolor=lowerColor, bordercolor=lowerColor)
 
volumeAvailable = not na(volume)
 
if barstate.islast
    bool channelActive = boxes.size() > 0 and upbreak == 0 and downbreak == 0
    
    if array.size(gaugeLines) > 0
        for ln in gaugeLines
            ln.delete()
        gaugeLines.clear()
    
    if not na(gaugeLabel)
        gaugeLabel.delete()
        gaugeLabel := na
    
    if channelActive
        float topBound    = boxes.get(0).get_top()
        float bottomBound = boxes.get(0).get_bottom()
        
        if not na(topBound) and not na(bottomBound) and topBound != bottomBound
            int segments = 21
            float segLen = (topBound - bottomBound) / segments
            
            for i = 0 to segments - 1
                float y1 = topBound   - i * segLen
                float y2 = topBound   - (i + 1) * segLen
                color segCol = color.from_gradient(y1, bottomBound, topBound, red, green)
                line ln = line.new(x1 = bar_index + 2,
                                     y1 = y1,
                                     x2 = bar_index + 2,
                                     y2 = y2,
                                     color = segCol,
                                     width = 4)
                gaugeLines.unshift(ln)
            
            float delvol = -100*2*((vold-lvold)/(hvold-lvold)-0.5)
            delvol := math.max(math.min(delvol, 100), -100)
            float pointerPos = topBound - ((delvol + 100) / 200) * (topBound - bottomBound)
            gaugeLabel := label.new(x = bar_index + 3,
                                     y = pointerPos,
                                     text = "◀",
                                     color = na,
                                     textcolor = chart.fg_color,
                                     size = size.small,
                                     style = label.style_label_left)
 
if not volumeAvailable
    var gaugeWarnTable = table.new(position = position.top_right, columns = 1, rows = 1, bgcolor = red, border_width = 1, border_color = chart.fg_color, frame_color = chart.fg_color, frame_width = 1)
    table.cell(gaugeWarnTable, 0, 0, "Volume not available\nGauge may not work as expected", text_color = chart.fg_color, text_halign = text.align_center, text_size = size.small)
 
// ============================================================================
// ALERTS
// ============================================================================
 
alertcondition(newChannelFormed, "New Channel Formation", "A new breakout channel has been formed")
alertcondition(bullishBreakout, "Bullish Breakout", "Price has broken out above the channel (bullish signal)")
alertcondition(bearishBreakout, "Bearish Breakout", "Price has broken out below the channel (bearish signal)")

← 返回列表