Market sessions and Volume profile - By Leviathan

LeviathanCapital · study · 307 行 · 点赞 23,342 · TradingView 原页

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

Pine Script

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Leviathan
// levia.io
 
// Some Volume Profile elements are inspired by @LonesomeTheBlue's volume profile script
 
//@version=5
indicator("Market sessions and Volume profile - By Leviathan", overlay=true, max_boxes_count=500)
 
//==========================
//Inputs
//==========================
sessionType = input.string('Daily', 'Session Type', options=['Tokyo','London','New York','Daily','Weekly', 'Monthly'])
 
showProf = input.bool(true, 'Show Volume Profile', group='Display')
showPoc = input.bool(true, 'Show POC', group='Display')
showVA = input.bool(true, 'Show Value Area', group='Display')
showCur = input.bool(true, 'Show Live Zone', group='Display')
resolution = input.int(30, 'Resolution', minval=5, tooltip='The higher the value, the more refined of a profile, but less profiles shown on chart', group='Volume Profile Settings')
smoothVol = input.bool(false, 'Smooth Volume Data', tooltip='Useful for assets that have very large spikes in volume over large bars, helps create better profiles', group='Volume Profile Settings')
dataTf = ''
 
bullCol = input.color(color.rgb(76, 175, 79, 50), 'Up Volume', group='Appearance')
bearCol = input.color(color.rgb(255, 82, 82, 50), 'Down Volume', group='Appearance')
pocCol = input.color(color.red, 'POC', inline='p', group='Appearance')
pocWid = input.int(1, 'Thickness', inline='p', group='Appearance')
vahCol = input.color(color.aqua, 'VAH', inline='h', group='Appearance')
vahWid = input.int(1, 'Thickness', inline='h', group='Appearance')
valCol = input.color(color.aqua, 'VAL', inline='l', group='Appearance')
valWid = input.int(1, 'Thickness', inline='l', group='Appearance')
boxWid = input.int(1, 'Session Box Thickness', group='Appearance')
 
//==========================
//Constants / Variable Declaration
//==========================
var int zoneStart = 0
int lookback = bar_index - zoneStart
var activeZone = false
 
// Defining arrays that store the information
var vpGreen = array.new_float(resolution, 0) // Sum of volume on long bars
var vpRed = array.new_float(resolution, 0) // Same thing but with red bars
var zoneBounds = array.new_float(resolution, 0) // array that stores the highest value that can be in a zone
 
//Values to store current intra bar data
var float[] ltfOpen =  array.new_float(0)
var float[] ltfClose =  array.new_float(0)
var float[] ltfHigh =  array.new_float(0)
var float[] ltfLow =  array.new_float(0)
var float[] ltfVolume = array.new_float(0)
 
 
vol() =>
    smoothVol ? ta.ema(volume, 5) : volume
 
//Getting intrabar intial data
[dO, dC, dH, dL, dV] = request.security_lower_tf(syminfo.tickerid, dataTf, [open, close, high, low, vol()])
 
//==========================
//Functions
//==========================
resetProfile(enable) =>
    if enable
        array.fill(vpGreen, 0)
        array.fill(vpRed, 0)
        array.clear(ltfOpen)
        array.clear(ltfHigh)
        array.clear(ltfLow)
        array.clear(ltfClose)
        array.clear(ltfVolume)
 
profHigh = ta.highest(high, lookback+1)[1]
profLow = ta.lowest(low, lookback+1)[1]
 
tr = ta.atr(1)
atr = ta.atr(14)
 
get_vol(y11, y12, y21, y22, height, vol) =>
    nz(math.max(math.min(math.max(y11, y12), math.max(y21, y22)) - math.max(math.min(y11, y12), math.min(y21, y22)), 0) * vol / height)
 
profileAdd(o, h, l, c, v, g, w) =>
    //Array to store how much to distribute in each zone, on scale of 1 for full gap size to 0
    zoneDist = array.new_float(resolution, 0)
    distSum = 0.0
    // Going over each zone
    for i = 0 to array.size(vpGreen) - 1
        // Checking to see if cur bar is in zone
        zoneTop = array.get(zoneBounds, i)
        zoneBot = zoneTop - g
 
        body_top = math.max(c, o)
        body_bot = math.min(c, o)
        itsgreen = c >= o
 
        topwick = h - body_top
        bottomwick = body_bot - l
        body = body_top - body_bot
 
        bodyvol = body * v / (2 * topwick + 2 * bottomwick + body)
        topwickvol = 2 * topwick * v / (2 * topwick + 2 * bottomwick + body)
        bottomwickvol = 2 * bottomwick * v / (2 * topwick + 2 * bottomwick + body)
 
        array.set(vpGreen, i, array.get(vpGreen, i) + (itsgreen ? get_vol(zoneBot, zoneTop, body_bot, body_top, body, bodyvol) : 0) + get_vol(zoneBot, zoneTop, body_top, h, topwick, topwickvol) / 2 + get_vol(zoneBot, zoneTop, body_bot, l, bottomwick, bottomwickvol) / 2)
        array.set(vpRed, i, array.get(vpRed, i) + (itsgreen ? 0 : get_vol(zoneBot, zoneTop, body_bot, body_top, body, bodyvol)) + get_vol(zoneBot, zoneTop, body_top, h, topwick, topwickvol) / 2 + get_vol(zoneBot, zoneTop, body_bot, l, bottomwick, bottomwickvol) / 2)
 
calcSession(update) =>
    array.fill(vpGreen, 0)
    array.fill(vpRed, 0)
    if bar_index > lookback and update
        gap = (profHigh - profLow) / resolution
 
        // Defining profile bounds
        for i = 0 to resolution - 1
            array.set(zoneBounds, i, profHigh - gap * i)
 
        // Putting each bar inside zone into the volume profile array
        if array.size(ltfOpen) > 0
            for j = 0 to array.size(ltfOpen) - 1
                profileAdd(array.get(ltfOpen, j), array.get(ltfHigh, j), array.get(ltfLow, j), array.get(ltfClose, j), array.get(ltfVolume, j), gap, 1)
 
pocLevel() =>
    float maxVol = 0
    int levelInd = 0
    for i = 0 to array.size(vpRed) - 1
        if array.get(vpRed, i) + array.get(vpGreen, i) > maxVol
            maxVol := array.get(vpRed, i) + array.get(vpGreen, i)
            levelInd := i
 
    float outLevel = na
    if levelInd != array.size(vpRed) - 1
        outLevel := array.get(zoneBounds, levelInd) - (array.get(zoneBounds, levelInd) - array.get(zoneBounds, levelInd+1)) / 2
    outLevel
 
valueLevels(poc) =>
    float gap = (profHigh - profLow) / resolution
    float volSum = array.sum(vpRed) + array.sum(vpGreen)
    float volCnt = 0
 
    float vah = profHigh
    float val = profLow
 
    //Finding poc index
    int pocInd = 0
    for i = 0 to array.size(zoneBounds)-2
        if array.get(zoneBounds, i) >= poc and array.get(zoneBounds, i + 1) < poc
            pocInd := i
 
    volCnt += (array.get(vpRed, pocInd) + array.get(vpGreen, pocInd))
    for i = 1 to array.size(vpRed)
        if pocInd + i >= 0 and pocInd + i < array.size(vpRed)
            volCnt += (array.get(vpRed, pocInd + i) + array.get(vpGreen, pocInd + i))
            if volCnt >= volSum * 0.7
                break
            else
                val := array.get(zoneBounds, pocInd + i) - gap
        if pocInd - i >= 0 and pocInd - i < array.size(vpRed)
            volCnt += (array.get(vpRed, pocInd - i) + array.get(vpGreen, pocInd - i))
            if volCnt >= volSum * 0.7
                break
            else
                vah := array.get(zoneBounds, pocInd - i)
 
    [val, vah]
 
drawNewZone(update) =>
    if bar_index > lookback and update and array.sum(vpGreen) + array.sum(vpRed) > 0
        gap = (profHigh - profLow) / resolution
        float leftMax = bar_index[lookback]
        float rightMax = bar_index[int(lookback / 1.4)]
        float rightMaxVol = array.max(vpGreen)+array.max(vpRed)
        float buffer = gap / 10
        if showProf
            for i = 0 to array.size(vpRed) - 1
                greenEnd = int(leftMax + (rightMax - leftMax) * (array.get(vpGreen, i) / rightMaxVol))
                redEnd = int(greenEnd + (rightMax - leftMax) * (array.get(vpRed, i) / rightMaxVol))
                box.new(int(leftMax), array.get(zoneBounds, i) - buffer, greenEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bullCol, border_width=0)
                box.new(greenEnd, array.get(zoneBounds, i) - buffer, redEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bearCol, border_width=0)
        box.new(int(leftMax), profHigh, bar_index-1, profLow, chart.fg_color, boxWid, line.style_dashed, bgcolor=color.rgb(0,0,0,100))
        poc = pocLevel()
        [val, vah] = valueLevels(poc)
        if showPoc
            line.new(int(leftMax), poc, bar_index-1, poc, color=pocCol, width=pocWid)
        if showVA
            line.new(int(leftMax), vah, bar_index-1, vah, color=vahCol, width=vahWid)
            line.new(int(leftMax), val, bar_index-1, val, color=valCol, width=valWid)
 
 
    //if update
    //    resetProfile(true)
 
drawCurZone(update) =>
    var line pocLine = na
    var line vahLine = na
    var line valLine = na
    var box outBox = na
 
    var redBoxes = array.new_box(array.size(vpRed), na)
    var greenBoxes = array.new_box(array.size(vpRed), na)
 
    if bar_index > lookback and update and array.sum(vpGreen) + array.sum(vpRed) > 0
        //Clearing the previous boxes and array
        if not na(pocLine)
            line.delete(pocLine)
        if not na(vahLine)
            line.delete(vahLine)
        if not na(valLine)
            line.delete(valLine)
        if not na(outBox)
            box.delete(outBox)
 
        for i = 0 to array.size(redBoxes) - 1
            if not na(array.get(redBoxes, i))
                box.delete(array.get(redBoxes, i))
                box.delete(array.get(greenBoxes, i))
 
 
        gap = (profHigh - profLow) / resolution
        float leftMax = bar_index[lookback]
        float rightMax = bar_index[int(lookback / 1.4)]
        float rightMaxVol = array.max(vpGreen)+array.max(vpRed)
        float buffer = gap / 10
        if showProf
            for i = 0 to array.size(vpRed) - 1
                greenEnd = int(leftMax + (rightMax - leftMax) * (array.get(vpGreen, i) / rightMaxVol))
                redEnd = int(greenEnd + (rightMax - leftMax) * (array.get(vpRed, i) / rightMaxVol))
                array.set(greenBoxes, i, box.new(int(leftMax), array.get(zoneBounds, i) - buffer, greenEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bullCol, border_width=0))
                array.set(redBoxes, i, box.new(greenEnd, array.get(zoneBounds, i) - buffer, redEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bearCol, border_width=0))
        outBox := box.new(int(leftMax), profHigh, bar_index-1, profLow, chart.fg_color, boxWid, line.style_dashed, bgcolor=color.rgb(0,0,0,100))
 
        poc = pocLevel()
        [val, vah] = valueLevels(poc)
        if showPoc
            line.new(int(leftMax), poc, bar_index-1, poc, color=pocCol, width=pocWid)
        if showVA
            line.new(int(leftMax), vah, bar_index-1, vah, color=vahCol, width=vahWid)
            line.new(int(leftMax), val, bar_index-1, val, color=valCol, width=valWid)
 
combArray(arr1, arr2) =>
    out = array.copy(arr1)
    if array.size(arr2) > 0
        for i = 0 to array.size(arr2) - 1
            array.push(out, array.get(arr2, i))
    out
 
updateIntra(o, h, l, c, v) =>
    if array.size(o) > 0
        for i = 0 to array.size(o) - 1
            array.push(ltfOpen, array.get(o, i))
            array.push(ltfHigh,array.get(h, i))
            array.push(ltfLow,array.get(l, i))
            array.push(ltfClose,array.get(c, i))
            array.push(ltfVolume,array.get(v, i))
 
 
//==========================
//Calculations
//==========================
//Detecting different start dates
newDaily = dayofweek != dayofweek[1]
newWeekly = (dayofweek != dayofweek[1] + 1) and (dayofweek != dayofweek[1])
newMonthly = (dayofmonth != dayofmonth[1] + 1) and (dayofmonth != dayofmonth[1])
 
utcHour = hour(time(timeframe.period, '0000-2400', 'GMT'), 'GMT')
 
newTokyo = utcHour != utcHour[1] + 1 and utcHour != utcHour[1]
endTokyo = utcHour >= 9 and utcHour[1] < 9
 
newLondon = utcHour >= 7 and utcHour[1] < 7
endLondon = utcHour >= 16 and utcHour[1] < 16
 
newNewYork = utcHour >= 13 and utcHour[1] < 13
endNewYork = utcHour >= 22 and utcHour[1] < 22
 
newSession = switch sessionType
    'Tokyo' => newTokyo
    'London' => newLondon
    'New York' => newNewYork
    'Daily' => newDaily
    'Weekly' => newWeekly
    'Monthly' => newMonthly
 
zoneEnd = switch sessionType
    'Tokyo' => endTokyo
    'London' => endLondon
    'New York' => endNewYork
    'Daily' => newDaily
    'Weekly' => newWeekly
    'Monthly' => newMonthly
 
//Re calculating and drawing zones
calcSession(zoneEnd or (barstate.islast and showCur))
drawNewZone(zoneEnd)
drawCurZone(barstate.islast and not zoneEnd and showCur and activeZone)
 
//Reseting profie at start of new zone
resetProfile(newSession)
 
//Updating data arrays
updateIntra(dO, dH, dL, dC, dV)
 
//Reseting zone start value
if zoneEnd
    activeZone := false
 
if newSession
    zoneStart := bar_index
    activeZone := true

← 返回列表