Breakout Volume Delta | Flux Charts

fluxchart · study · 454 行 · 点赞 6,094 · TradingView 原页

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

Pine Script

// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// ©fluxchart
 
//@version=6
indicator("Breakout Volume Delta | Flux Charts", overlay = true, max_lines_count = 500, max_labels_count = 500, behind_chart = false)
 
//#region CONSTANTS
const string sTfTip = "Timeframe used to detect swing highs/lows."
const string volDelTip = "Lower timeframe used to estimate buy vs sell volume for the breakout candle."
const string brModeTip = "Choose whether a level is considered broken by candle wicks or by candle close."
const string showNeTip = "Limits how many most recent swing high/low levels shown on the chart."
const string brVolFiltTip = "Only count breakouts when the breakout candle has enough bull/bear volume dominance."
 
const string grpSw = "GENERAL CONFIGURATION"
const string grpLn = "Styling"
disp = display.none
//#endregion CONSTANTS
 
//#region INPUTS
int swLenL = input.int(10, "Swing Left", minval = 1, group = grpSw, inline = "sw", display = disp)
int swLenR = input.int(10, "Right", minval = 1, group = grpSw, inline = "sw", display = disp)
 
string swTfIn = input.timeframe("", "Swing Timeframe", tooltip = sTfTip, group = grpSw, display = disp)
string vdLtf = input.timeframe("1", "Volume Delta LTF", tooltip = volDelTip, group = grpSw, display = disp)
 
string brMode = input.string("Close", "Breakout by", options = ["Close", "Wick"], tooltip = brModeTip, group = grpSw, display = disp)
int maxLvls = input.int(5, "Show Nearest", minval = 1, tooltip = showNeTip, group = grpSw, display = disp)
 
bool brVolFiltOn = input.bool(false, "Breakout Volume Filter", tooltip = brVolFiltTip, group = grpSw, inline = "bv", display = disp)
float brVolFiltPctIn = input.float(60.0, "", minval = 0.0, maxval = 100.0, step = 0.5, group = grpSw, inline = "bv", display = disp)
 
string unmitStyleIn = input.string("Dotted", "Unmitigated Levels", options = ["Solid", "Dashed", "Dotted"], group = grpLn, inline = "unm", display = disp)
int unmitW = input.int(1, "", minval = 1, maxval = 4, group = grpLn, inline = "unm", display = disp)
color unmitHiCol = input.color(color.new(color.lime, 0), "", group = grpLn, inline = "unm", display = disp)
color unmitLoCol = input.color(color.new(color.red, 0), "", group = grpLn, inline = "unm", display = disp)
 
string brkStyleIn = input.string("Solid", "Broken Levels", options = ["Solid", "Dashed", "Dotted"], group = grpLn, inline = "brk", display = disp)
int brkW = input.int(1, "", minval = 1, maxval = 4, group = grpLn, inline = "brk", display = disp)
color brkHiCol = input.color(color.new(color.lime, 0), "", group = grpLn, inline = "brk", display = disp)
color brkLoCol = input.color(color.new(color.red, 0), "", group = grpLn, inline = "brk", display = disp)
 
color bullBase = input.color(color.new(#00FF6A, 0), "Volume Delta", group = grpLn, inline = "vd", display = disp)
color bearBase = input.color(color.new(#FF2D55, 0), "", group = grpLn, inline = "vd", display = disp)
 
int extendBars = input.int(5, "Extend Levels", minval = 1, group = grpLn, inline = "ext11", display = disp)
bool extendRight = input.bool(false, "Extend Right", group = grpLn, inline = "ext", display = disp)
bool dbgShow = input.bool(true, "Volume Delta Labels", group = grpLn, inline = "ext", display = disp)
//#endregion INPUTS
 
//#region TYPES
type PxT
    float p
    int t
 
type SwBar
    PxT h
    PxT l
//#endregion TYPES
 
//#region ARRAYS
var array<line> linesHi = array.new_line()
var array<float> pricesHi = array.new_float()
var array<bool> mitigHi = array.new_bool()
 
var array<line> linesLo = array.new_line()
var array<float> pricesLo = array.new_float()
var array<bool> mitigLo = array.new_bool()
 
var array<SwBar> swHist = array.new<SwBar>()
//#endregion ARRAYS
 
//#region VARIABLES
var string swTfPrev = ""
var bool swInited = false
var SwBar swCur = na
 
var int lastPhT = na
var int lastPlT = na
 
float fillOpen = na
float fillHigh = na
float fillLow = na
float fillClose = na
color fillCol = na
 
float oppOpen = na
float oppHigh = na
float oppLow = na
float oppClose = na
color oppCol = na
 
color breakOutlineCol = na
 
var bool dbgDoLabel = false
var bool dbgIsBull = false
var string dbgLblTxt = ""
var float dbgLblY = na
//#endregion VARIABLES
 
//#region FUNCTIONS
// Converts a string input into a line style.
fLineStyle(string s) =>
    s == "Solid" ? line.style_solid : s == "Dashed" ? line.style_dashed : line.style_dotted
 
fFmt(float v) =>
    float av = math.abs(v)
    string suf = av >= 1e9 ? "B" : av >= 1e6 ? "M" : av >= 1e3 ? "K" : ""
    float div = av >= 1e9 ? 1e9 : av >= 1e6 ? 1e6 : av >= 1e3 ? 1e3 : 1.0
    string s = str.tostring(v / div, av >= 1e3 ? "#.##" : "#")
    s + suf
 
fPctStr(float pct01) =>
    float p = math.max(0.0, math.min(1.0, pct01)) * 100.0
    str.tostring(p, "#.#") + "%"
 
// Sums bullish sub-bar volume from a lower timeframe.
fBullVolSum(string ltf) =>
    arr = request.security_lower_tf(syminfo.tickerid, ltf, close > open ? volume : 0.0)
    float sum = 0.0
    int n = array.size(arr)
    if n > 0
        for i = 0 to n - 1
            sum += array.get(arr, i)
    sum
 
// Sums bearish sub-bar volume from a lower timeframe.
fBearVolSum(string ltf) =>
    arr = request.security_lower_tf(syminfo.tickerid, ltf, close < open ? volume : 0.0)
    float sum = 0.0
    int n = array.size(arr)
    if n > 0
        for i = 0 to n - 1
            sum += array.get(arr, i)
    sum
 
tfResolve(string tfIn) =>
    string tf = tfIn == "" ? timeframe.period : tfIn
    int chartSec = timeframe.in_seconds(timeframe.period)
    int wantSec = timeframe.in_seconds(tf)
    (not na(chartSec) and not na(wantSec) and wantSec < chartSec) ? timeframe.period : tf
 
pxNew(float p, int t) =>
    PxT.new(p, t)
 
swBarEmpty() =>
    SwBar.new(pxNew(na, na), pxNew(na, na))
 
fTrim() =>
    while array.size(linesHi) > maxLvls
        line.delete(array.shift(linesHi))
        array.shift(pricesHi)
        array.shift(mitigHi)
    while array.size(linesLo) > maxLvls
        line.delete(array.shift(linesLo))
        array.shift(pricesLo)
        array.shift(mitigLo)
//#endregion FUNCTIONS
 
//#region CALCULATIONS
int chartSec = timeframe.in_seconds(timeframe.period)
int barMs = na(chartSec) ? 60000 : (chartSec * 1000)
int extX2 = time + extendBars * barMs
 
// Swing engine and level creation.
string swTf = tfResolve(swTfIn)
string unmitStyle = fLineStyle(unmitStyleIn)
 
if na(swCur)
    swCur := swBarEmpty()
 
if barstate.isfirst or swTfPrev != swTf
    swTfPrev := swTf
    swInited := false
    swCur := swBarEmpty()
    array.clear(swHist)
 
bool swNewTf = timeframe.change(swTf)
 
if swNewTf and swInited
    array.unshift(swHist, swCur)
 
    int keepN = math.max(50, swLenL + swLenR + 10)
    while array.size(swHist) > keepN
        array.pop(swHist)
 
    swInited := false
    swCur := swBarEmpty()
 
if not swInited
    swCur.h := pxNew(high, time)
    swCur.l := pxNew(low, time)
    swInited := true
else
    if na(swCur.h.p) or high > swCur.h.p
        swCur.h := pxNew(high, time)
    if na(swCur.l.p) or low < swCur.l.p
        swCur.l := pxNew(low, time)
 
bool gotPivotEvent = swNewTf and array.size(swHist) >= (swLenL + swLenR + 1)
 
if gotPivotEvent
    int r = swLenR
    int l = swLenL
 
    SwBar cand = array.get(swHist, r)
 
    float candH = cand.h.p
    float candL = cand.l.p
 
    bool isPh = not na(candH)
    bool isPl = not na(candL)
 
    for j = 0 to r - 1
        SwBar b = array.get(swHist, j)
        if isPh and not na(b.h.p) and candH <= b.h.p
            isPh := false
        if isPl and not na(b.l.p) and candL >= b.l.p
            isPl := false
 
    for j = r + 1 to r + l
        SwBar b = array.get(swHist, j)
        if isPh and not na(b.h.p) and candH <= b.h.p
            isPh := false
        if isPl and not na(b.l.p) and candL >= b.l.p
            isPl := false
 
    if isPh and (na(lastPhT) or cand.h.t != lastPhT)
        float y = candH
        line ln = line.new(
            x1 = cand.h.t, y1 = y,
            x2 = (extendRight ? time : extX2), y2 = y,
            xloc = xloc.bar_time,
            extend = (extendRight ? extend.right : extend.none),
            style = unmitStyle, width = unmitW, color = unmitHiCol)
        array.push(linesHi, ln)
        array.push(pricesHi, y)
        array.push(mitigHi, false)
        fTrim()
        lastPhT := cand.h.t
 
    if isPl and (na(lastPlT) or cand.l.t != lastPlT)
        float y = candL
        line ln = line.new(
            x1 = cand.l.t, y1 = y,
            x2 = (extendRight ? time : extX2), y2 = y,
            xloc = xloc.bar_time,
            extend = (extendRight ? extend.right : extend.none),
            style = unmitStyle, width = unmitW, color = unmitLoCol)
        array.push(linesLo, ln)
        array.push(pricesLo, y)
        array.push(mitigLo, false)
        fTrim()
        lastPlT := cand.l.t
 
// LTF bull/bear volume.
float bullVol = fBullVolSum(vdLtf)
float bearVol = fBearVolSum(vdLtf)
float totalVol = bullVol + bearVol
 
float bullPct = totalVol > 0 ? bullVol / totalVol : 0.5
bullPct := math.max(0.0, math.min(1.0, bullPct))
float bearPct = totalVol > 0 ? (1.0 - bullPct) : 0.5
bearPct := math.max(0.0, math.min(1.0, bearPct))
 
float brVolThr = math.max(0.0, math.min(1.0, brVolFiltPctIn / 100.0))
 
// Breakout detection and level updates.
bool useWick = brMode == "Wick"
bool didHighBreak = false
bool didLowBreak = false
 
string brkStyle = fLineStyle(brkStyleIn)
 
int hiCount = array.size(linesHi)
if hiCount > 0
    for i = hiCount - 1 to 0 by 1
        if not array.get(mitigHi, i)
            float lvl = array.get(pricesHi, i)
            bool br = useWick ? high > lvl : close > lvl
            if br
                bool volOk = not brVolFiltOn or (bullPct > brVolThr)
                if brVolFiltOn and not volOk
                    line lnDel = array.get(linesHi, i)
                    line.delete(lnDel)
                    array.remove(linesHi, i)
                    array.remove(pricesHi, i)
                    array.remove(mitigHi, i)
                else
                    array.set(mitigHi, i, true)
                    line ln = array.get(linesHi, i)
                    line.set_style(ln, brkStyle)
                    line.set_width(ln, brkW)
                    line.set_color(ln, brkHiCol)
                    line.set_extend(ln, extend.none)
                    line.set_x2(ln, time)
                    line.set_y2(ln, lvl)
                    didHighBreak := true
 
int loCount = array.size(linesLo)
if loCount > 0
    for i = loCount - 1 to 0 by 1
        if not array.get(mitigLo, i)
            float lvl = array.get(pricesLo, i)
            bool br = useWick ? low < lvl : close < lvl
            if br
                bool volOk = not brVolFiltOn or (bearPct > brVolThr)
                if brVolFiltOn and not volOk
                    line lnDel = array.get(linesLo, i)
                    line.delete(lnDel)
                    array.remove(linesLo, i)
                    array.remove(pricesLo, i)
                    array.remove(mitigLo, i)
                else
                    array.set(mitigLo, i, true)
                    line ln = array.get(linesLo, i)
                    line.set_style(ln, brkStyle)
                    line.set_width(ln, brkW)
                    line.set_color(ln, brkLoCol)
                    line.set_extend(ln, extend.none)
                    line.set_x2(ln, time)
                    line.set_y2(ln, lvl)
                    didLowBreak := true
 
hiCount := array.size(linesHi)
if hiCount > 0
    for i = 0 to hiCount - 1
        if not array.get(mitigHi, i)
            float lvl = array.get(pricesHi, i)
            line ln = array.get(linesHi, i)
            line.set_color(ln, unmitHiCol)
            line.set_width(ln, unmitW)
            line.set_style(ln, unmitStyle)
            line.set_y2(ln, lvl)
            if extendRight
                line.set_extend(ln, extend.right)
                line.set_x2(ln, time)
            else
                line.set_extend(ln, extend.none)
                line.set_x2(ln, extX2)
 
loCount := array.size(linesLo)
if loCount > 0
    for i = 0 to loCount - 1
        if not array.get(mitigLo, i)
            float lvl = array.get(pricesLo, i)
            line ln = array.get(linesLo, i)
            line.set_color(ln, unmitLoCol)
            line.set_width(ln, unmitW)
            line.set_style(ln, unmitStyle)
            line.set_y2(ln, lvl)
            if extendRight
                line.set_extend(ln, extend.right)
                line.set_x2(ln, time)
            else
                line.set_extend(ln, extend.none)
                line.set_x2(ln, extX2)
 
// Breakout fill calculation.
fillOpen := na
fillHigh := na
fillLow := na
fillClose := na
fillCol := na
 
oppOpen := na
oppHigh := na
oppLow := na
oppClose := na
oppCol := na
 
breakOutlineCol := na
 
dbgDoLabel := false
 
bool breakBar = didHighBreak or didLowBreak
 
if breakBar
    bool isBullBreak = didHighBreak
    breakOutlineCol := isBullBreak ? bullBase : bearBase
 
    float bodyTop = math.max(open, close)
    float bodyBtm = math.min(open, close)
    float bodyRng = bodyTop - bodyBtm
    if bodyRng <= 0
        float pad0 = math.max(high - low, syminfo.mintick) * 0.35
        bodyTop := close + pad0
        bodyBtm := close - pad0
        bodyRng := bodyTop - bodyBtm
 
    if isBullBreak
        float cut = bodyBtm + bodyRng * bullPct
 
        fillOpen := bodyBtm
        fillClose := cut
        fillHigh := math.max(fillOpen, fillClose)
        fillLow := math.min(fillOpen, fillClose)
        fillCol := bullBase
 
        oppOpen := cut
        oppClose := bodyTop
        oppHigh := math.max(oppOpen, oppClose)
        oppLow := math.min(oppOpen, oppClose)
        oppCol := bearBase
    else
        float cut = bodyTop - bodyRng * bearPct
 
        fillOpen := cut
        fillClose := bodyTop
        fillHigh := math.max(fillOpen, fillClose)
        fillLow := math.min(fillOpen, fillClose)
        fillCol := bearBase
 
        oppOpen := bodyBtm
        oppClose := cut
        oppHigh := math.max(oppOpen, oppClose)
        oppLow := math.min(oppOpen, oppClose)
        oppCol := bullBase
 
    if dbgShow
        dbgLblTxt := fFmt(bullVol) + " (" + fPctStr(bullPct) + ") | " + fFmt(bearVol) + " (" + fPctStr(bearPct) + ")"
        float pad = math.max(high - low, syminfo.mintick) * 0.25
        dbgLblY := isBullBreak ? (high + pad) : (low - pad)
        dbgIsBull := isBullBreak
        dbgDoLabel := true
//#endregion CALCULATIONS
 
//#region VISUALS
if dbgDoLabel
    label.new(
        x = bar_index,
        y = dbgLblY,
        text = dbgLblTxt,
        style = dbgIsBull ? label.style_label_down : label.style_label_up,
        textcolor = dbgIsBull ? bullBase : bearBase,
        color = color.new(color.black, 100),
        size = size.small, force_overlay = true)
 
plotcandle(
    oppOpen, oppHigh, oppLow, oppClose,
    title = "Breakout Delta Top/Bottom (Opp Segment)",
    color = oppCol, wickcolor = na, bordercolor = na)
 
plotcandle(
    fillOpen, fillHigh, fillLow, fillClose,
    title = "Breakout Delta Top/Bottom (Main Segment)",
    color = fillCol, wickcolor = na, bordercolor = na)
 
plotcandle(
    open, high, low, close,
    title = "Breakout Outline",
    color = na,
    wickcolor = breakOutlineCol,
    bordercolor = breakOutlineCol)
//#endregion VISUALS

← 返回列表