VWAP filtered MACD Bars with positive MACD histogram value and closing above VWAP are colored, long positions should be taken in areas made of those bars.
Similarly, bars with negative MACD histogram value and closing below VWAP are also colored, short positions should be taken there.
This indicator by default should be a part of your trend following trading system.
In the setting you can change colors
Above grow: positive and rising MACD histogram value
Above fall: positive and falling MACD histogram value
Below fall: negative and falling MACD histogram value
Below grow: negative and rising MACD histogram value
Cari dalam skrip untuk "bar"
MACD Aspray Hybrid Bars (teal/red) = raw momentum (Aspray Histogram).
Teal line = smooth curve of the histogram (Aspray Line).
Orange line = 9-EMA of that line (new signal).
Zero line for reference.
Bar numberAdds a number above the last 50 candles. Candle 1 is always the most recent.
Can be useful when teaching people onlinet. Now they can just ask « what’s candle number 20 » instead of « what’s with that narrow range candle next to the big one to the left… no not that one, the other one »
Bars pattern MLThis script implements a K-Nearest Neighbors (KNN)-based machine learning model to predict future price movements in financial markets. It analyzes past price action using Euclidean distance and selects the most similar historical patterns to estimate future price changes. Unlike traditional KNN implementations, this approach optimizes distance calculations by maintaining a dynamically updated list of the closest neighbors, ensuring efficient selection without the need for sorting. The model generates a forecasted price trajectory based on incremental predictions, which are visualized on the chart using polylines for better interpretability.
Volume HighlightBar colouring: this indicator is simple but effective, it repaints higher than normal candles a certain colour (by default gold/yellow) it helps to know what are valuable areas to trade around for longs and shorts.
Changing the volume multiplier manually helps you to screen volume relevant to the timeframe you are trading on.
For example, some charts 1min the best filter/setting would be 12-35 multiplier where others like btc 1-4 hourly, the filter/setting might be 8-12.
The key is having only the highest/most relevant 3-4 volume candles showing as they often represent supports and resistances.
Pivot Points And Breakout Price Action With LuckyNickVaBar Color Candle Aligned with pivot points swing high and swing lows For Those Who Are Familiar with Trading The Breakouts Of Highs & Lows Of Structure. Pivots are said to be key areas in the market where price shows heavy reaction to where reversals make occur. At these points there are swing Highs & swing lows that traders may be able to find opportunity in the market. This Script is a combination of pivot points and Barcolor signals for the breakout.
Koalafied Volume Extension Bar colours based on extensions from volume Z-Score. Large volume candles can often signal exhaustion or show market strength in reversals or breakouts. Candles not supported by rising volume are coloured black while those that are retain their colouring.
Bars CharacteristicsThis code is for defining or explaining market conditions via micro trend and the characterized bars.
lines 5,6: show the conditions for a normal trend, means market can go in the direction that it has in the past.
lines 11,12: show the conditions for kind of the trend having cumulative energy itself, mean market can go in the same direction.
lines 18,19: show the conditions for kind of the trend having overbought/sold concept, means it's better exit from the market or to look for the other clues.
lines 24,25: show some kind of noise not a stable trend, it's better not to enter the market.
WhenWasThePriceAction
Bars of largest range (volatility)
* see moments of strongest price action immediately
* colored & upDown by candle color
* amplifier: you see only the bull runs, and subsequent dumps
Very nice on the 5 years scale of BITSTAMP:BTCUSD - nothing comparable to 2013 has happened yet.
Internals:
squared_range = pow(high-low, 2)
That is essentially it already. The rest are details:
* gauge with (in case of Bitcoin exponentially rising) price
* show in red for negative candles
* take even higher polynomial (than 2) to show only the very largest values
* allow some user input (but there is not much more that can be chosen here.)
Sorry for such a simple formula - but sometimes the easiest things are powerful.
Please give feedback. www.tradingview.com and/or in the cryptocurrency chat. Thanks.
Bars Since the last RSI ExtremeThis is something Jamie Saettele pointed out. Gold has been in 'neutral' RSI territory for its longest stretch in four years. It's coiling up for its next major move.
Heikin Ashi Overlay SuiteHeikin Ashi Overlay Suite is designed to give traders more control and clarity when working with Heikin Ashi candles — whether you're analyzing trend strength, reducing chart noise, or simply improving your visual read of market momentum. It works by layering multiple types of HA overlays and color systems on top of your standard candlestick chart — without switching chart types. With dynamic gradient coloring, smoothing options, and a predictive line tool, this script helps you see not just what the current trend is, but how strong it is, and what it would take to reverse it.
Heikin Ashi candles help reduce noise but this script goes further by:
➡️adding color intelligence that shows trend strength using a streak counter
➡️uses smoothing logic to clean up chop and whipsaws
➡️introduces a predictive close line — a subtle but powerful guide for anticipating trend flips before they happen
Everything is configurable: colors, candle sources, overlays, predictive tools, and line styles. It’s built for traders who want visual speed, but don’t want to sacrifice signal quality.
At its core, the script offers two powerful dropdown controls:
💥HA Color Scheme (Colors Regular Candles) — Applies Heikin Ashi-derived coloring to your regular candles based on trend direction or streak strength. This gives you instant visual context without switching to a separate chart type.
💥HA Candle Overlay Mode — Overlays actual Heikin Ashi-style candles directly on top of your chart, using your preferred source:
➡️Custom HA candles using internal formula logic
➡️TradingView’s built-in Heikin Ashi source with your own colors
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
🎨 Custom + Gradient HA Coloring🎨
See trend strength at a glance:
➡️1–4 bar streaks → lighter tone
➡️5–8 bars → medium tone
➡️9+ bars → bold tone, ideal for momentum-based entries, exits, or scaling strategies
→ Choose from:
➡️Your own custom color set
➡️A simple 2-color base mode
➡️Or a 3-level gradient for progressive trend analysis (using the streak counter)
🏛️ TradingView Official Heikin Ashi Overlay
Prefer native HA candles but want your own colors?
This mode plots TradingView's Heikin Ashi source, with your personal bullish/bearish color scheme.
➡️Ensures consistency with built-in charts while still leveraging your visual style.
🌊 Smoothed Heikin Ashi Candles — Clarity in Chaos🌊
These aren’t your standard HA candles. Smoothed Heikin Ashi uses a two-step EMA process to transform chaotic price action into a cleaner, slower-moving trend structure:
🔹 First, it smooths the raw OHLC data using EMA — filtering out minor price fluctuations.
🔹 Then, it applies the Heikin Ashi transformation on top of the smoothed data.
🔹 Finally, it applies a second EMA smoothing pass to the HA values — creating ultra-smooth candles.
📈 What You See:
Trends appear more fluid and consistent.
Choppy ranges and fakeouts are visually suppressed.
Minor pullbacks within a trend are de-emphasized, helping you avoid premature exits.
🎯 Best For:
Swing traders looking to stay in positions longer.
Intraday traders dealing with volatile or noisy instruments.
Anyone who wants a "trend map" overlay without the distractions of raw price action.
✅ Reduces whipsaws
✅ Delivers high-contrast trend zones
✅ Makes reversals more visually apparent (but with a slight lag)
📍 Predictive Close Line📍
Shows where the real close must land to flip the current HA candle's color.
✅ Use it like predictive support/resistance
✅ Know if the trend is actually at risk
✅Visualize potential fakeouts or confirmation
Color-coded based on current HA direction (bullish, bearish, or neutral).
📈 Tick by tick & bar-to-bar Plots📈
Provides 2 plot types:
1)1 plot that tracks a bar tick by tick
2)another plot that tracks the close from bar to bar
For the bar to bar plot, you can choose between 2 options:
✅Full Plot — continuous line colored by HA trend
✅Recent Segments — color just the last few bars (configurable) to reduce chart clutter
✅ Customize width, number of bars, and visibility
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
📘 How to Use this script📘
Imagine you're watching a choppy 15-minute chart on a volatile crypto pair — price action is messy, and it’s hard to tell if a trend is forming or just noise.
Here’s how to cut through the chaos using Heikin Ashi Overlay Suite:
🔹 Step 1: Enable "Smoothed HA Candles"
Start by turning on the smoothed candles. You’ll immediately notice the noise fades, and broader directional moves become easier to follow. It's like switching from static to clean trend zones.
🧠 Why: Smoothed HA uses a double EMA process that filters out small reversals and lets larger moves stand out. Perfect for sideways or jittery charts.
🔹 Step 2: Watch the Color Gradient Build
As the smoothed candles begin to align in one direction, the gradient coloring (1–4, 5–8, 9+ streaks) gives you an at-a-glance visual of how strong the trend is.
✅ If you see 9+ same-colored candles? You’re likely in a mature trend.
✅ If it resets often? You’re in chop — consider staying out.
🔹 Step 3: Use the Predictive Close Line for Anticipation
Now here’s the edge — this line tells you where the candle would have to close to flip colors.
📉 If price is hovering just above it during a bullish run — momentum may be weakening.
📈 If price bounces off it — the trend may be strengthening.
This is excellent for confirming entries, exits, or spotting early warning signs.
🔹 Step 4: Switch Between Candle Modes as Needed
You can flip between:
✅ Custom HA: Gradient candles with your colors
✅ TradingView HA: The official source with your styling
✅ None: Just color regular candles using the HA logic
Use what fits your style — everything is modular.
🔹 Step 5: Tune It to Your Chart
Lastly, tweak streak thresholds (currently only can do this within the source code), smoothing lengths, and line styles to match your timeframe and strategy.
🎯 Tailor The Settings to Fit Your Trading Style🎯
🔹 🧪 Scalper (1–5 min charts)
If you’re trading fast intraday moves, you want quicker responsiveness and less lag.
Try these settings:
🔸Smoothing Lengths: Use lower values (e.g. len = 3, len2 = 5)
🔸Candle Mode: Use Custom HA or TV’s HA for real-time color flips
🔸Predictive Close Line: Great for ultra-fast anticipation of color reversals
🔸Line Mode: Use Recent Segments mode to track short bursts of trend
🔸Colors: Use high-contrast, opaque colors for clarity
✅ These settings help you catch micro-trends and flip signals faster, while still filtering out the worst of the noise.
🔹 🧪 Swing Trader (30m–4h charts and beyond)
If you’re looking for multi-hour or multi-day trend confirmation, prioritize clarity and staying in moves longer.
Recommended setup:
🔸Smoothing Lengths: Medium to high values (e.g. len = 8, len2 = 21)
🔸Candle Mode: Use Smoothed HA Candles to block out intrabar chop
🔸Gradient Colors: Enable to visualize trend maturity and strength
🔸Predictive Close Line: Helps confirm trend continuation or spot early reversals
🔸Line Mode: Use Full Plot Line for clean HA-based trend tracking
✅ These settings give you a calm, clean view of the bigger picture — ideal for holding positions longer and avoiding early exits.
🔧 This script isn’t just a chart overlay — it’s a visual trend engine.🔧
Ideal For:
🔶 Trend-followers who want clean, color-coded confirmation
🔶 Reversal traders spotting exhaustion via predictive flips
🔶 Scalpers filtering noise with lighter smoothing
🔶 Swing traders using smoothed visuals to hold longer
📌 Final Note
Heikin Ashi Overlay Pro is designed to help you see momentum, trend shifts, and market structure with greater clarity — not to predict price on its own. For best results:
✔️ Combine with support/resistance, moving averages, or price action patterns
✔️ Use Predictive Close as a confirmation tool, not a signal generator
✔️ Pair gradient colors with structure to gauge trend maturity
✔️ Always zoom out and check higher timeframes for context
🧠 Use this as part of a layered approach — not a standalone system.
🙏 Credits🙏
⚡HA logic based on SimpleCryptoLife
⚡Smoothed HA concept adapted from a script by Jackvmk
💡💡💡Turn logic into clarity. Structure into trades. And uncertainty into confidence.💡💡💡
MultiScalpMACDThis indicator, the "Custom MACD MTF," is an adaptive version of the classic Moving Average Convergence Divergence (MACD) that automatically adjusts its parameters based on the chart's timeframe. It is designed to provide more fine-tuned momentum readings for traders who focus on specific intraday timeframes.
Overview
The "Custom MACD MTF" modifies the standard MACD calculation by applying unique settings for the 5-minute, 15-minute, and 1-hour charts. For all other timeframes, it reverts to the user-defined default values. This dynamic adjustment allows the indicator to better reflect the momentum characteristics of different trading sessions without requiring manual changes from the user.
Key Features
Adaptive Parameters: The indicator automatically uses optimized MACD settings for popular timeframes:
5-minute: Fast Length = 3, Slow Length = 10, Signal Length = 16
15-minute: Fast Length = 8, Slow Length = 17, Signal Length = 9
1-hour: Fast Length = 12, Slow Length = 26, Signal Length = 9
Momentum-Based Histogram: The histogram bars are colored to provide a clear visual cue about changes in momentum. A light gray bar indicates that momentum is increasing (the current bar is higher than the previous one), while a dark gray bar indicates that momentum is decreasing.
Clear Visual Plots: The indicator plots a pink MACD line, a black signal line, and a gray zero line for easy interpretation of crossovers and trend direction.
How to Interpret
This indicator can be used in the same way as a traditional MACD, but with added sensitivity on the specified timeframes. Traders can look for MACD and signal line crossovers, zero-line crosses, and divergences to identify potential trade signals. The histogram's color change provides an early warning that momentum is either accelerating or decelerating, which can precede a change in price direction.
Settings
Fast Length (Default): The default fast EMA period used for all timeframes except 5m, 15m, and 1h.
Slow Length (Default): The default slow EMA period used for all timeframes except 5m, 15m, and 1h.
Signal Length (Default): The default signal line EMA period used for all timeframes except 5m, 15m, and 1h.
Source: The price source for the MACD calculation (default is Close).
Price Action Concepts [RUDYINDICATOR]/// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) creativecommons.org
// © RUDYBANK INDICATOR - formerly know as RUDY INDICATOR
//@version=5
indicator("Price Action Concepts ", shorttitle = "RUDYINDICATOR-V1
- Price Action RUDYINDICATOR ", overlay = true, max_lines_count = 500, max_labels_count = 500, max_boxes_count = 500, max_bars_back = 500, max_polylines_count = 100)
//-----------------------------------------------------------------------------{
//Boolean set
//-----------------------------------------------------------------------------{
s_BOS = 0
s_CHoCH = 1
i_BOS = 2
i_CHoCH = 3
i_pp_CHoCH = 4
green_candle = 5
red_candle = 6
s_CHoCHP = 7
i_CHoCHP = 8
boolean =
array.from(
false
, false
, false
, false
, false
, false
, false
, false
, false
)
//-----------------------------------------------------------------------------{
// User inputs
//-----------------------------------------------------------------------------{
show_swing_ms = input.string ("All" , "Swing " , inline = "1", group = "MARKET STRUCTURE" , options = )
show_internal_ms = input.string ("All" , "Internal " , inline = "2", group = "MARKET STRUCTURE" , options = )
internal_r_lookback = input.int (5 , "" , inline = "2", group = "MARKET STRUCTURE" , minval = 2)
swing_r_lookback = input.int (50 , "" , inline = "1", group = "MARKET STRUCTURE" , minval = 2)
ms_mode = input.string ("Manual" , "Market Structure Mode" , inline = "a", group = "MARKET STRUCTURE" , tooltip = " Use selected lenght\n Use automatic lenght" ,options = )
show_mtf_str = input.bool (true , "MTF Scanner" , inline = "9", group = "MARKET STRUCTURE" , tooltip = "Display Multi-Timeframe Market Structure Trend Directions. Green = Bullish. Red = Bearish")
show_eql = input.bool (false , "Show EQH/EQL" , inline = "6", group = "MARKET STRUCTURE")
plotcandle_bool = input.bool (false , "Plotcandle" , inline = "3", group = "MARKET STRUCTURE" , tooltip = "Displays a cleaner colored candlestick chart in place of the default candles. (requires hiding the current ticker candles)")
barcolor_bool = input.bool (false , "Bar Color" , inline = "4", group = "MARKET STRUCTURE" , tooltip = "Color the candle bodies according to market strucutre trend")
i_ms_up_BOS = input.color (#089981 , "" , inline = "2", group = "MARKET STRUCTURE")
i_ms_dn_BOS = input.color (#f23645 , "" , inline = "2", group = "MARKET STRUCTURE")
s_ms_up_BOS = input.color (#089981 , "" , inline = "1", group = "MARKET STRUCTURE")
s_ms_dn_BOS = input.color (#f23645 , "" , inline = "1", group = "MARKET STRUCTURE")
lvl_daily = input.bool (false , "Day " , inline = "1", group = "HIGHS & LOWS MTF")
lvl_weekly = input.bool (false , "Week " , inline = "2", group = "HIGHS & LOWS MTF")
lvl_monthly = input.bool (false , "Month" , inline = "3", group = "HIGHS & LOWS MTF")
lvl_yearly = input.bool (false , "Year " , inline = "4", group = "HIGHS & LOWS MTF")
css_d = input.color (color.blue , "" , inline = "1", group = "HIGHS & LOWS MTF")
css_w = input.color (color.blue , "" , inline = "2", group = "HIGHS & LOWS MTF")
css_m = input.color (color.blue , "" , inline = "3", group = "HIGHS & LOWS MTF")
css_y = input.color (color.blue , "" , inline = "4", group = "HIGHS & LOWS MTF")
s_d = input.string ('⎯⎯⎯' , '' , inline = '1', group = 'HIGHS & LOWS MTF' , options = )
s_w = input.string ('⎯⎯⎯' , '' , inline = '2', group = 'HIGHS & LOWS MTF' , options = )
s_m = input.string ('⎯⎯⎯' , '' , inline = '3', group = 'HIGHS & LOWS MTF' , options = )
s_y = input.string ('⎯⎯⎯' , '' , inline = '4', group = 'HIGHS & LOWS MTF' , options = )
ob_show = input.bool (true , "Show Last " , inline = "1", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Display volumetric order blocks on the chart \n\n Ammount of volumetric order blocks to show")
ob_num = input.int (5 , "" , inline = "1", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Orderblocks number", minval = 1, maxval = 10)
ob_metrics_show = input.bool (true , "Internal Buy/Sell Activity" , inline = "2", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Display volume metrics that have formed the orderblock")
css_metric_up = input.color (color.new(#089981, 50) , " " , inline = "2", group = "VOLUMETRIC ORDER BLOCKS")
css_metric_dn = input.color (color.new(#f23645 , 50) , "" , inline = "2", group = "VOLUMETRIC ORDER BLOCKS")
ob_swings = input.bool (false , "Swing Order Blocks" , inline = "a", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Display swing volumetric order blocks")
css_swing_up = input.color (color.new(color.gray , 90) , " " , inline = "a", group = "VOLUMETRIC ORDER BLOCKS")
css_swing_dn = input.color (color.new(color.silver, 90) , "" , inline = "a", group = "VOLUMETRIC ORDER BLOCKS")
ob_filter = input.string ("None" , "Filtering " , inline = "d", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Filter out volumetric order blocks by BOS/CHoCH/CHoCH+", options = )
ob_mitigation = input.string ("Absolute" , "Mitigation " , inline = "4", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Trigger to remove volumetric order blocks", options = )
ob_pos = input.string ("Precise" , "Positioning " , inline = "k", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Position of the Order Block\n Cover the whole candle\n Cover half candle\n Adjust to volatility\n Same as Accurate but more precise", options = )
use_grayscale = input.bool (false , "Grayscale" , inline = "6", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Use gray as basic order blocks color")
use_show_metric = input.bool (true , "Show Metrics" , inline = "7", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Show volume associated with the orderblock and his relevance")
use_middle_line = input.bool (true , "Show Middle-Line" , inline = "8", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Show mid-line order blocks")
use_overlap = input.bool (true , "Hide Overlap" , inline = "9", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Hide overlapping order blocks")
use_overlap_method = input.string ("Previous" , "Overlap Method " , inline = "Z", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = " Preserve the most recent volumetric order blocks\n\n Preserve the previous volumetric order blocks", options = )
ob_bull_css = input.color (color.new(#089981 , 90) , "" , inline = "1", group = "VOLUMETRIC ORDER BLOCKS")
ob_bear_css = input.color (color.new(#f23645 , 90) , "" , inline = "1", group = "VOLUMETRIC ORDER BLOCKS")
show_acc_dist_zone = input.bool (false , "" , inline = "1", group = "Accumulation And Distribution")
zone_mode = input.string ("Fast" , "" , inline = "1", group = "Accumulation And Distribution" , tooltip = " Find small zone pattern formation\n Find bigger zone pattern formation" ,options = )
acc_css = input.color (color.new(#089981 , 60) , "" , inline = "1", group = "Accumulation And Distribution")
dist_css = input.color (color.new(#f23645 , 60) , "" , inline = "1", group = "Accumulation And Distribution")
show_lbl = input.bool (false , "Show swing point" , inline = "1", group = "High and Low" , tooltip = "Display swing point")
show_mtb = input.bool (false , "Show High/Low/Equilibrium" , inline = "2", group = "High and Low" , tooltip = "Display Strong/Weak High And Low and Equilibrium")
toplvl = input.color (color.red , "Premium Zone " , inline = "3", group = "High and Low")
midlvl = input.color (color.gray , "Equilibrium Zone" , inline = "4", group = "High and Low")
btmlvl = input.color (#089981 , "Discount Zone " , inline = "5", group = "High and Low")
fvg_enable = input.bool (false , " " , inline = "1", group = "FAIR VALUE GAP" , tooltip = "Display fair value gap")
what_fvg = input.string ("FVG" , "" , inline = "1", group = "FAIR VALUE GAP" , tooltip = "Display fair value gap", options = )
fvg_num = input.int (5 , "Show Last " , inline = "1a", group = "FAIR VALUE GAP" , tooltip = "Number of fvg to show")
fvg_upcss = input.color (color.new(#089981, 80) , "" , inline = "1", group = "FAIR VALUE GAP")
fvg_dncss = input.color (color.new(color.red , 80) , "" , inline = "1", group = "FAIR VALUE GAP")
fvg_extend = input.int (10 , "Extend FVG" , inline = "2", group = "FAIR VALUE GAP" , tooltip = "Extend the display of the FVG.")
fvg_src = input.string ("Close" , "Mitigation " , inline = "3", group = "FAIR VALUE GAP" , tooltip = " Use the close of the body as trigger\n\n Use the extreme point of the body as trigger", options = )
fvg_tf = input.timeframe ("" , "Timeframe " , inline = "4", group = "FAIR VALUE GAP" , tooltip = "Timeframe of the fair value gap")
t = color.t (ob_bull_css)
invcol = color.new (color.white , 100)
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - UDT }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
type bar
float o = open
float c = close
float h = high
float l = low
float v = volume
int n = bar_index
int t = time
type Zphl
line top
line bottom
label top_label
label bottom_label
bool stopcross
bool sbottomcross
bool itopcross
bool ibottomcross
string txtup
string txtdn
float topy
float bottomy
float topx
float bottomx
float tup
float tdn
int tupx
int tdnx
float itopy
float itopx
float ibottomy
float ibottomx
float uV
float dV
type FVG
box box
line ln
bool bull
float top
float btm
int left
int right
type ms
float p
int n
float l
type msDraw
int n
float p
color css
string txt
bool bull
type obC
float top
float btm
int left
float avg
float dV
float cV
int wM
int blVP
int brVP
int dir
float h
float l
int n
type obD
box ob
box eOB
box blB
box brB
line mL
type zone
chart.point points
float p
int c
int t
type hqlzone
box pbx
box ebx
box lbx
label plb
label elb
label lbl
type ehl
float pt
int t
float pb
int b
type pattern
string found = "None"
bool isfound = false
int period = 0
bool bull = false
type alerts
bool chochswing = false
bool chochplusswing = false
bool swingbos = false
bool chochplus = false
bool choch = false
bool bos = false
bool equal = false
bool ob = false
bool swingob = false
bool zone = false
bool fvg = false
bool obtouch = false
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - End }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - General Setup }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
bar b = bar.new()
var pattern p = pattern.new()
alerts blalert = alerts.new()
alerts bralert = alerts.new()
if p.isfound
p.period += 1
if p.period == 50
p.period := 0
p.found := "None"
p.isfound := false
p.bull := na
switch
b.c > b.o => boolean.set(green_candle, true)
b.c < b.o => boolean.set(red_candle , true)
f_zscore(src, lookback) =>
(src - ta.sma(src, lookback)) / ta.stdev(src, lookback)
var int iLen = internal_r_lookback
var int sLen = swing_r_lookback
vv = f_zscore(((close - close ) / close ) * 100,iLen)
if ms_mode == "Dynamic"
switch
vv >= 1.5 or vv <= -1.5 => iLen := 10
vv >= 1.6 or vv <= -1.6 => iLen := 9
vv >= 1.7 or vv <= -1.7 => iLen := 8
vv >= 1.8 or vv <= -1.8 => iLen := 7
vv >= 1.9 or vv <= -1.9 => iLen := 6
vv >= 2.0 or vv <= -2.0 => iLen := 5
=> iLen
var msline = array.new(0)
iH = ta.pivothigh(high, iLen, iLen)
sH = ta.pivothigh(high, sLen, sLen)
iL = ta.pivotlow (low , iLen, iLen)
sL = ta.pivotlow (low , sLen, sLen)
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - End }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - ARRAYS }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
hl () =>
= request.security(syminfo.tickerid , 'D' , hl() , lookahead = barmerge.lookahead_on)
= request.security(syminfo.tickerid , 'W' , hl() , lookahead = barmerge.lookahead_on)
= request.security(syminfo.tickerid , 'M' , hl() , lookahead = barmerge.lookahead_on)
= request.security(syminfo.tickerid , '12M', hl() , lookahead = barmerge.lookahead_on)
lstyle(style) =>
out = switch style
'⎯⎯⎯' => line.style_solid
'----' => line.style_dashed
'····' => line.style_dotted
mtfphl(h, l ,tf ,css, pdhl_style) =>
var line hl = line.new(
na
, na
, na
, na
, xloc = xloc.bar_time
, color = css
, style = lstyle(pdhl_style)
)
var line ll = line.new(
na
, na
, na
, na
, xloc = xloc.bar_time
, color = css
, style = lstyle(pdhl_style)
)
var label lbl = label.new(
na
, na
, xloc = xloc.bar_time
, text = str.format('P{0}L', tf)
, color = invcol
, textcolor = css
, size = size.small
, style = label.style_label_left
)
var label hlb = label.new(
na
, na
, xloc = xloc.bar_time
, text = str.format('P{0}H', tf)
, color = invcol
, textcolor = css
, size = size.small
, style = label.style_label_left
)
hy = ta.valuewhen(h != h , h , 1)
hx = ta.valuewhen(h == high , time , 1)
ly = ta.valuewhen(l != l , l , 1)
lx = ta.valuewhen(l == low , time , 1)
if barstate.islast
extension = time + (time - time ) * 50
line.set_xy1(hl , hx , hy)
line.set_xy2(hl , extension , hy)
label.set_xy(hlb, extension , hy)
line.set_xy1(ll , lx , ly)
line.set_xy2(ll , extension , ly)
label.set_xy(lbl, extension , ly)
if lvl_daily
mtfphl(pdh , pdl , 'D' , css_d, s_d)
if lvl_weekly
mtfphl(pwh , pwl , 'W' , css_w, s_w)
if lvl_monthly
mtfphl(pmh , pml, 'M' , css_m, s_m)
if lvl_yearly
mtfphl(pyh , pyl , '12M', css_y, s_y)
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - End }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - Market Structure }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
method darkcss(color css, float factor, bool bull) =>
blue = color.b(css) * (1 - factor)
red = color.r(css) * (1 - factor)
green = color.g(css) * (1 - factor)
color.rgb(red, green, blue, 0)
method f_line(msDraw d, size, style) =>
var line id = na
var label lbl = na
id := line.new(
d.n
, d.p
, b.n
, d.p
, color = d.css
, width = 1
, style = style
)
if msline.size() >= 250
line.delete(msline.shift())
msline.push(id)
lbl := label.new(
int(math.avg(d.n, b.n))
, d.p
, d.txt
, color = invcol
, textcolor = d.css
, style = d.bull ? label.style_label_down : label.style_label_up
, size = size
, text_font_family = font.family_monospace
)
structure(bool mtf) =>
msDraw drw = na
bool isdrw = false
bool isdrwS = false
var color css = na
var color icss = na
var int itrend = 0
var int trend = 0
bool bull_ob = false
bool bear_ob = false
bool s_bull_ob = false
bool s_bear_ob = false
n = bar_index
var ms up = ms.new(
array.new()
, array.new< int >()
, array.new()
)
var ms dn = ms.new(
array.new()
, array.new< int >()
, array.new()
)
var ms sup = ms.new(
array.new()
, array.new< int >()
, array.new()
)
var ms sdn = ms.new(
array.new()
, array.new< int >()
, array.new()
)
switch show_swing_ms
"All" => boolean.set(s_BOS , true ), boolean.set(s_CHoCH, true ) , boolean.set(s_CHoCHP, true )
"CHoCH" => boolean.set(s_BOS , false), boolean.set(s_CHoCH, true ) , boolean.set(s_CHoCHP, false )
"CHoCH+" => boolean.set(s_BOS , false), boolean.set(s_CHoCH, false) , boolean.set(s_CHoCHP, true )
"BOS" => boolean.set(s_BOS , true ), boolean.set(s_CHoCH, false) , boolean.set(s_CHoCHP, false )
"None" => boolean.set(s_BOS , false), boolean.set(s_CHoCH, false) , boolean.set(s_CHoCHP, false )
=> na
switch show_internal_ms
"All" => boolean.set(i_BOS, true ), boolean.set(i_CHoCH, true ), boolean.set(i_CHoCHP, true )
"CHoCH" => boolean.set(i_BOS, false), boolean.set(i_CHoCH, true ), boolean.set(i_CHoCHP, false)
"CHoCH+" => boolean.set(i_BOS, false), boolean.set(i_CHoCH, false ), boolean.set(i_CHoCHP, true )
"BOS" => boolean.set(i_BOS, true ), boolean.set(i_CHoCH, false ), boolean.set(i_CHoCHP, false)
"None" => boolean.set(i_BOS, false), boolean.set(i_CHoCH, false ), boolean.set(i_CHoCHP, false)
=> na
switch
iH =>
up.p.unshift(b.h )
up.l.unshift(b.h )
up.n.unshift(n )
iL =>
dn.p.unshift(b.l )
dn.l.unshift(b.l )
dn.n.unshift(n )
sL =>
sdn.p.unshift(b.l )
sdn.l.unshift(b.l )
sdn.n.unshift(n )
sH =>
sup.p.unshift(b.h )
sup.l.unshift(b.h )
sup.n.unshift(n )
// INTERNAL BULLISH STRUCTURE
if up.p.size() > 0 and dn.l.size() > 1
if ta.crossover(b.c, up.p.first())
bool CHoCH = na
string txt = na
if itrend < 0
CHoCH := true
switch
not CHoCH =>
txt := "BOS"
css := i_ms_up_BOS
blalert.bos := true
if boolean.get(i_BOS) and mtf == false and na(drw)
isdrw := true
drw := msDraw.new(
up.n.first()
, up.p.first()
, i_ms_up_BOS
, txt
, true
)
CHoCH =>
dn.l.first() > dn.l.get(1) ? blalert.chochplus : blalert.choch
txt := dn.l.first() > dn.l.get(1) ? "CHoCH+" : "CHoCH"
css := i_ms_up_BOS.darkcss(0.25, true)
if (dn.l.first() > dn.l.get(1) ? boolean.get(i_CHoCHP) : boolean.get(i_CHoCH)) and mtf == false and na(drw)
isdrw := true
drw := msDraw.new(
up.n.first()
, up.p.first()
, i_ms_up_BOS.darkcss(0.25, true)
, txt
, true
)
if mtf == false
switch
ob_filter == "None" => bull_ob := true
ob_filter == "BOS" and txt == "BOS" => bull_ob := true
ob_filter == "CHoCH" and txt == "CHoCH" => bull_ob := true
ob_filter == "CHoCH+" and txt == "CHoCH+" => bull_ob := true
itrend := 1
up.n.clear()
up.p.clear()
// INTERNAL BEARISH STRUCTURE
if dn.p.size() > 0 and up.l.size() > 1
if ta.crossunder(b.c, dn.p.first())
bool CHoCH = na
string txt = na
if itrend > 0
CHoCH := true
switch
not CHoCH =>
bralert.bos := true
txt := "BOS"
css := i_ms_dn_BOS
if boolean.get(i_BOS) and mtf == false and na(drw)
isdrw := true
drw := msDraw.new(
dn.n.first()
, dn.p.first()
, i_ms_dn_BOS
, txt
, false
)
CHoCH =>
if up.l.first() < up.l.get(1)
bralert.chochplus := true
else
bralert.choch := true
txt := up.l.first() < up.l.get(1) ? "CHoCH+" : "CHoCH"
css := i_ms_dn_BOS.darkcss(0.25, false)
if (up.l.first() < up.l.get(1) ? boolean.get(i_CHoCHP) : boolean.get(i_CHoCH)) and mtf == false and na(drw)
isdrw := true
drw := msDraw.new(
dn.n.first()
, dn.p.first()
, i_ms_dn_BOS.darkcss(0.25, false)
, txt
, false
)
if mtf == false
switch
ob_filter == "None" => bear_ob := true
ob_filter == "BOS" and txt == "BOS" => bear_ob := true
ob_filter == "CHoCH" and txt == "CHoCH" => bear_ob := true
ob_filter == "CHoCH+" and txt == "CHoCH+" => bear_ob := true
itrend := -1
dn.n.clear()
dn.p.clear()
// SWING BULLISH STRUCTURE
if sup.p.size() > 0 and sdn.l.size() > 1
if ta.crossover(b.c, sup.p.first())
bool CHoCH = na
string txt = na
if trend < 0
CHoCH := true
switch
not CHoCH =>
blalert.swingbos := true
txt := "BOS"
icss := s_ms_up_BOS
if boolean.get(s_BOS) and mtf == false and na(drw)
isdrwS := true
drw := msDraw.new(
sup.n.first()
, sup.p.first()
, s_ms_up_BOS
, txt
, true
)
CHoCH =>
if sdn.l.first() > sdn.l.get(1)
blalert.chochplusswing := true
else
blalert.chochswing := true
txt := sdn.l.first() > sdn.l.get(1) ? "CHoCH+" : "CHoCH"
icss := s_ms_up_BOS.darkcss(0.25, true)
if (sdn.l.first() > sdn.l.get(1) ? boolean.get(s_CHoCHP) : boolean.get(s_CHoCH)) and mtf == false and na(drw)
isdrwS := true
drw := msDraw.new(
sup.n.first()
, sup.p.first()
, s_ms_up_BOS.darkcss(0.25, true)
, txt
, true
)
if mtf == false
switch
ob_filter == "None" => s_bull_ob := true
ob_filter == "BOS" and txt == "BOS" => s_bull_ob := true
ob_filter == "CHoCH" and txt == "CHoCH" => s_bull_ob := true
ob_filter == "CHoCH+" and txt == "CHoCH+" => s_bull_ob := true
trend := 1
sup.n.clear()
sup.p.clear()
// SWING BEARISH STRUCTURE
if sdn.p.size() > 0 and sup.l.size() > 1
if ta.crossunder(b.c, sdn.p.first())
bool CHoCH = na
string txt = na
if trend > 0
CHoCH := true
switch
not CHoCH =>
bralert.swingbos := true
txt := "BOS"
icss := s_ms_dn_BOS
if boolean.get(s_BOS) and mtf == false and na(drw)
isdrwS := true
drw := msDraw.new(
sdn.n.first()
, sdn.p.first()
, s_ms_dn_BOS
, txt
, false
)
CHoCH =>
if sup.l.first() < sup.l.get(1)
bralert.chochplusswing := true
else
bralert.chochswing := true
txt := sup.l.first() < sup.l.get(1) ? "CHoCH+" : "CHoCH"
icss := s_ms_dn_BOS.darkcss(0.25, false)
if (sup.l.first() < sup.l.get(1) ? boolean.get(s_CHoCHP) : boolean.get(s_CHoCH)) and mtf == false and na(drw)
isdrwS := true
drw := msDraw.new(
sdn.n.first()
, sdn.p.first()
, s_ms_dn_BOS.darkcss(0.25, false)
, txt
, false
)
if mtf == false
switch
ob_filter == "None" => s_bear_ob := true
ob_filter == "BOS" and txt == "BOS" => s_bear_ob := true
ob_filter == "CHoCH" and txt == "CHoCH" => s_bear_ob := true
ob_filter == "CHoCH+" and txt == "CHoCH+" => s_bear_ob := true
trend := -1
sdn.n.clear()
sdn.p.clear()
= structure(false)
if isdrw
f_line(drw, size.small, line.style_dashed)
if isdrwS
f_line(drw, size.small, line.style_solid)
= request.security("", "15" , structure(true))
= request.security("", "60" , structure(true))
= request.security("", "240" , structure(true))
= request.security("", "1440" , structure(true))
if show_mtf_str
var tab = table.new(position = position.top_right, columns = 10, rows = 10, bgcolor = na, frame_color = color.rgb(54, 58, 69, 0), frame_width = 1, border_color = color.rgb(54, 58, 69, 100), border_width = 1)
table.cell(tab, 0, 1, text = "15" , text_color = color.silver, text_halign = text.align_center, text_size = size.normal, bgcolor = chart.bg_color, text_font_family = font.family_monospace, width = 2)
table.cell(tab, 0, 2, text = "1H" , text_color = color.silver, text_halign = text.align_center, text_size = size.normal, bgcolor = chart.bg_color, text_font_family = font.family_monospace, width = 2)
table.cell(tab, 0, 3, text = "4H" , text_color = color.silver, text_halign = text.align_center, text_size = size.normal, bgcolor = chart.bg_color, text_font_family = font.family_monospace, width = 2)
table.cell(tab, 0, 4, text = "1D" , text_color = color.silver, text_halign = text.align_center, text_size = size.normal, bgcolor = chart.bg_color, text_font_family = font.family_monospace, width = 2)
table.cell(tab, 1, 1, text = itrend15 == 1 ? "BULLISH" : itrend15 == -1 ? "BEARISH" : na , text_halign = text.align_center, text_size = size.normal, text_color = itrend15 == 1 ? i_ms_up_BOS.darkcss(-0.25, true) : itrend15 == -1 ? i_ms_dn_BOS.darkcss(0.25, false) : color.gray, bgcolor = chart.bg_color, text_font_family = font.family_monospace)
table.cell(tab, 1, 2, text = itrend1H == 1 ? "BULLISH" : itrend1H == -1 ? "BEARISH" : na , text_halign = text.align_center, text_size = size.normal, text_color = itrend1H == 1 ? i_ms_up_BOS.darkcss(-0.25, true) : itrend1H == -1 ? i_ms_dn_BOS.darkcss(0.25, false) : color.gray, bgcolor = chart.bg_color, text_font_family = font.family_monospace)
table.cell(tab, 1, 3, text = itrend4H == 1 ? "BULLISH" : itrend4H == -1 ? "BEARISH" : na , text_halign = text.align_center, text_size = size.normal, text_color = itrend4H == 1 ? i_ms_up_BOS.darkcss(-0.25, true) : itrend4H == -1 ? i_ms_dn_BOS.darkcss(0.25, false) : color.gray, bgcolor = chart.bg_color, text_font_family = font.family_monospace)
table.cell(tab, 1, 4, text = itrend1D == 1 ? "BULLISH" : itrend1D == -1 ? "BEARISH" : na , text_halign = text.align_center, text_size = size.normal, text_color = itrend1D == 1 ? i_ms_up_BOS.darkcss(-0.25, true) : itrend1D == -1 ? i_ms_dn_BOS.darkcss(0.25, false) : color.gray, bgcolor = chart.bg_color, text_font_family = font.family_monospace)
table.cell(tab, 0, 5, text = "Detected Pattern", text_halign = text.align_center, text_size = size.normal, text_color = color.silver, bgcolor = chart.bg_color, text_font_family = font.family_monospace)
table.cell(tab, 0, 6, text = p.found, text_halign = text.align_center, text_size = size.normal, text_color = na(p.bull) ? color.white : p.bull ? i_ms_up_BOS.darkcss(-0.25, true) : p.bull == false ? i_ms_dn_BOS.darkcss(0.25, false) : na, bgcolor = chart.bg_color, text_font_family = font.family_monospace)
table.merge_cells(tab, 0, 5, 1, 5)
table.merge_cells(tab, 0, 6, 1, 6)
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - End }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - Strong/Weak High/Low And Equilibrium }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
var phl = Zphl.new(
na
, na
, label.new(na , na , color = invcol , textcolor = i_ms_dn_BOS , style = label.style_label_down , size = size.tiny , text = "")
, label.new(na , na , color = invcol , textcolor = i_ms_up_BOS , style = label.style_label_up , size = size.tiny , text = "")
, true
, true
, true
, true
, ""
, ""
, 0
, 0
, 0
, 0
, high
, low
, 0
, 0
, 0
, 0
, 0
, 0
, na
, na
)
zhl(len)=>
upper = ta.highest(len)
lower = ta.lowest(len)
var float out = 0
out := b.h > upper ? 0 : b.l < lower ? 1 : out
top = out == 0 and out != 0 ? b.h : 0
btm = out == 1 and out != 1 ? b.l : 0
= zhl(sLen)
= zhl(iLen)
upphl(trend) =>
var label lbl = label.new(
na
, na
, color = invcol
, textcolor = toplvl
, style = label.style_label_down
, size = size.small
)
if top
phl.stopcross := true
phl.txtup := top > phl.topy ? "HH" : "HL"
if show_lbl
topl = label.new(
b.n - swing_r_lookback
, top
, phl.txtup
, color = invcol
, textcolor = toplvl
, style = label.style_label_down
, size = size.small
)
line.delete(phl.top )
phl.top := line.new(
b.n - sLen
, top
, b.n
, top
, color = toplvl)
phl.topy := top
phl.topx := b.n - sLen
phl.tup := top
phl.tupx := b.n - sLen
if itop
phl.itopcross := true
phl.itopy := itop
phl.itopx := b.n - iLen
phl.tup := math.max(high, phl.tup)
phl.tupx := phl.tup == high ? b.n : phl.tupx
phl.uV := phl.tup != phl.tup ? b.v : phl.uV
if barstate.islast
line.set_xy1(
phl.top
, phl.tupx
, phl.tup
)
line.set_xy2(
phl.top
, b.n + 50
, phl.tup
)
label.set_x(
lbl
, b.n + 50
)
label.set_y(
lbl
, phl.tup
)
dist = math.abs(phl.uV / (phl.uV + phl.dV)) * 100
label.set_text (lbl, trend < 0
? "Strong High | " + str.tostring(phl.uV, format.volume) + " (" + str.tostring(math.round(dist,0)) + "%)"
: "Weak High | " + str.tostring(phl.uV, format.volume) + " (" + str.tostring(math.round(dist,0)) + "%)")
dnphl(trend) =>
var label lbl = label.new(
na
, na
, color = invcol
, textcolor = btmlvl
, style = label.style_label_up
, size = size.small
)
if btm
phl.sbottomcross := true
phl.txtdn := btm > phl.bottomy ? "LH" : "LL"
if show_lbl
btml = label.new(
b.n - swing_r_lookback
, btm, phl.txtdn
, color = invcol
, textcolor = btmlvl
, style = label.style_label_up
, size = size.small
)
line.delete(phl.bottom )
phl.bottom := line.new(
b.n - sLen
, btm
, b.n
, btm
, color = btmlvl
)
phl.bottomy := btm
phl.bottomx := b.n - sLen
phl.tdn := btm
phl.tdnx := b.n - sLen
if ibtm
phl.ibottomcross := true
phl.ibottomy := ibtm
phl.ibottomx := b.n - iLen
phl.tdn := math.min(low, phl.tdn)
phl.tdnx := phl.tdn == low ? b.n : phl.tdnx
phl.dV := phl.tdn != phl.tdn ? b.v : phl.dV
if barstate.islast
line.set_xy1(
phl.bottom
, phl.tdnx
, phl.tdn
)
line.set_xy2(
phl.bottom
, b.n + 50
, phl.tdn
)
label.set_x(
lbl
, b.n + 50
)
label.set_y(
lbl
, phl.tdn
)
dist = math.abs(phl.dV / (phl.uV + phl.dV)) * 100
label.set_text (lbl, trend > 0
? "Strong Low | " + str.tostring(phl.dV, format.volume) + " (" + str.tostring(math.round(dist,0)) + "%)"
: "Weak Low | " + str.tostring(phl.uV, format.volume) + " (" + str.tostring(math.round(dist,0)) + "%)")
midphl() =>
avg = math.avg(phl.bottom.get_y2(), phl.top.get_y2())
var line l = line.new(
y1 = avg
, y2 = avg
, x1 = b.n - sLen
, x2 = b.n + 50
, color = midlvl
, style = line.style_solid
)
var label lbl = label.new(
x = b.n + 50
, y = avg
, text = "Equilibrium"
, style = label.style_label_left
, color = invcol
, textcolor = midlvl
, size = size.small
)
if barstate.islast
more = (phl.bottom.get_x1() + phl.bottom.get_x2()) > (phl.top.get_x1() + phl.top.get_x2()) ? phl.top.get_x1() : phl.bottom.get_x1()
line.set_xy1(l , more , avg)
line.set_xy2(l , b.n + 50, avg)
label.set_x (lbl , b.n + 50 )
label.set_y (lbl , avg )
dist = math.abs((l.get_y2() - close) / close) * 100
label.set_text (lbl, "Equilibrium (" + str.tostring(math.round(dist,0)) + "%)")
hqlzone() =>
if barstate.islast
var hqlzone dZone = hqlzone.new(
box.new(
na
, na
, na
, na
, bgcolor = color.new(toplvl, 70)
, border_color = na
)
, box.new(
na
, na
, na
, na
, bgcolor = color.new(midlvl, 70)
, border_color = na
)
, box.new(
na
, na
, na
, na
, bgcolor = color.new(btmlvl, 70)
, border_color = na
)
, label.new(na, na, text = "Premium" , color = invcol, textcolor = toplvl, style = label.style_label_down, size = size.small)
, label.new(na, na, text = "Equilibrium", color = invcol, textcolor = midlvl, style = label.style_label_left, size = size.small)
, label.new(na, na, text = "Discount" , color = invcol, textcolor = btmlvl, style = label.style_label_up , size = size.small)
)
dZone.pbx.set_lefttop(int(math.max(phl.topx, phl.bottomx)) , phl.tup)
dZone.pbx.set_rightbottom(b.n + 50 , 0.95 * phl.tup + 0.05 * phl.tdn)
dZone.ebx.set_lefttop(int(math.max(phl.topx, phl.bottomx)), 0.525 * phl.tup + 0.475 * phl.tdn)
dZone.ebx.set_rightbottom(b.n + 50 , 0.525 * phl.tdn + 0.475 * phl.tup)
dZone.lbx.set_lefttop(int(math.max(phl.topx, phl.bottomx)), 0.95 * phl.tdn + 0.05 * phl.tup)
dZone.lbx.set_rightbottom(b.n + 50 , phl.tdn)
dZone.plb.set_xy( int(math.avg(math.max(phl.topx, phl.bottomx), int(b.n + 50))) , phl.tup)
dZone.elb.set_xy( int(b.n + 50) , math.avg(phl.tup, phl.tdn))
dZone.lbl.set_xy( int(math.avg(math.max(phl.topx, phl.bottomx), int(b.n + 50))) , phl.tdn)
if show_mtb
upphl (trend)
dnphl (trend)
hqlzone()
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - End }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - Volumetric Order Block }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
method eB(box b, bool ext, color css, bool swing) =>
b.unshift(
box.new(
na
, na
, na
, na
, xloc = xloc.bar_time
, text_font_family = font.family_monospace
, extend = ext ? extend.right : extend.none
, border_color = swing ? color.new(css, 0) : color.new(color.white,100)
, bgcolor = css
, border_width = 1
)
)
method eL(line l, bool ext, bool solid, color css) =>
l.unshift(
line.new(
na
, na
, na
, na
, width = 1
, color = css
, xloc = xloc.bar_time
, extend = ext ? extend.right : extend.none
, style = solid ? line.style_solid : line.style_dashed
)
)
method drawVOB(bool cdn, bool bull, color css, int loc, bool swing) =>
= request.security(
syminfo.tickerid
, ""
,
, lookahead = barmerge.lookahead_off
)
var obC obj = obC.new(
array.new()
, array.new()
, array.new< int >()
, array.new()
, array.new()
, array.new()
, array.new< int >()
, array.new< int >()
, array.new< int >()
, array.new< int >()
, array.new()
, array.new()
, array.new< int >()
)
var obD draw = obD.new(
array.new()
, array.new()
, array.new()
, array.new()
, array.new()
)
if barstate.isfirst
for i = 0 to ob_num - 1
draw.mL .eL(false, false, use_grayscale ? color.new(color.gray, 0) : color.new(css,0))
draw.ob .eB(false, use_grayscale ? color.new(color.gray, 90) : css, swing)
draw.blB.eB(false, css_metric_up , swing)
draw.brB.eB(false, css_metric_dn , swing)
draw.eOB.eB(true , use_grayscale ? color.new(color.gray, 90) : css, swing)
float pos = ob_pos == "Full"
? (bull ? high : low)
: ob_pos == "Middle"
? ohlc4
: ob_pos == "Accurate"
? hl2
: hl2
if cdn
obj.h.clear()
obj.l.clear()
obj.n.clear()
for i = 0 to math.abs((loc - b.n)) - 1
obj.h.push(hH )
obj.l.push(lL )
obj.n.push(b.t )
// obj.h.reverse()
// obj.l.reverse()
int iU = obj.l.indexof(obj.l.min()) + 1
int iD = obj.h.indexof(obj.h.max()) + 1
obj.dir.unshift(
bull
? (b.c > b.o ? 1 : -1)
: (b.c > b.o ? 1 : -1)
)
obj.top.unshift(
bull
? pos
: obj.h.max()
)
obj.btm.unshift(
bull
? obj.l.min()
: pos
)
obj.left.unshift(
bull
? obj.n.get(obj.l.indexof(obj.l.min()))
: obj.n.get(obj.h.indexof(obj.h.max()))
)
obj.avg.unshift(
math.avg(obj.top.first(), obj.btm.first())
)
obj.cV.unshift(
bull
? b.v
: b.v
)
if ob_pos == "Precise"
switch bull
true =>
if obj.avg.get(0) < (b.c < b.o ? b.c : b.o ) and obj.top.get(0) > hlcc4
obj.top.set(0, obj.avg.get(0))
obj.avg.set(0, math.avg(obj.top.first(), obj.btm.first()))
false =>
if obj.avg.get(0) > (b.c < b.o ? b.o : b.c ) and obj.btm.get(0) < hlcc4
obj.btm.set(0, obj.avg.get(0))
obj.avg.set(0, math.avg(obj.top.first(), obj.btm.first()))
obj.blVP.unshift ( 0 )
obj.brVP.unshift ( 0 )
obj.wM .unshift ( 1 )
if use_overlap
int rmP = use_overlap_method == "Recent" ? 1 : 0
if obj.avg.size() > 1
if bull
? obj.btm.first() < obj.top.get(1)
: obj.top.first() > obj.btm.get(1)
obj.wM .remove(rmP)
obj.cV .remove(rmP)
obj.dir .remove(rmP)
obj.top .remove(rmP)
obj.avg .remove(rmP)
obj.btm .remove(rmP)
obj.left .remove(rmP)
obj.blVP .remove(rmP)
obj.brVP .remove(rmP)
if barstate.isconfirmed
for x = 0 to ob_num - 1
tg = switch ob_mitigation
"Middle" => obj.avg
"Absolute" => bull ? obj.btm : obj.top
for in tg
if (bull ? cC < pt : cC > pt)
obj.wM .remove(idx)
obj.cV .remove(idx)
obj.dir .remove(idx)
obj.top .remove(idx)
obj.avg .remove(idx)
obj.btm .remove(idx)
obj.left .remove(idx)
obj.blVP .remove(idx)
obj.brVP .remove(idx)
if barstate.islast
if obj.avg.size() > 0
// Alert
if bull
? ta.crossunder(low , obj.top.get(0))
: ta.crossover (high, obj.btm.get(0))
switch bull
true => blalert.obtouch := true
false => bralert.obtouch := true
float tV = 0
obj.dV.clear()
seq = math.min(ob_num - 1, obj.avg.size() - 1)
for j = 0 to seq
tV += obj.cV.get(j)
if j == seq
for y = 0 to seq
obj.dV.unshift(
math.floor(
(obj.cV.get(y) / tV) * 100)
)
obj.dV.reverse()
for i = 0 to math.min(ob_num - 1, obj.avg.size() - 1)
dmL = draw.mL .get(i)
dOB = draw.ob .get(i)
dblB = draw.blB.get(i)
dbrB = draw.brB.get(i)
deOB = draw.eOB.get(i)
dOB.set_lefttop (obj.left .get(i) , obj.top.get(i))
deOB.set_lefttop (b.t , obj.top.get(i))
dOB.set_rightbottom (b.t , obj.btm.get(i))
deOB.set_rightbottom(b.t + (b.t - b.t ) * 100 , obj.btm.get(i))
if use_middle_line
dmL.set_xy1(obj.left.get(i), obj.avg.get(i))
dmL.set_xy2(b.t , obj.avg.get(i))
if ob_metrics_show
dblB.set_lefttop (obj.left.get(i), obj.top.get(i))
dbrB.set_lefttop (obj.left.get(i), obj.avg.get(i))
dblB.set_rightbottom(obj.left.get(i), obj.avg.get(i))
dbrB.set_rightbottom(obj.left.get(i), obj.btm.get(i))
rpBL = dblB.get_right()
rpBR = dbrB.get_right()
dbrB.set_right(rpBR + (b.t - b.t ) * obj.brVP.get(i))
dblB.set_right(rpBL + (b.t - b.t ) * obj.blVP.get(i))
if use_show_metric
txt = switch
obj.cV.get(i) >= 1000000000 => str.tostring(math.round(obj.cV.get(i) / 1000000000,3)) + "B"
obj.cV.get(i) >= 1000000 => str.tostring(math.round(obj.cV.get(i) / 1000000,3)) + "M"
obj.cV.get(i) >= 1000 => str.tostring(math.round(obj.cV.get(i) / 1000,3)) + "K"
obj.cV.get(i) < 1000 => str.tostring(math.round(obj.cV.get(i)))
deOB.set_text(
str.tostring(
txt + " (" + str.tostring(obj.dV.get(i)) + "%)")
)
deOB.set_text_size (size.auto)
deOB.set_text_halign(text.align_left)
deOB.set_text_color (use_grayscale ? color.silver : color.new(css, 0))
if ob_metrics_show and barstate.isconfirmed
if obj.wM.size() > 0
for i = 0 to obj.avg.size() - 1
switch obj.dir.get(i)
1 =>
switch obj.wM.get(i)
1 => obj.blVP.set(i, obj.blVP.get(i) + 1), obj.wM.set(i, 2)
2 => obj.blVP.set(i, obj.blVP.get(i) + 1), obj.wM.set(i, 3)
3 => obj.brVP.set(i, obj.brVP.get(i) + 1), obj.wM.set(i, 1)
-1 =>
switch obj.wM.get(i)
1 => obj.brVP.set(i, obj.brVP.get(i) + 1), obj.wM.set(i, 2)
2 => obj.brVP.set(i, obj.brVP.get(i) + 1), obj.wM.set(i, 3)
3 => obj.blVP.set(i, obj.blVP.get(i) + 1), obj.wM.set(i, 1)
var hN = array.new(1, b.n)
var lN = array.new(1, b.n)
var hS = array.new(1, b.n)
var lS = array.new(1, b.n)
if iH
hN.pop()
hN.unshift(int(b.n ))
if iL
lN.pop()
lN.unshift(int(b.n ))
if sH
hS.pop()
hS.unshift(int(b.n ))
if sL
lS.pop()
lS.unshift(int(b.n ))
if ob_show
bull_ob.drawVOB(true , ob_bull_css, hN.first(), false)
bear_ob.drawVOB(false, ob_bear_css, lN.first(), false)
if ob_swings
s_bull_ob.drawVOB(true , css_swing_up, hS.first(), true)
s_bear_ob.drawVOB(false, css_swing_dn, lS.first(), true)
if bull_ob
blalert.ob := true
if bear_ob
bralert.ob := true
if s_bull_ob
blalert.swingob := true
if s_bear_ob
blalert.swingob := true
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - End }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{ - FVG | VI | OG }
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
//{----------------------------------------------------------------------------------------------------------------------------------------------}
ghl() => request.security(syminfo.tickerid, fvg_tf, [high , low , close , open ])
tfG() => request.security(syminfo.tickerid, fvg_tf, )
cG(bool bull) =>
= ghl()
= tfG()
var FVG draw = FVG.new(
array.new()
, array.new()
)
var FVG cords = array.new()
float pup = na
float pdn = na
bool cdn = na
int pos = 2
cc = timeframe.change(fvg_tf)
if barstate.isfirst
for i = 0 to fvg_num - 1
draw.box.unshift(box.new (na, na, na, na, border_color = color.new(color.white, 100), xloc = xloc.bar_time))
draw.ln.unshift (line.new(na, na, na, na, xloc = xloc.bar_time, width = 1, style = line.style_solid))
switch what_fvg
"FVG" =>
pup := bull ? gl : l
pdn := bull ? h : gh
cdn := bull ? gl > h and cc : gh < l and cc
pos := 2
"VI" =>
pup := bull
? (gc > go
? go
: gc)
: (gc > go
? go
: gc )
pdn := bull
? (gc > go
? gc
: go )
: (gc > go
? gc
: go)
cdn := bull
? go > gc and gh > gl and gc > gc and go > go and gh < math.min(gc, go) and cc
: go < gc and gl < gh and gc < gc and go < go and gl > math.max(gc, go) and cc
pos := 1
"OG" =>
pup := bull ? b.l : gl
p
Omega ATR Indicator📖 Introduction
The Ω ATR Indicator was created to provide a more complete and professional framework for volatility analysis than the classic Average True Range (ATR).
While the traditional ATR is a useful tool, it has limitations: it delivers a simple rolling average of volatility, but it does not adapt to market regimes, it does not highlight extreme events, and it often leaves the trader with incomplete information about risk.
The Ω ATR takes the same foundation and elevates it into a multi-dimensional volatility dashboard, adding statistical layers, adaptive calculations, and clear visual references that allow traders to interpret volatility in a way that is immediately actionable.
🔎 What makes it different from a standard ATR?
This indicator introduces several features beyond the classic formula:
True Range Core – plots the raw True Range (TR) for each bar, providing a direct, bar-by-bar view of volatility impulses.
Standard & Adjusted ATR – includes both the conventional ATR (smoothed average) and an Adjusted ATR that automatically corrects for extreme conditions by incorporating percentile rescaling.
Percentile Volatility Levels – dynamically calculated extreme thresholds (99.8%, 75%, 50%, 25%), plotted as dotted levels across the chart. These act as reference lines for “normal” vs. “abnormal” volatility, useful for spotting unusual price expansions or contractions.
Linear Regression Volatility Trend – overlays a regression line of volatility, showing whether the market is moving toward expansion (rising vol), contraction (falling vol), or stability.
Monetary Value Translation – the indicator converts volatility into points, ticks, and dollar values (based on the instrument’s point value). This allows futures traders and high-value instruments users to immediately see how much volatility is “worth” in cash terms.
Interactive Table Display – a real-time statistics table is displayed directly on the chart, showing:
SMA of ATR in $ and points
Percentile-based volatility range (VAR) in $ and points
Tick equivalences, for quick position sizing
⚡ How traders can use it
The Ω ATR Indicator is designed to be versatile, fitting both discretionary traders and systematic strategy developers.
Risk Management: ATR-based stop losses and position sizing are significantly improved by using the adjusted ATR and percentile thresholds. Traders can size their positions according to volatility regimes, not just raw averages.
Breakout & Exhaustion Detection: When TR or ATR values spike above the 99.8% or 95% percentile levels, this often corresponds to breakout conditions or volatility exhaustion — useful for breakout strategies, mean-reversion setups, and volatility fades.
Market Regime Identification: The regression line helps distinguish if volatility is rising (trending environment, larger swings expected) or compressing (range-bound environment, lower risk opportunities).
Multi-Asset Flexibility: Works equally well on equities, futures, crypto, and FX. Its point/tick/dollar conversion makes it especially powerful for futures traders who need to quantify risk precisely.
Scalping to Swing Trading: On lower timeframes, it acts as a micro-volatility detector; on higher timeframes, it functions as a strategic risk gauge for position management.
⚙️ Settings and Customization
Length: The ATR lookback period (default = 34).
Shorter lengths (14–21) for intraday traders who want fast response.
Longer lengths (34–55) for swing/position traders who want smoother readings.
AVG / ADJ AVG: Toggle to display the standard ATR or the adjusted ATR.
Volatility Levels: Enable/disable up to 4 percentile-based levels (1st = 25%, 2nd = 50%, 3rd = 75%, 4th = 99.8%). Recommended: keep 3 levels active for clarity.
Color Controls: All plots and levels are fully customizable to match your chart style.
Table Display: Positioned on the chart (default: middle-right) with key values updated in real time.
🧭 Best Practices for Use
Combine with Trend Tools: Volatility readings are most powerful when combined with trend filters or volume analysis. For example, a breakout with both high volatility and trend confirmation is stronger than either alone.
ATR Stops: Use the Adjusted ATR rather than the standard one when trailing stops in highly volatile instruments like crypto or Nasdaq futures, as it adapts to outlier spikes.
Dollar Risk Translation: Use the dollar-value outputs to predefine maximum acceptable risk per trade (e.g., “I only risk $250 per position”). This bridges volatility to portfolio risk management.
Event Monitoring: Around economic events or earnings, expect volatility spikes above higher percentile levels. The indicator makes these moves instantly visible.
📌 Summary
The Ω ATR Indicator is not just “another ATR.” It is a comprehensive volatility framework that transforms volatility from a simple statistic into an actionable trading signal.
By combining:
the classic ATR,
an adjusted ATR,
percentile extremes,
regression-based volatility trends,
and real-time dollar conversions,
…this tool allows traders to precisely understand, visualize, and act on volatility in ways that a standard ATR simply cannot provide.
Whether you are scalping intraday moves, swing trading equities, or managing futures positions, the Ω ATR equips you with a professional-grade volatility dashboard that clarifies risk, highlights opportunity, and adapts across all markets and timeframes.
👉 Designed and developed by OmegaTools for traders who demand precision, clarity, and adaptability in their volatility analysis.
ATAI Volume analysis with price action V 1.00ATAI Volume Analysis with Price Action
1. Introduction
1.1 Overview
ATAI Volume Analysis with Price Action is a composite indicator designed for TradingView. It combines per‑side volume data —that is, how much buying and selling occurs during each bar—with standard price‑structure elements such as swings, trend lines and support/resistance. By blending these elements the script aims to help a trader understand which side is in control, whether a breakout is genuine, when markets are potentially exhausted and where liquidity providers might be active.
The indicator is built around TradingView’s up/down volume feed accessed via the TradingView/ta/10 library. The following excerpt from the script illustrates how this feed is configured:
import TradingView/ta/10 as tvta
// Determine lower timeframe string based on user choice and chart resolution
string lower_tf_breakout = use_custom_tf_input ? custom_tf_input :
timeframe.isseconds ? "1S" :
timeframe.isintraday ? "1" :
timeframe.isdaily ? "5" : "60"
// Request up/down volume (both positive)
= tvta.requestUpAndDownVolume(lower_tf_breakout)
Lower‑timeframe selection. If you do not specify a custom lower timeframe, the script chooses a default based on your chart resolution: 1 second for second charts, 1 minute for intraday charts, 5 minutes for daily charts and 60 minutes for anything longer. Smaller intervals provide a more precise view of buyer and seller flow but cover fewer bars. Larger intervals cover more history at the cost of granularity.
Tick vs. time bars. Many trading platforms offer a tick / intrabar calculation mode that updates an indicator on every trade rather than only on bar close. Turning on one‑tick calculation will give the most accurate split between buy and sell volume on the current bar, but it typically reduces the amount of historical data available. For the highest fidelity in live trading you can enable this mode; for studying longer histories you might prefer to disable it. When volume data is completely unavailable (some instruments and crypto pairs), all modules that rely on it will remain silent and only the price‑structure backbone will operate.
Figure caption, Each panel shows the indicator’s info table for a different volume sampling interval. In the left chart, the parentheses “(5)” beside the buy‑volume figure denote that the script is aggregating volume over five‑minute bars; the center chart uses “(1)” for one‑minute bars; and the right chart uses “(1T)” for a one‑tick interval. These notations tell you which lower timeframe is driving the volume calculations. Shorter intervals such as 1 minute or 1 tick provide finer detail on buyer and seller flow, but they cover fewer bars; longer intervals like five‑minute bars smooth the data and give more history.
Figure caption, The values in parentheses inside the info table come directly from the Breakout — Settings. The first row shows the custom lower-timeframe used for volume calculations (e.g., “(1)”, “(5)”, or “(1T)”)
2. Price‑Structure Backbone
Even without volume, the indicator draws structural features that underpin all other modules. These features are always on and serve as the reference levels for subsequent calculations.
2.1 What it draws
• Pivots: Swing highs and lows are detected using the pivot_left_input and pivot_right_input settings. A pivot high is identified when the high recorded pivot_right_input bars ago exceeds the highs of the preceding pivot_left_input bars and is also higher than (or equal to) the highs of the subsequent pivot_right_input bars; pivot lows follow the inverse logic. The indicator retains only a fixed number of such pivot points per side, as defined by point_count_input, discarding the oldest ones when the limit is exceeded.
• Trend lines: For each side, the indicator connects the earliest stored pivot and the most recent pivot (oldest high to newest high, and oldest low to newest low). When a new pivot is added or an old one drops out of the lookback window, the line’s endpoints—and therefore its slope—are recalculated accordingly.
• Horizontal support/resistance: The highest high and lowest low within the lookback window defined by length_input are plotted as horizontal dashed lines. These serve as short‑term support and resistance levels.
• Ranked labels: If showPivotLabels is enabled the indicator prints labels such as “HH1”, “HH2”, “LL1” and “LL2” near each pivot. The ranking is determined by comparing the price of each stored pivot: HH1 is the highest high, HH2 is the second highest, and so on; LL1 is the lowest low, LL2 is the second lowest. In the case of equal prices the newer pivot gets the better rank. Labels are offset from price using ½ × ATR × label_atr_multiplier, with the ATR length defined by label_atr_len_input. A dotted connector links each label to the candle’s wick.
2.2 Key settings
• length_input: Window length for finding the highest and lowest values and for determining trend line endpoints. A larger value considers more history and will generate longer trend lines and S/R levels.
• pivot_left_input, pivot_right_input: Strictness of swing confirmation. Higher values require more bars on either side to form a pivot; lower values create more pivots but may include minor swings.
• point_count_input: How many pivots are kept in memory on each side. When new pivots exceed this number the oldest ones are discarded.
• label_atr_len_input and label_atr_multiplier: Determine how far pivot labels are offset from the bar using ATR. Increasing the multiplier moves labels further away from price.
• Styling inputs for trend lines, horizontal lines and labels (color, width and line style).
Figure caption, The chart illustrates how the indicator’s price‑structure backbone operates. In this daily example, the script scans for bars where the high (or low) pivot_right_input bars back is higher (or lower) than the preceding pivot_left_input bars and higher or lower than the subsequent pivot_right_input bars; only those bars are marked as pivots.
These pivot points are stored and ranked: the highest high is labelled “HH1”, the second‑highest “HH2”, and so on, while lows are marked “LL1”, “LL2”, etc. Each label is offset from the price by half of an ATR‑based distance to keep the chart clear, and a dotted connector links the label to the actual candle.
The red diagonal line connects the earliest and latest stored high pivots, and the green line does the same for low pivots; when a new pivot is added or an old one drops out of the lookback window, the end‑points and slopes adjust accordingly. Dashed horizontal lines mark the highest high and lowest low within the current lookback window, providing visual support and resistance levels. Together, these elements form the structural backbone that other modules reference, even when volume data is unavailable.
3. Breakout Module
3.1 Concept
This module confirms that a price break beyond a recent high or low is supported by a genuine shift in buying or selling pressure. It requires price to clear the highest high (“HH1”) or lowest low (“LL1”) and, simultaneously, that the winning side shows a significant volume spike, dominance and ranking. Only when all volume and price conditions pass is a breakout labelled.
3.2 Inputs
• lookback_break_input : This controls the number of bars used to compute moving averages and percentiles for volume. A larger value smooths the averages and percentiles but makes the indicator respond more slowly.
• vol_mult_input : The “spike” multiplier; the current buy or sell volume must be at least this multiple of its moving average over the lookback window to qualify as a breakout.
• rank_threshold_input (0–100) : Defines a volume percentile cutoff: the current buyer/seller volume must be in the top (100−threshold)%(100−threshold)% of all volumes within the lookback window. For example, if set to 80, the current volume must be in the top 20 % of the lookback distribution.
• ratio_threshold_input (0–1) : Specifies the minimum share of total volume that the buyer (for a bullish breakout) or seller (for bearish) must hold on the current bar; the code also requires that the cumulative buyer volume over the lookback window exceeds the seller volume (and vice versa for bearish cases).
• use_custom_tf_input / custom_tf_input : When enabled, these inputs override the automatic choice of lower timeframe for up/down volume; otherwise the script selects a sensible default based on the chart’s timeframe.
• Label appearance settings : Separate options control the ATR-based offset length, offset multiplier, label size and colors for bullish and bearish breakout labels, as well as the connector style and width.
3.3 Detection logic
1. Data preparation : Retrieve per‑side volume from the lower timeframe and take absolute values. Build rolling arrays of the last lookback_break_input values to compute simple moving averages (SMAs), cumulative sums and percentile ranks for buy and sell volume.
2. Volume spike: A spike is flagged when the current buy (or, in the bearish case, sell) volume is at least vol_mult_input times its SMA over the lookback window.
3. Dominance test: The buyer’s (or seller’s) share of total volume on the current bar must meet or exceed ratio_threshold_input. In addition, the cumulative sum of buyer volume over the window must exceed the cumulative sum of seller volume for a bullish breakout (and vice versa for bearish). A separate requirement checks the sign of delta: for bullish breakouts delta_breakout must be non‑negative; for bearish breakouts it must be non‑positive.
4. Percentile rank: The current volume must fall within the top (100 – rank_threshold_input) percent of the lookback distribution—ensuring that the spike is unusually large relative to recent history.
5. Price test: For a bullish signal, the closing price must close above the highest pivot (HH1); for a bearish signal, the close must be below the lowest pivot (LL1).
6. Labeling: When all conditions above are satisfied, the indicator prints “Breakout ↑” above the bar (bullish) or “Breakout ↓” below the bar (bearish). Labels are offset using half of an ATR‑based distance and linked to the candle with a dotted connector.
Figure caption, (Breakout ↑ example) , On this daily chart, price pushes above the red trendline and the highest prior pivot (HH1). The indicator recognizes this as a valid breakout because the buyer‑side volume on the lower timeframe spikes above its recent moving average and buyers dominate the volume statistics over the lookback period; when combined with a close above HH1, this satisfies the breakout conditions. The “Breakout ↑” label appears above the candle, and the info table highlights that up‑volume is elevated relative to its 11‑bar average, buyer share exceeds the dominance threshold and money‑flow metrics support the move.
Figure caption, In this daily example, price breaks below the lowest pivot (LL1) and the lower green trendline. The indicator identifies this as a bearish breakout because sell‑side volume is sharply elevated—about twice its 11‑bar average—and sellers dominate both the bar and the lookback window. With the close falling below LL1, the script triggers a Breakout ↓ label and marks the corresponding row in the info table, which shows strong down volume, negative delta and a seller share comfortably above the dominance threshold.
4. Market Phase Module (Volume Only)
4.1 Concept
Not all markets trend; many cycle between periods of accumulation (buying pressure building up), distribution (selling pressure dominating) and neutral behavior. This module classifies the current bar into one of these phases without using ATR , relying solely on buyer and seller volume statistics. It looks at net flows, ratio changes and an OBV‑like cumulative line with dual‑reference (1‑ and 2‑bar) trends. The result is displayed both as on‑chart labels and in a dedicated row of the info table.
4.2 Inputs
• phase_period_len: Number of bars over which to compute sums and ratios for phase detection.
• phase_ratio_thresh : Minimum buyer share (for accumulation) or minimum seller share (for distribution, derived as 1 − phase_ratio_thresh) of the total volume.
• strict_mode: When enabled, both the 1‑bar and 2‑bar changes in each statistic must agree on the direction (strict confirmation); when disabled, only one of the two references needs to agree (looser confirmation).
• Color customisation for info table cells and label styling for accumulation and distribution phases, including ATR length, multiplier, label size, colors and connector styles.
• show_phase_module: Toggles the entire phase detection subsystem.
• show_phase_labels: Controls whether on‑chart labels are drawn when accumulation or distribution is detected.
4.3 Detection logic
The module computes three families of statistics over the volume window defined by phase_period_len:
1. Net sum (buyers minus sellers): net_sum_phase = Σ(buy) − Σ(sell). A positive value indicates a predominance of buyers. The code also computes the differences between the current value and the values 1 and 2 bars ago (d_net_1, d_net_2) to derive up/down trends.
2. Buyer ratio: The instantaneous ratio TF_buy_breakout / TF_tot_breakout and the window ratio Σ(buy) / Σ(total). The current ratio must exceed phase_ratio_thresh for accumulation or fall below 1 − phase_ratio_thresh for distribution. The first and second differences of the window ratio (d_ratio_1, d_ratio_2) determine trend direction.
3. OBV‑like cumulative net flow: An on‑balance volume analogue obv_net_phase increments by TF_buy_breakout − TF_sell_breakout each bar. Its differences over the last 1 and 2 bars (d_obv_1, d_obv_2) provide trend clues.
The algorithm then combines these signals:
• For strict mode , accumulation requires: (a) current ratio ≥ threshold, (b) cumulative ratio ≥ threshold, (c) both ratio differences ≥ 0, (d) net sum differences ≥ 0, and (e) OBV differences ≥ 0. Distribution is the mirror case.
• For loose mode , it relaxes the directional tests: either the 1‑ or the 2‑bar difference needs to agree in each category.
If all conditions for accumulation are satisfied, the phase is labelled “Accumulation” ; if all conditions for distribution are satisfied, it’s labelled “Distribution” ; otherwise the phase is “Neutral” .
4.4 Outputs
• Info table row : Row 8 displays “Market Phase (Vol)” on the left and the detected phase (Accumulation, Distribution or Neutral) on the right. The text colour of both cells matches a user‑selectable palette (typically green for accumulation, red for distribution and grey for neutral).
• On‑chart labels : When show_phase_labels is enabled and a phase persists for at least one bar, the module prints a label above the bar ( “Accum” ) or below the bar ( “Dist” ) with a dashed or dotted connector. The label is offset using ATR based on phase_label_atr_len_input and phase_label_multiplier and is styled according to user preferences.
Figure caption, The chart displays a red “Dist” label above a particular bar, indicating that the accumulation/distribution module identified a distribution phase at that point. The detection is based on seller dominance: during that bar, the net buyer-minus-seller flow and the OBV‑style cumulative flow were trending down, and the buyer ratio had dropped below the preset threshold. These conditions satisfy the distribution criteria in strict mode. The label is placed above the bar using an ATR‑based offset and a dashed connector. By the time of the current bar in the screenshot, the phase indicator shows “Neutral” in the info table—signaling that neither accumulation nor distribution conditions are currently met—yet the historical “Dist” label remains to mark where the prior distribution phase began.
Figure caption, In this example the market phase module has signaled an Accumulation phase. Three bars before the current candle, the algorithm detected a shift toward buyers: up‑volume exceeded its moving average, down‑volume was below average, and the buyer share of total volume climbed above the threshold while the on‑balance net flow and cumulative ratios were trending upwards. The blue “Accum” label anchored below that bar marks the start of the phase; it remains on the chart because successive bars continue to satisfy the accumulation conditions. The info table confirms this: the “Market Phase (Vol)” row still reads Accumulation, and the ratio and sum rows show buyers dominating both on the current bar and across the lookback window.
5. OB/OS Spike Module
5.1 What overbought/oversold means here
In many markets, a rapid extension up or down is often followed by a period of consolidation or reversal. The indicator interprets overbought (OB) conditions as abnormally strong selling risk at or after a price rally and oversold (OS) conditions as unusually strong buying risk after a decline. Importantly, these are not direct trade signals; rather they flag areas where caution or contrarian setups may be appropriate.
5.2 Inputs
• minHits_obos (1–7): Minimum number of oscillators that must agree on an overbought or oversold condition for a label to print.
• syncWin_obos: Length of a small sliding window over which oscillator votes are smoothed by taking the maximum count observed. This helps filter out choppy signals.
• Volume spike criteria: kVolRatio_obos (ratio of current volume to its SMA) and zVolThr_obos (Z‑score threshold) across volLen_obos. Either threshold can trigger a spike.
• Oscillator toggles and periods: Each of RSI, Stochastic (K and D), Williams %R, CCI, MFI, DeMarker and Stochastic RSI can be independently enabled; their periods are adjustable.
• Label appearance: ATR‑based offset, size, colors for OB and OS labels, plus connector style and width.
5.3 Detection logic
1. Directional volume spikes: Volume spikes are computed separately for buyer and seller volumes. A sell volume spike (sellVolSpike) flags a potential OverBought bar, while a buy volume spike (buyVolSpike) flags a potential OverSold bar. A spike occurs when the respective volume exceeds kVolRatio_obos times its simple moving average over the window or when its Z‑score exceeds zVolThr_obos.
2. Oscillator votes: For each enabled oscillator, calculate its overbought and oversold state using standard thresholds (e.g., RSI ≥ 70 for OB and ≤ 30 for OS; Stochastic %K/%D ≥ 80 for OB and ≤ 20 for OS; etc.). Count how many oscillators vote for OB and how many vote for OS.
3. Minimum hits: Apply the smoothing window syncWin_obos to the vote counts using a maximum‑of‑last‑N approach. A candidate bar is only considered if the smoothed OB hit count ≥ minHits_obos (for OverBought) or the smoothed OS hit count ≥ minHits_obos (for OverSold).
4. Tie‑breaking: If both OverBought and OverSold spike conditions are present on the same bar, compare the smoothed hit counts: the side with the higher count is selected; ties default to OverBought.
5. Label printing: When conditions are met, the bar is labelled as “OverBought X/7” above the candle or “OverSold X/7” below it. “X” is the number of oscillators confirming, and the bracket lists the abbreviations of contributing oscillators. Labels are offset from price using half of an ATR‑scaled distance and can optionally include a dotted or dashed connector line.
Figure caption, In this chart the overbought/oversold module has flagged an OverSold signal. A sell‑off from the prior highs brought price down to the lower trend‑line, where the bar marked “OverSold 3/7 DeM” appears. This label indicates that on that bar the module detected a buy‑side volume spike and that at least three of the seven enabled oscillators—in this case including the DeMarker—were in oversold territory. The label is printed below the candle with a dotted connector, signaling that the market may be temporarily exhausted on the downside. After this oversold print, price begins to rebound towards the upper red trend‑line and higher pivot levels.
Figure caption, This example shows the overbought/oversold module in action. In the left‑hand panel you can see the OB/OS settings where each oscillator (RSI, Stochastic, Williams %R, CCI, MFI, DeMarker and Stochastic RSI) can be enabled or disabled, and the ATR length and label offset multiplier adjusted. On the chart itself, price has pushed up to the descending red trendline and triggered an “OverBought 3/7” label. That means the sell‑side volume spiked relative to its average and three out of the seven enabled oscillators were in overbought territory. The label is offset above the candle by half of an ATR and connected with a dashed line, signaling that upside momentum may be overextended and a pause or pullback could follow.
6. Buyer/Seller Trap Module
6.1 Concept
A bull trap occurs when price appears to break above resistance, attracting buyers, but fails to sustain the move and quickly reverses, leaving a long upper wick and trapping late entrants. A bear trap is the opposite: price breaks below support, lures in sellers, then snaps back, leaving a long lower wick and trapping shorts. This module detects such traps by looking for price structure sweeps, order‑flow mismatches and dominance reversals. It uses a scoring system to differentiate risk from confirmed traps.
6.2 Inputs
• trap_lookback_len: Window length used to rank extremes and detect sweeps.
• trap_wick_threshold: Minimum proportion of a bar’s range that must be wick (upper for bull traps, lower for bear traps) to qualify as a sweep.
• trap_score_risk: Minimum aggregated score required to flag a trap risk. (The code defines a trap_score_confirm input, but confirmation is actually based on price reversal rather than a separate score threshold.)
• trap_confirm_bars: Maximum number of bars allowed for price to reverse and confirm the trap. If price does not reverse in this window, the risk label will expire or remain unconfirmed.
• Label settings: ATR length and multiplier for offsetting, size, colours for risk and confirmed labels, and connector style and width. Separate settings exist for bull and bear traps.
• Toggle inputs: show_trap_module and show_trap_labels enable the module and control whether labels are drawn on the chart.
6.3 Scoring logic
The module assigns points to several conditions and sums them to determine whether a trap risk is present. For bull traps, the score is built from the following (bear traps mirror the logic with highs and lows swapped):
1. Sweep (2 points): Price trades above the high pivot (HH1) but fails to close above it and leaves a long upper wick at least trap_wick_threshold × range. For bear traps, price dips below the low pivot (LL1), fails to close below and leaves a long lower wick.
2. Close break (1 point): Price closes beyond HH1 or LL1 without leaving a long wick.
3. Candle/delta mismatch (2 points): The candle closes bullish yet the order flow delta is negative or the seller ratio exceeds 50%, indicating hidden supply. Conversely, a bearish close with positive delta or buyer dominance suggests hidden demand.
4. Dominance inversion (2 points): The current bar’s buyer volume has the highest rank in the lookback window while cumulative sums favor sellers, or vice versa.
5. Low‑volume break (1 point): Price crosses the pivot but total volume is below its moving average.
The total score for each side is compared to trap_score_risk. If the score is high enough, a “Bull Trap Risk” or “Bear Trap Risk” label is drawn, offset from the candle by half of an ATR‑scaled distance using a dashed outline. If, within trap_confirm_bars, price reverses beyond the opposite level—drops back below the high pivot for bull traps or rises above the low pivot for bear traps—the label is upgraded to a solid “Bull Trap” or “Bear Trap” . In this version of the code, there is no separate score threshold for confirmation: the variable trap_score_confirm is unused; confirmation depends solely on a successful price reversal within the specified number of bars.
Figure caption, In this example the trap module has flagged a Bear Trap Risk. Price initially breaks below the most recent low pivot (LL1), but the bar closes back above that level and leaves a long lower wick, suggesting a failed push lower. Combined with a mismatch between the candle direction and the order flow (buyers regain control) and a reversal in volume dominance, the aggregate score exceeds the risk threshold, so a dashed “Bear Trap Risk” label prints beneath the bar. The green and red trend lines mark the current low and high pivot trajectories, while the horizontal dashed lines show the highest and lowest values in the lookback window. If, within the next few bars, price closes decisively above the support, the risk label would upgrade to a solid “Bear Trap” label.
Figure caption, In this example the trap module has identified both ends of a price range. Near the highs, price briefly pushes above the descending red trendline and the recent pivot high, but fails to close there and leaves a noticeable upper wick. That combination of a sweep above resistance and order‑flow mismatch generates a Bull Trap Risk label with a dashed outline, warning that the upside break may not hold. At the opposite extreme, price later dips below the green trendline and the labelled low pivot, then quickly snaps back and closes higher. The long lower wick and subsequent price reversal upgrade the previous bear‑trap risk into a confirmed Bear Trap (solid label), indicating that sellers were caught on a false breakdown. Horizontal dashed lines mark the highest high and lowest low of the lookback window, while the red and green diagonals connect the earliest and latest pivot highs and lows to visualize the range.
7. Sharp Move Module
7.1 Concept
Markets sometimes display absorption or climax behavior—periods when one side steadily gains the upper hand before price breaks out with a sharp move. This module evaluates several order‑flow and volume conditions to anticipate such moves. Users can choose how many conditions must be met to flag a risk and how many (plus a price break) are required for confirmation.
7.2 Inputs
• sharp Lookback: Number of bars in the window used to compute moving averages, sums, percentile ranks and reference levels.
• sharpPercentile: Minimum percentile rank for the current side’s volume; the current buy (or sell) volume must be greater than or equal to this percentile of historical volumes over the lookback window.
• sharpVolMult: Multiplier used in the volume climax check. The current side’s volume must exceed this multiple of its average to count as a climax.
• sharpRatioThr: Minimum dominance ratio (current side’s volume relative to the opposite side) used in both the instant and cumulative dominance checks.
• sharpChurnThr: Maximum ratio of a bar’s range to its ATR for absorption/churn detection; lower values indicate more absorption (large volume in a small range).
• sharpScoreRisk: Minimum number of conditions that must be true to print a risk label.
• sharpScoreConfirm: Minimum number of conditions plus a price break required for confirmation.
• sharpCvdThr: Threshold for cumulative delta divergence versus price change (positive for bullish accumulation, negative for bearish distribution).
• Label settings: ATR length (sharpATRlen) and multiplier (sharpLabelMult) for positioning labels, label size, colors and connector styles for bullish and bearish sharp moves.
• Toggles: enableSharp activates the module; show_sharp_labels controls whether labels are drawn.
7.3 Conditions (six per side)
For each side, the indicator computes six boolean conditions and sums them to form a score:
1. Dominance (instant and cumulative):
– Instant dominance: current buy volume ≥ sharpRatioThr × current sell volume.
– Cumulative dominance: sum of buy volumes over the window ≥ sharpRatioThr × sum of sell volumes (and vice versa for bearish checks).
2. Accumulation/Distribution divergence: Over the lookback window, cumulative delta rises by at least sharpCvdThr while price fails to rise (bullish), or cumulative delta falls by at least sharpCvdThr while price fails to fall (bearish).
3. Volume climax: The current side’s volume is ≥ sharpVolMult × its average and the product of volume and bar range is the highest in the lookback window.
4. Absorption/Churn: The current side’s volume divided by the bar’s range equals the highest value in the window and the bar’s range divided by ATR ≤ sharpChurnThr (indicating large volume within a small range).
5. Percentile rank: The current side’s volume percentile rank is ≥ sharp Percentile.
6. Mirror logic for sellers: The above checks are repeated with buyer and seller roles swapped and the price break levels reversed.
Each condition that passes contributes one point to the corresponding side’s score (0 or 1). Risk and confirmation thresholds are then applied to these scores.
7.4 Scoring and labels
• Risk: If scoreBull ≥ sharpScoreRisk, a “Sharp ↑ Risk” label is drawn above the bar. If scoreBear ≥ sharpScoreRisk, a “Sharp ↓ Risk” label is drawn below the bar.
• Confirmation: A risk label is upgraded to “Sharp ↑” when scoreBull ≥ sharpScoreConfirm and the bar closes above the highest recent pivot (HH1); for bearish cases, confirmation requires scoreBear ≥ sharpScoreConfirm and a close below the lowest pivot (LL1).
• Label positioning: Labels are offset from the candle by ATR × sharpLabelMult (full ATR times multiplier), not half, and may include a dashed or dotted connector line if enabled.
Figure caption, In this chart both bullish and bearish sharp‑move setups have been flagged. Earlier in the range, a “Sharp ↓ Risk” label appears beneath a candle: the sell‑side score met the risk threshold, signaling that the combination of strong sell volume, dominance and absorption within a narrow range suggested a potential sharp decline. The price did not close below the lower pivot, so this label remains a “risk” and no confirmation occurred. Later, as the market recovered and volume shifted back to the buy side, a “Sharp ↑ Risk” label prints above a candle near the top of the channel. Here, buy‑side dominance, cumulative delta divergence and a volume climax aligned, but price has not yet closed above the upper pivot (HH1), so the alert is still a risk rather than a confirmed sharp‑up move.
Figure caption, In this chart a Sharp ↑ label is displayed above a candle, indicating that the sharp move module has confirmed a bullish breakout. Prior bars satisfied the risk threshold — showing buy‑side dominance, positive cumulative delta divergence, a volume climax and strong absorption in a narrow range — and this candle closes above the highest recent pivot, upgrading the earlier “Sharp ↑ Risk” alert to a full Sharp ↑ signal. The green label is offset from the candle with a dashed connector, while the red and green trend lines trace the high and low pivot trajectories and the dashed horizontals mark the highest and lowest values of the lookback window.
8. Market‑Maker / Spread‑Capture Module
8.1 Concept
Liquidity providers often “capture the spread” by buying and selling in almost equal amounts within a very narrow price range. These bars can signal temporary congestion before a move or reflect algorithmic activity. This module flags bars where both buyer and seller volumes are high, the price range is only a few ticks and the buy/sell split remains close to 50%. It helps traders spot potential liquidity pockets.
8.2 Inputs
• scalpLookback: Window length used to compute volume averages.
• scalpVolMult: Multiplier applied to each side’s average volume; both buy and sell volumes must exceed this multiple.
• scalpTickCount: Maximum allowed number of ticks in a bar’s range (calculated as (high − low) / minTick). A value of 1 or 2 captures ultra‑small bars; increasing it relaxes the range requirement.
• scalpDeltaRatio: Maximum deviation from a perfect 50/50 split. For example, 0.05 means the buyer share must be between 45% and 55%.
• Label settings: ATR length, multiplier, size, colors, connector style and width.
• Toggles : show_scalp_module and show_scalp_labels to enable the module and its labels.
8.3 Signal
When, on the current bar, both TF_buy_breakout and TF_sell_breakout exceed scalpVolMult times their respective averages and (high − low)/minTick ≤ scalpTickCount and the buyer share is within scalpDeltaRatio of 50%, the module prints a “Spread ↔” label above the bar. The label uses the same ATR offset logic as other modules and draws a connector if enabled.
Figure caption, In this chart the spread‑capture module has identified a potential liquidity pocket. Buyer and seller volumes both spiked above their recent averages, yet the candle’s range measured only a couple of ticks and the buy/sell split stayed close to 50 %. This combination met the module’s criteria, so it printed a grey “Spread ↔” label above the bar. The red and green trend lines link the earliest and latest high and low pivots, and the dashed horizontals mark the highest high and lowest low within the current lookback window.
9. Money Flow Module
9.1 Concept
To translate volume into a monetary measure, this module multiplies each side’s volume by the closing price. It tracks buying and selling system money default currency on a per-bar basis and sums them over a chosen period. The difference between buy and sell currencies (Δ$) shows net inflow or outflow.
9.2 Inputs
• mf_period_len_mf: Number of bars used for summing buy and sell dollars.
• Label appearance settings: ATR length, multiplier, size, colors for up/down labels, and connector style and width.
• Toggles: Use enableMoneyFlowLabel_mf and showMFLabels to control whether the module and its labels are displayed.
9.3 Calculations
• Per-bar money: Buy $ = TF_buy_breakout × close; Sell $ = TF_sell_breakout × close. Their difference is Δ$ = Buy $ − Sell $.
• Summations: Over mf_period_len_mf bars, compute Σ Buy $, Σ Sell $ and ΣΔ$ using math.sum().
• Info table entries: Rows 9–13 display these values as texts like “↑ USD 1234 (1M)” or “ΣΔ USD −5678 (14)”, with colors reflecting whether buyers or sellers dominate.
• Money flow status: If Δ$ is positive the bar is marked “Money flow in” ; if negative, “Money flow out” ; if zero, “Neutral”. The cumulative status is similarly derived from ΣΔ.Labels print at the bar that changes the sign of ΣΔ, offset using ATR × label multiplier and styled per user preferences.
Figure caption, The chart illustrates a steady rise toward the highest recent pivot (HH1) with price riding between a rising green trend‑line and a red trend‑line drawn through earlier pivot highs. A green Money flow in label appears above the bar near the top of the channel, signaling that net dollar flow turned positive on this bar: buy‑side dollar volume exceeded sell‑side dollar volume, pushing the cumulative sum ΣΔ$ above zero. In the info table, the “Money flow (bar)” and “Money flow Σ” rows both read In, confirming that the indicator’s money‑flow module has detected an inflow at both bar and aggregate levels, while other modules (pivots, trend lines and support/resistance) remain active to provide structural context.
In this example the Money Flow module signals a net outflow. Price has been trending downward: successive high pivots form a falling red trend‑line and the low pivots form a descending green support line. When the latest bar broke below the previous low pivot (LL1), both the bar‑level and cumulative net dollar flow turned negative—selling volume at the close exceeded buying volume and pushed the cumulative Δ$ below zero. The module reacts by printing a red “Money flow out” label beneath the candle; the info table confirms that the “Money flow (bar)” and “Money flow Σ” rows both show Out, indicating sustained dominance of sellers in this period.
10. Info Table
10.1 Purpose
When enabled, the Info Table appears in the lower right of your chart. It summarises key values computed by the indicator—such as buy and sell volume, delta, total volume, breakout status, market phase, and money flow—so you can see at a glance which side is dominant and which signals are active.
10.2 Symbols
• ↑ / ↓ — Up (↑) denotes buy volume or money; down (↓) denotes sell volume or money.
• MA — Moving average. In the table it shows the average value of a series over the lookback period.
• Σ (Sigma) — Cumulative sum over the chosen lookback period.
• Δ (Delta) — Difference between buy and sell values.
• B / S — Buyer and seller share of total volume, expressed as percentages.
• Ref. Price — Reference price for breakout calculations, based on the latest pivot.
• Status — Indicates whether a breakout condition is currently active (True) or has failed.
10.3 Row definitions
1. Up volume / MA up volume – Displays current buy volume on the lower timeframe and its moving average over the lookback period.
2. Down volume / MA down volume – Shows current sell volume and its moving average; sell values are formatted in red for clarity.
3. Δ / ΣΔ – Lists the difference between buy and sell volume for the current bar and the cumulative delta volume over the lookback period.
4. Σ / MA Σ (Vol/MA) – Total volume (buy + sell) for the bar, with the ratio of this volume to its moving average; the right cell shows the average total volume.
5. B/S ratio – Buy and sell share of the total volume: current bar percentages and the average percentages across the lookback period.
6. Buyer Rank / Seller Rank – Ranks the bar’s buy and sell volumes among the last (n) bars; lower rank numbers indicate higher relative volume.
7. Σ Buy / Σ Sell – Sum of buy and sell volumes over the lookback window, indicating which side has traded more.
8. Breakout UP / DOWN – Shows the breakout thresholds (Ref. Price) and whether the breakout condition is active (True) or has failed.
9. Market Phase (Vol) – Reports the current volume‑only phase: Accumulation, Distribution or Neutral.
10. Money Flow – The final rows display dollar amounts and status:
– ↑ USD / Σ↑ USD – Buy dollars for the current bar and the cumulative sum over the money‑flow period.
– ↓ USD / Σ↓ USD – Sell dollars and their cumulative sum.
– Δ USD / ΣΔ USD – Net dollar difference (buy minus sell) for the bar and cumulatively.
– Money flow (bar) – Indicates whether the bar’s net dollar flow is positive (In), negative (Out) or neutral.
– Money flow Σ – Shows whether the cumulative net dollar flow across the chosen period is positive, negative or neutral.
The chart above shows a sequence of different signals from the indicator. A Bull Trap Risk appears after price briefly pushes above resistance but fails to hold, then a green Accum label identifies an accumulation phase. An upward breakout follows, confirmed by a Money flow in print. Later, a Sharp ↓ Risk warns of a possible sharp downturn; after price dips below support but quickly recovers, a Bear Trap label marks a false breakdown. The highlighted info table in the center summarizes key metrics at that moment, including current and average buy/sell volumes, net delta, total volume versus its moving average, breakout status (up and down), market phase (volume), and bar‑level and cumulative money flow (In/Out).
11. Conclusion & Final Remarks
This indicator was developed as a holistic study of market structure and order flow. It brings together several well‑known concepts from technical analysis—breakouts, accumulation and distribution phases, overbought and oversold extremes, bull and bear traps, sharp directional moves, market‑maker spread bars and money flow—into a single Pine Script tool. Each module is based on widely recognized trading ideas and was implemented after consulting reference materials and example strategies, so you can see in real time how these concepts interact on your chart.
A distinctive feature of this indicator is its reliance on per‑side volume: instead of tallying only total volume, it separately measures buy and sell transactions on a lower time frame. This approach gives a clearer view of who is in control—buyers or sellers—and helps filter breakouts, detect phases of accumulation or distribution, recognize potential traps, anticipate sharp moves and gauge whether liquidity providers are active. The money‑flow module extends this analysis by converting volume into currency values and tracking net inflow or outflow across a chosen window.
Although comprehensive, this indicator is intended solely as a guide. It highlights conditions and statistics that many traders find useful, but it does not generate trading signals or guarantee results. Ultimately, you remain responsible for your positions. Use the information presented here to inform your analysis, combine it with other tools and risk‑management techniques, and always make your own decisions when trading.
ValueAtTime█ OVERVIEW
This library is a Pine Script® programming tool for accessing historical values in a time series using UNIX timestamps . Its data structure and functions index values by time, allowing scripts to retrieve past values based on absolute timestamps or relative time offsets instead of relying on bar index offsets.
█ CONCEPTS
UNIX timestamps
In Pine Script®, a UNIX timestamp is an integer representing the number of milliseconds elapsed since January 1, 1970, at 00:00:00 UTC (the UNIX Epoch ). The timestamp is a unique, absolute representation of a specific point in time. Unlike a calendar date and time, a UNIX timestamp's meaning does not change relative to any time zone .
This library's functions process series values and corresponding UNIX timestamps in pairs , offering a simplified way to identify values that occur at or near distinct points in time instead of on specific bars.
Storing and retrieving time-value pairs
This library's `Data` type defines the structure for collecting time and value information in pairs. Objects of the `Data` type contain the following two fields:
• `times` – An array of "int" UNIX timestamps for each recorded value.
• `values` – An array of "float" values for each saved timestamp.
Each index in both arrays refers to a specific time-value pair. For instance, the `times` and `values` elements at index 0 represent the first saved timestamp and corresponding value. The library functions that maintain `Data` objects queue up to one time-value pair per bar into the object's arrays, where the saved timestamp represents the bar's opening time .
Because the `times` array contains a distinct UNIX timestamp for each item in the `values` array, it serves as a custom mapping for retrieving saved values. All the library functions that return information from a `Data` object use this simple two-step process to identify a value based on time:
1. Perform a binary search on the `times` array to find the earliest saved timestamp closest to the specified time or offset and get the element's index.
2. Access the element from the `values` array at the retrieved index, returning the stored value corresponding to the found timestamp.
Value search methods
There are several techniques programmers can use to identify historical values from corresponding timestamps. This library's functions include three different search methods to locate and retrieve values based on absolute times or relative time offsets:
Timestamp search
Find the value with the earliest saved timestamp closest to a specified timestamp.
Millisecond offset search
Find the value with the earliest saved timestamp closest to a specified number of milliseconds behind the current bar's opening time. This search method provides a time-based alternative to retrieving historical values at specific bar offsets.
Period offset search
Locate the value with the earliest saved timestamp closest to a defined period offset behind the current bar's opening time. The function calculates the span of the offset based on a period string . The "string" must contain one of the following unit tokens:
• "D" for days
• "W" for weeks
• "M" for months
• "Y" for years
• "YTD" for year-to-date, meaning the time elapsed since the beginning of the bar's opening year in the exchange time zone.
The period string can include a multiplier prefix for all supported units except "YTD" (e.g., "2W" for two weeks).
Note that the precise span covered by the "M", "Y", and "YTD" units varies across time. The "1M" period can cover 28, 29, 30, or 31 days, depending on the bar's opening month and year in the exchange time zone. The "1Y" period covers 365 or 366 days, depending on leap years. The "YTD" period's span changes with each new bar, because it always measures the time from the start of the current bar's opening year.
█ CALCULATIONS AND USE
This library's functions offer a flexible, structured approach to retrieving historical values at or near specific timestamps, millisecond offsets, or period offsets for different analytical needs.
See below for explanations of the exported functions and how to use them.
Retrieving single values
The library includes three functions that retrieve a single stored value using timestamp, millisecond offset, or period offset search methods:
• `valueAtTime()` – Locates the saved value with the earliest timestamp closest to a specified timestamp.
• `valueAtTimeOffset()` – Finds the saved value with the earliest timestamp closest to the specified number of milliseconds behind the current bar's opening time.
• `valueAtPeriodOffset()` – Finds the saved value with the earliest timestamp closest to the period-based offset behind the current bar's opening time.
Each function has two overloads for advanced and simple use cases. The first overload searches for a value in a user-specified `Data` object created by the `collectData()` function (see below). It returns a tuple containing the found value and the corresponding timestamp.
The second overload maintains a `Data` object internally to store and retrieve values for a specified `source` series. This overload returns a tuple containing the historical `source` value, the corresponding timestamp, and the current bar's `source` value, making it helpful for comparing past and present values from requested contexts.
Retrieving multiple values
The library includes the following functions to retrieve values from multiple historical points in time, facilitating calculations and comparisons with values retrieved across several intervals:
• `getDataAtTimes()` – Locates a past `source` value for each item in a `timestamps` array. Each retrieved value's timestamp represents the earliest time closest to one of the specified timestamps.
• `getDataAtTimeOffsets()` – Finds a past `source` value for each item in a `timeOffsets` array. Each retrieved value's timestamp represents the earliest time closest to one of the specified millisecond offsets behind the current bar's opening time.
• `getDataAtPeriodOffsets()` – Finds a past value for each item in a `periods` array. Each retrieved value's timestamp represents the earliest time closest to one of the specified period offsets behind the current bar's opening time.
Each function returns a tuple with arrays containing the found `source` values and their corresponding timestamps. In addition, the tuple includes the current `source` value and the symbol's description, which also makes these functions helpful for multi-interval comparisons using data from requested contexts.
Processing period inputs
When writing scripts that retrieve historical values based on several user-specified period offsets, the most concise approach is to create a single text input that allows users to list each period, then process the "string" list into an array for use in the `getDataAtPeriodOffsets()` function.
This library includes a `getArrayFromString()` function to provide a simple way to process strings containing comma-separated lists of periods. The function splits the specified `str` by its commas and returns an array containing every non-empty item in the list with surrounding whitespaces removed. View the example code to see how we use this function to process the value of a text area input .
Calculating period offset times
Because the exact amount of time covered by a specified period offset can vary, it is often helpful to verify the resulting times when using the `valueAtPeriodOffset()` or `getDataAtPeriodOffsets()` functions to ensure the calculations work as intended for your use case.
The library's `periodToTimestamp()` function calculates an offset timestamp from a given period and reference time. With this function, programmers can verify the time offsets in a period-based data search and use the calculated offset times in additional operations.
For periods with "D" or "W" units, the function calculates the time offset based on the absolute number of milliseconds the period covers (e.g., `86400000` for "1D"). For periods with "M", "Y", or "YTD" units, the function calculates an offset time based on the reference time's calendar date in the exchange time zone.
Collecting data
All the `getDataAt*()` functions, and the second overloads of the `valueAt*()` functions, collect and maintain data internally, meaning scripts do not require a separate `Data` object when using them. However, the first overloads of the `valueAt*()` functions do not collect data, because they retrieve values from a user-specified `Data` object.
For cases where a script requires a separate `Data` object for use with these overloads or other custom routines, this library exports the `collectData()` function. This function queues each bar's `source` value and opening timestamp into a `Data` object and returns the object's ID.
This function is particularly useful when searching for values from a specific series more than once. For instance, instead of using multiple calls to the second overloads of `valueAt*()` functions with the same `source` argument, programmers can call `collectData()` to store each bar's `source` and opening timestamp, then use the returned `Data` object's ID in calls to the first `valueAt*()` overloads to reduce memory usage.
The `collectData()` function and all the functions that collect data internally include two optional parameters for limiting the saved time-value pairs to a sliding window: `timeOffsetLimit` and `timeframeLimit`. When either has a non-na argument, the function restricts the collected data to the maximum number of recent bars covered by the specified millisecond- and timeframe-based intervals.
NOTE : All calls to the functions that collect data for a `source` series can execute up to once per bar or realtime tick, because each stored value requires a unique corresponding timestamp. Therefore, scripts cannot call these functions iteratively within a loop . If a call to these functions executes more than once inside a loop's scope, it causes a runtime error.
█ EXAMPLE CODE
The example code at the end of the script demonstrates one possible use case for this library's functions. The code retrieves historical price data at user-specified period offsets, calculates price returns for each period from the retrieved data, and then populates a table with the results.
The example code's process is as follows:
1. Input a list of periods – The user specifies a comma-separated list of period strings in the script's "Period list" input (e.g., "1W, 1M, 3M, 1Y, YTD"). Each item in the input list represents a period offset from the latest bar's opening time.
2. Process the period list – The example calls `getArrayFromString()` on the first bar to split the input list by its commas and construct an array of period strings.
3. Request historical data – The code uses a call to `getDataAtPeriodOffsets()` as the `expression` argument in a request.security() call to retrieve the closing prices of "1D" bars for each period included in the processed `periods` array.
4. Display information in a table – On the latest bar, the code uses the retrieved data to calculate price returns over each specified period, then populates a two-row table with the results. The cells for each return percentage are color-coded based on the magnitude and direction of the price change. The cells also include tooltips showing the compared daily bar's opening date in the exchange time zone.
█ NOTES
• This library's architecture relies on a user-defined type (UDT) for its data storage format. UDTs are blueprints from which scripts create objects , i.e., composite structures with fields containing independent values or references of any supported type.
• The library functions search through a `Data` object's `times` array using the array.binary_search_leftmost() function, which is more efficient than looping through collected data to identify matching timestamps. Note that this built-in works only for arrays with elements sorted in ascending order .
• Each function that collects data from a `source` series updates the values and times stored in a local `Data` object's arrays. If a single call to these functions were to execute in a loop , it would store multiple values with an identical timestamp, which can cause erroneous search behavior. To prevent looped calls to these functions, the library uses the `checkCall()` helper function in their scopes. This function maintains a counter that increases by one each time it executes on a confirmed bar. If the count exceeds the total number of bars, indicating the call executes more than once in a loop, it raises a runtime error .
• Typically, when requesting higher-timeframe data with request.security() while using barmerge.lookahead_on as the `lookahead` argument, the `expression` argument should be offset with the history-referencing operator to prevent lookahead bias on historical bars. However, the call in this script's example code enables lookahead without offsetting the `expression` because the script displays results only on the last historical bar and all realtime bars, where there is no future data to leak into the past. This call ensures the displayed results use the latest data available from the context on realtime bars.
Look first. Then leap.
█ EXPORTED TYPES
Data
A structure for storing successive timestamps and corresponding values from a dataset.
Fields:
times (array) : An "int" array containing a UNIX timestamp for each value in the `values` array.
values (array) : A "float" array containing values corresponding to the timestamps in the `times` array.
█ EXPORTED FUNCTIONS
getArrayFromString(str)
Splits a "string" into an array of substrings using the comma (`,`) as the delimiter. The function trims surrounding whitespace characters from each substring, and it excludes empty substrings from the result.
Parameters:
str (series string) : The "string" to split into an array based on its commas.
Returns: (array) An array of trimmed substrings from the specified `str`.
periodToTimestamp(period, referenceTime)
Calculates a UNIX timestamp representing the point offset behind a reference time by the amount of time within the specified `period`.
Parameters:
period (series string) : The period string, which determines the time offset of the returned timestamp. The specified argument must contain a unit and an optional multiplier (e.g., "1Y", "3M", "2W", "YTD"). Supported units are:
- "Y" for years.
- "M" for months.
- "W" for weeks.
- "D" for days.
- "YTD" (Year-to-date) for the span from the start of the `referenceTime` value's year in the exchange time zone. An argument with this unit cannot contain a multiplier.
referenceTime (series int) : The millisecond UNIX timestamp from which to calculate the offset time.
Returns: (int) A millisecond UNIX timestamp representing the offset time point behind the `referenceTime`.
collectData(source, timeOffsetLimit, timeframeLimit)
Collects `source` and `time` data successively across bars. The function stores the information within a `Data` object for use in other exported functions/methods, such as `valueAtTimeOffset()` and `valueAtPeriodOffset()`. Any call to this function cannot execute more than once per bar or realtime tick.
Parameters:
source (series float) : The source series to collect. The function stores each value in the series with an associated timestamp representing its corresponding bar's opening time.
timeOffsetLimit (simple int) : Optional. A time offset (range) in milliseconds. If specified, the function limits the collected data to the maximum number of bars covered by the range, with a minimum of one bar. If the call includes a non-empty `timeframeLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
timeframeLimit (simple string) : Optional. A valid timeframe string. If specified and not empty, the function limits the collected data to the maximum number of bars covered by the timeframe, with a minimum of one bar. If the call includes a non-na `timeOffsetLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
Returns: (Data) A `Data` object containing collected `source` values and corresponding timestamps over the allowed time range.
method valueAtTime(data, timestamp)
(Overload 1 of 2) Retrieves value and time data from a `Data` object's fields at the index of the earliest timestamp closest to the specified `timestamp`. Callable as a method or a function.
Parameters:
data (series Data) : The `Data` object containing the collected time and value data.
timestamp (series int) : The millisecond UNIX timestamp to search. The function returns data for the earliest saved timestamp that is closest to the value.
Returns: ( ) A tuple containing the following data from the `Data` object:
- The stored value corresponding to the identified timestamp ("float").
- The earliest saved timestamp that is closest to the specified `timestamp` ("int").
valueAtTime(source, timestamp, timeOffsetLimit, timeframeLimit)
(Overload 2 of 2) Retrieves `source` and time information for the earliest bar whose opening timestamp is closest to the specified `timestamp`. Any call to this function cannot execute more than once per bar or realtime tick.
Parameters:
source (series float) : The source series to analyze. The function stores each value in the series with an associated timestamp representing its corresponding bar's opening time.
timestamp (series int) : The millisecond UNIX timestamp to search. The function returns data for the earliest bar whose timestamp is closest to the value.
timeOffsetLimit (simple int) : Optional. A time offset (range) in milliseconds. If specified, the function limits the collected data to the maximum number of bars covered by the range, with a minimum of one bar. If the call includes a non-empty `timeframeLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
timeframeLimit (simple string) : (simple string) Optional. A valid timeframe string. If specified and not empty, the function limits the collected data to the maximum number of bars covered by the timeframe, with a minimum of one bar. If the call includes a non-na `timeOffsetLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
Returns: ( ) A tuple containing the following data:
- The `source` value corresponding to the identified timestamp ("float").
- The earliest bar's timestamp that is closest to the specified `timestamp` ("int").
- The current bar's `source` value ("float").
method valueAtTimeOffset(data, timeOffset)
(Overload 1 of 2) Retrieves value and time data from a `Data` object's fields at the index of the earliest saved timestamp closest to `timeOffset` milliseconds behind the current bar's opening time. Callable as a method or a function.
Parameters:
data (series Data) : The `Data` object containing the collected time and value data.
timeOffset (series int) : The millisecond offset behind the bar's opening time. The function returns data for the earliest saved timestamp that is closest to the calculated offset time.
Returns: ( ) A tuple containing the following data from the `Data` object:
- The stored value corresponding to the identified timestamp ("float").
- The earliest saved timestamp that is closest to `timeOffset` milliseconds before the current bar's opening time ("int").
valueAtTimeOffset(source, timeOffset, timeOffsetLimit, timeframeLimit)
(Overload 2 of 2) Retrieves `source` and time information for the earliest bar whose opening timestamp is closest to `timeOffset` milliseconds behind the current bar's opening time. Any call to this function cannot execute more than once per bar or realtime tick.
Parameters:
source (series float) : The source series to analyze. The function stores each value in the series with an associated timestamp representing its corresponding bar's opening time.
timeOffset (series int) : The millisecond offset behind the bar's opening time. The function returns data for the earliest bar's timestamp that is closest to the calculated offset time.
timeOffsetLimit (simple int) : Optional. A time offset (range) in milliseconds. If specified, the function limits the collected data to the maximum number of bars covered by the range, with a minimum of one bar. If the call includes a non-empty `timeframeLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
timeframeLimit (simple string) : Optional. A valid timeframe string. If specified and not empty, the function limits the collected data to the maximum number of bars covered by the timeframe, with a minimum of one bar. If the call includes a non-na `timeOffsetLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
Returns: ( ) A tuple containing the following data:
- The `source` value corresponding to the identified timestamp ("float").
- The earliest bar's timestamp that is closest to `timeOffset` milliseconds before the current bar's opening time ("int").
- The current bar's `source` value ("float").
method valueAtPeriodOffset(data, period)
(Overload 1 of 2) Retrieves value and time data from a `Data` object's fields at the index of the earliest timestamp closest to a calculated offset behind the current bar's opening time. The calculated offset represents the amount of time covered by the specified `period`. Callable as a method or a function.
Parameters:
data (series Data) : The `Data` object containing the collected time and value data.
period (series string) : The period string, which determines the calculated time offset. The specified argument must contain a unit and an optional multiplier (e.g., "1Y", "3M", "2W", "YTD"). Supported units are:
- "Y" for years.
- "M" for months.
- "W" for weeks.
- "D" for days.
- "YTD" (Year-to-date) for the span from the start of the current bar's year in the exchange time zone. An argument with this unit cannot contain a multiplier.
Returns: ( ) A tuple containing the following data from the `Data` object:
- The stored value corresponding to the identified timestamp ("float").
- The earliest saved timestamp that is closest to the calculated offset behind the bar's opening time ("int").
valueAtPeriodOffset(source, period, timeOffsetLimit, timeframeLimit)
(Overload 2 of 2) Retrieves `source` and time information for the earliest bar whose opening timestamp is closest to a calculated offset behind the current bar's opening time. The calculated offset represents the amount of time covered by the specified `period`. Any call to this function cannot execute more than once per bar or realtime tick.
Parameters:
source (series float) : The source series to analyze. The function stores each value in the series with an associated timestamp representing its corresponding bar's opening time.
period (series string) : The period string, which determines the calculated time offset. The specified argument must contain a unit and an optional multiplier (e.g., "1Y", "3M", "2W", "YTD"). Supported units are:
- "Y" for years.
- "M" for months.
- "W" for weeks.
- "D" for days.
- "YTD" (Year-to-date) for the span from the start of the current bar's year in the exchange time zone. An argument with this unit cannot contain a multiplier.
timeOffsetLimit (simple int) : Optional. A time offset (range) in milliseconds. If specified, the function limits the collected data to the maximum number of bars covered by the range, with a minimum of one bar. If the call includes a non-empty `timeframeLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
timeframeLimit (simple string) : Optional. A valid timeframe string. If specified and not empty, the function limits the collected data to the maximum number of bars covered by the timeframe, with a minimum of one bar. If the call includes a non-na `timeOffsetLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
Returns: ( ) A tuple containing the following data:
- The `source` value corresponding to the identified timestamp ("float").
- The earliest bar's timestamp that is closest to the calculated offset behind the current bar's opening time ("int").
- The current bar's `source` value ("float").
getDataAtTimes(timestamps, source, timeOffsetLimit, timeframeLimit)
Retrieves `source` and time information for each bar whose opening timestamp is the earliest one closest to one of the UNIX timestamps specified in the `timestamps` array. Any call to this function cannot execute more than once per bar or realtime tick.
Parameters:
timestamps (array) : An array of "int" values representing UNIX timestamps. The function retrieves `source` and time data for each element in this array.
source (series float) : The source series to analyze. The function stores each value in the series with an associated timestamp representing its corresponding bar's opening time.
timeOffsetLimit (simple int) : Optional. A time offset (range) in milliseconds. If specified, the function limits the collected data to the maximum number of bars covered by the range, with a minimum of one bar. If the call includes a non-empty `timeframeLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
timeframeLimit (simple string) : Optional. A valid timeframe string. If specified and not empty, the function limits the collected data to the maximum number of bars covered by the timeframe, with a minimum of one bar. If the call includes a non-na `timeOffsetLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
Returns: ( ) A tuple of the following data:
- An array containing a `source` value for each identified timestamp (array).
- An array containing an identified timestamp for each item in the `timestamps` array (array).
- The current bar's `source` value ("float").
- The symbol's description from `syminfo.description` ("string").
getDataAtTimeOffsets(timeOffsets, source, timeOffsetLimit, timeframeLimit)
Retrieves `source` and time information for each bar whose opening timestamp is the earliest one closest to one of the time offsets specified in the `timeOffsets` array. Each offset in the array represents the absolute number of milliseconds behind the current bar's opening time. Any call to this function cannot execute more than once per bar or realtime tick.
Parameters:
timeOffsets (array) : An array of "int" values representing the millisecond time offsets used in the search. The function retrieves `source` and time data for each element in this array. For example, the array ` ` specifies that the function returns data for the timestamps closest to one day and one week behind the current bar's opening time.
source (float) : (series float) The source series to analyze. The function stores each value in the series with an associated timestamp representing its corresponding bar's opening time.
timeOffsetLimit (simple int) : Optional. A time offset (range) in milliseconds. If specified, the function limits the collected data to the maximum number of bars covered by the range, with a minimum of one bar. If the call includes a non-empty `timeframeLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
timeframeLimit (simple string) : Optional. A valid timeframe string. If specified and not empty, the function limits the collected data to the maximum number of bars covered by the timeframe, with a minimum of one bar. If the call includes a non-na `timeOffsetLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
Returns: ( ) A tuple of the following data:
- An array containing a `source` value for each identified timestamp (array).
- An array containing an identified timestamp for each offset specified in the `timeOffsets` array (array).
- The current bar's `source` value ("float").
- The symbol's description from `syminfo.description` ("string").
getDataAtPeriodOffsets(periods, source, timeOffsetLimit, timeframeLimit)
Retrieves `source` and time information for each bar whose opening timestamp is the earliest one closest to a calculated offset behind the current bar's opening time. Each calculated offset represents the amount of time covered by a period specified in the `periods` array. Any call to this function cannot execute more than once per bar or realtime tick.
Parameters:
periods (array) : An array of period strings, which determines the time offsets used in the search. The function retrieves `source` and time data for each element in this array. For example, the array ` ` specifies that the function returns data for the timestamps closest to one day, week, and month behind the current bar's opening time. Each "string" in the array must contain a unit and an optional multiplier. Supported units are:
- "Y" for years.
- "M" for months.
- "W" for weeks.
- "D" for days.
- "YTD" (Year-to-date) for the span from the start of the current bar's year in the exchange time zone. An argument with this unit cannot contain a multiplier.
source (float) : (series float) The source series to analyze. The function stores each value in the series with an associated timestamp representing its corresponding bar's opening time.
timeOffsetLimit (simple int) : Optional. A time offset (range) in milliseconds. If specified, the function limits the collected data to the maximum number of bars covered by the range, with a minimum of one bar. If the call includes a non-empty `timeframeLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
timeframeLimit (simple string) : Optional. A valid timeframe string. If specified and not empty, the function limits the collected data to the maximum number of bars covered by the timeframe, with a minimum of one bar. If the call includes a non-na `timeOffsetLimit` value, the function limits the data using the largest number of bars covered by the two ranges. The default is `na`.
Returns: ( ) A tuple of the following data:
- An array containing a `source` value for each identified timestamp (array).
- An array containing an identified timestamp for each period specified in the `periods` array (array).
- The current bar's `source` value ("float").
- The symbol's description from `syminfo.description` ("string").
chrono_utilsLibrary "chrono_utils"
Collection of objects and common functions that are related to datetime windows session days and time
ranges. The main purpose of this library is to handle time-related functionality and make it easy to reason about a
future bar and see if it is part of a predefined user session and/or inside a datetime window. All existing session
functions I found in the documentation e.g. "not na(time(timeframe, session, timezone))" are not suitable for
strategies, since the execution of the orders is delayed by one bar due to the execution happening at the bar close.
So a prediction for the next bar is necessary. Moreover, a history operator with a negative value is not allowed e.g.
`not na(time(timeframe, session, timezone) )` expression is not valid. Thus, I created this library to overcome
this small but very important limitation. In the meantime, I added useful functionality to handle session-based
behavior. An interesting utility that emerged from this development is data anomaly detection where a comparison
between the prediction and the actual value is happening. If those two values are different then a data inconsistency
happens between the prediction bar and the actual bar (probably due to a holiday or half session day etc..)
exTimezone(timezone)
exTimezone - Convert extended timezone to timezone string
Parameters:
timezone (simple string) : - The timezone or a special string
Returns: string representing the timezone
nameOfDay(day)
nameOfDay - Convert the day id into a short nameOfDay
Parameters:
day (int) : - The day id to convert
Returns: - The short name of the day
today()
today - Get the day id of this day
Returns: - The day id
nthDayAfter(day, n)
nthDayAfter - Get the day id of n days after the given day
Parameters:
day (int) : - The day id of the reference day
n (int) : - The number of days to go forward
Returns: - The day id of the day that is n days after the reference day
nextDayAfter(day)
nextDayAfter - Get the day id of next day after the given day
Parameters:
day (int) : - The day id of the reference day
Returns: - The day id of the next day after the reference day
nthDayBefore(day, n)
nthDayBefore - Get the day id of n days before the given day
Parameters:
day (int) : - The day id of the reference day
n (int) : - The number of days to go forward
Returns: - The day id of the day that is n days before the reference day
prevDayBefore(day)
prevDayBefore - Get the day id of previous day before the given day
Parameters:
day (int) : - The day id of the reference day
Returns: - The day id of the previous day before the reference day
tomorrow()
tomorrow - Get the day id of the next day
Returns: - The next day day id
normalize(num, min, max)
normalizeHour - Check if number is inthe range of
Parameters:
num (int)
min (int)
max (int)
Returns: - The normalized number
normalizeHour(hourInDay)
normalizeHour - Check if hour is valid and return a noralized hour range from
Parameters:
hourInDay (int)
Returns: - The normalized hour
normalizeMinute(minuteInHour)
normalizeMinute - Check if minute is valid and return a noralized minute from
Parameters:
minuteInHour (int)
Returns: - The normalized minute
monthInMilliseconds(mon)
monthInMilliseconds - Calculate the miliseconds in one bar of the timeframe
Parameters:
mon (int) : - The month of reference to get the miliseconds
Returns: - The number of milliseconds of the month
barInMilliseconds()
barInMilliseconds - Calculate the miliseconds in one bar of the timeframe
Returns: - The number of milliseconds in one bar
method init(this, fromDateTime, toDateTime)
init - Initialize the time window object from boolean values of each session day
Namespace types: DateTimeWindow
Parameters:
this (DateTimeWindow) : - The time window object that will hold the from and to datetimes
fromDateTime (int) : - The starting datetime of the time window
toDateTime (int) : - The ending datetime of the time window
Returns: - The time window object
method init(this, refTimezone, chTimezone, fromDateTime, toDateTime)
init - Initialize the time window object from boolean values of each session day
Namespace types: DateTimeWindow
Parameters:
this (DateTimeWindow) : - The time window object that will hold the from and to datetimes
refTimezone (simple string) : - The timezone of reference of the 'from' and 'to' dates
chTimezone (simple string) : - The target timezone to convert the 'from' and 'to' dates
fromDateTime (int) : - The starting datetime of the time window
toDateTime (int) : - The ending datetime of the time window
Returns: - The time window object
method init(this, sun, mon, tue, wed, thu, fri, sat)
init - Initialize the session days object from boolean values of each session day
Namespace types: SessionDays
Parameters:
this (SessionDays) : - The session days object that will hold the day selection
sun (bool) : - Is Sunday a trading day?
mon (bool) : - Is Monday a trading day?
tue (bool) : - Is Tuesday a trading day?
wed (bool) : - Is Wednesday a trading day?
thu (bool) : - Is Thursday a trading day?
fri (bool) : - Is Friday a trading day?
sat (bool) : - Is Saturday a trading day?
Returns: - The session days objectfrom_chart
method init(this, unixTime)
init - Initialize the object from the hour and minute of the session time in exchange timezone (syminfo.timezone)
Namespace types: SessionTime
Parameters:
this (SessionTime) : - The session time object with the hour and minute of the time of the day
unixTime (int) : - The unix time
Returns: - The session time object
method init(this, hourInDay, minuteInHour)
init - Initialize the object from the hour and minute of the session time in exchange timezone (syminfo.timezone)
Namespace types: SessionTime
Parameters:
this (SessionTime) : - The session time object with the hour and minute of the time of the day
hourInDay (int) : - The hour of the time
minuteInHour (int) : - The minute of the time
Returns: - The session time object
method init(this, hourInDay, minuteInHour, refTimezone)
init - Initialize the object from the hour and minute of the session time
Namespace types: SessionTime
Parameters:
this (SessionTime) : - The session time object with the hour and minute of the time of the day
hourInDay (int) : - The hour of the time
minuteInHour (int) : - The minute of the time
refTimezone (string) : - The timezone of reference of the 'hour' and 'minute'
Returns: - The session time object
method init(this, startTime, endTime)
init - Initialize the object from the start and end session time in exchange timezone (syminfo.timezone)
Namespace types: SessionTimeRange
Parameters:
this (SessionTimeRange) : - The session time range object that will hold the start and end time of the daily session
startTime (SessionTime) : - The time the session begins
endTime (SessionTime) : - The time the session ends
Returns: - The session time range object
method init(this, startTimeHour, startTimeMinute, endTimeHour, endTimeMinute, refTimezone)
init - Initialize the object from the start and end session time
Namespace types: SessionTimeRange
Parameters:
this (SessionTimeRange) : - The session time range object that will hold the start and end time of the daily session
startTimeHour (int) : - The time hour the session begins
startTimeMinute (int) : - The time minute the session begins
endTimeHour (int) : - The time hour the session ends
endTimeMinute (int) : - The time minute the session ends
refTimezone (string)
Returns: - The session time range object
method init(this, days, timeRanges)
init - Initialize the user session object from session days and time range
Namespace types: UserSession
Parameters:
this (UserSession) : - The user-defined session object that will hold the day and the time range selection
days (SessionDays) : - The session days object that defines the days the session is happening
timeRanges (SessionTimeRange ) : - The array of all the session time ranges during a session day
Returns: - The user session object
method to_string(this)
to_string - Formats the time window into a human-readable string
Namespace types: DateTimeWindow
Parameters:
this (DateTimeWindow) : - The time window object with the from and to datetimes
Returns: - The string of the time window
method to_string(this)
to_string - Formats the session days into a human-readable string with short day names
Namespace types: SessionDays
Parameters:
this (SessionDays) : - The session days object with the day selection
Returns: - The string of the session day short names
method to_string(this)
to_string - Formats the session time into a human-readable string
Namespace types: SessionTime
Parameters:
this (SessionTime) : - The session time object with the hour and minute of the time of the day
Returns: - The string of the session time
method to_string(this)
to_string - Formats the session time into a human-readable string
Namespace types: SessionTimeRange
Parameters:
this (SessionTimeRange) : - The session time range object with the start and end time of the daily session
Returns: - The string of the session time
method to_string(this)
to_string - Formats the user session into a human-readable string
Namespace types: UserSession
Parameters:
this (UserSession) : - The user-defined session object with the day and the time range selection
Returns: - The string of the user session
method to_string(this)
to_string - Formats the bar into a human-readable string
Namespace types: Bar
Parameters:
this (Bar) : - The bar object with the open and close times
Returns: - The string of the bar times
method to_string(this)
to_string - Formats the chart session into a human-readable string
Namespace types: ChartSession
Parameters:
this (ChartSession) : - The chart session object that contains the days and the time range shown in the chart
Returns: - The string of the chart session
method get_size_in_secs(this)
get_size_in_secs - Count the seconds from start to end in the given timeframe
Namespace types: DateTimeWindow
Parameters:
this (DateTimeWindow) : - The time window object with the from and to datetimes
Returns: - The number of seconds inside the time widow for the given timeframe
method get_size_in_secs(this)
get_size_in_secs - Calculate the seconds inside the session
Namespace types: SessionTimeRange
Parameters:
this (SessionTimeRange) : - The session time range object with the start and end time of the daily session
Returns: - The number of seconds inside the session
method get_size_in_bars(this)
get_size_in_bars - Count the bars from start to end in the given timeframe
Namespace types: DateTimeWindow
Parameters:
this (DateTimeWindow) : - The time window object with the from and to datetimes
Returns: - The number of bars inside the time widow for the given timeframe
method get_size_in_bars(this)
get_size_in_bars - Calculate the bars inside the session
Namespace types: SessionTimeRange
Parameters:
this (SessionTimeRange) : - The session time range object with the start and end time of the daily session
Returns: - The number of bars inside the session for the given timeframe
method from_chart(this)
from_chart - Initialize the session days object from the chart
Namespace types: SessionDays
Parameters:
this (SessionDays) : - The session days object that will hold the day selection
Returns: - The user session object
method from_chart(this)
from_chart - Initialize the session time range object from the chart
Namespace types: SessionTimeRange
Parameters:
this (SessionTimeRange) : - The session time range object that will hold the start and end time of the daily session
Returns: - The session time range object
method from_chart(this)
from_chart - Initialize the session object from the chart
Namespace types: ChartSession
Parameters:
this (ChartSession) : - The chart session object that will hold the days and the time range shown in the chart
Returns: - The chart session object
method to_sess_string(this)
to_sess_string - Formats the session days into a session string with day ids
Namespace types: SessionDays
Parameters:
this (SessionDays) : - The session days object
Returns: - The string of the session day ids
method to_sess_string(this)
to_sess_string - Formats the session time into a session string
Namespace types: SessionTime
Parameters:
this (SessionTime) : - The session time object with the hour and minute of the time of the day
Returns: - The string of the session time
method to_sess_string(this)
to_sess_string - Formats the session time into a session string
Namespace types: SessionTimeRange
Parameters:
this (SessionTimeRange) : - The session time range object with the start and end time of the daily session
Returns: - The string of the session time
method to_sess_string(this)
to_sess_string - Formats the user session into a session string
Namespace types: UserSession
Parameters:
this (UserSession) : - The user-defined session object with the day and the time range selection
Returns: - The string of the user session
method to_sess_string(this)
to_sess_string - Formats the chart session into a session string
Namespace types: ChartSession
Parameters:
this (ChartSession) : - The chart session object that contains the days and the time range shown in the chart
Returns: - The string of the chart session
method from_sess_string(this, sess)
from_sess_string - Initialize the session days object from the session string
Namespace types: SessionDays
Parameters:
this (SessionDays) : - The session days object that will hold the day selection
sess (string) : - The session string part that represents the days
Returns: - The session days object
method from_sess_string(this, sess)
from_sess_string - Initialize the session time object from the session string in exchange timezone (syminfo.timezone)
Namespace types: SessionTime
Parameters:
this (SessionTime) : - The session time object that will hold the hour and minute of the time
sess (string) : - The session string part that represents the time HHmm
Returns: - The session time object
method from_sess_string(this, sess, refTimezone)
from_sess_string - Initialize the session time object from the session string
Namespace types: SessionTime
Parameters:
this (SessionTime) : - The session time object that will hold the hour and minute of the time
sess (string) : - The session string part that represents the time HHmm
refTimezone (simple string) : - The timezone of reference of the 'hour' and 'minute'
Returns: - The session time object
method from_sess_string(this, sess)
from_sess_string - Initialize the session time range object from the session string in exchange timezone (syminfo.timezone)
Namespace types: SessionTimeRange
Parameters:
this (SessionTimeRange) : - The session time range object that will hold the start and end time of the daily session
sess (string) : - The session string part that represents the time range HHmm-HHmm
Returns: - The session time range object
method from_sess_string(this, sess, refTimezone)
from_sess_string - Initialize the session time range object from the session string
Namespace types: SessionTimeRange
Parameters:
this (SessionTimeRange) : - The session time range object that will hold the start and end time of the daily session
sess (string) : - The session string part that represents the time range HHmm-HHmm
refTimezone (simple string) : - The timezone of reference of the time ranges
Returns: - The session time range object
method from_sess_string(this, sess)
from_sess_string - Initialize the user session object from the session string in exchange timezone (syminfo.timezone)
Namespace types: UserSession
Parameters:
this (UserSession) : - The user-defined session object that will hold the day and the time range selection
sess (string) : - The session string that represents the user session HHmm-HHmm,HHmm-HHmm:ddddddd
Returns: - The session time range object
method from_sess_string(this, sess, refTimezone)
from_sess_string - Initialize the user session object from the session string
Namespace types: UserSession
Parameters:
this (UserSession) : - The user-defined session object that will hold the day and the time range selection
sess (string) : - The session string that represents the user session HHmm-HHmm,HHmm-HHmm:ddddddd
refTimezone (simple string) : - The timezone of reference of the time ranges
Returns: - The session time range object
method nth_day_after(this, day, n)
nth_day_after - The nth day after the given day that is a session day (true) in the object
Namespace types: SessionDays
Parameters:
this (SessionDays) : - The session days object with the day selection
day (int) : - The day id of the reference day
n (int) : - The number of days after
Returns: - The day id of the nth session day of the week after the given day
method nth_day_before(this, day, n)
nth_day_before - The nth day before the given day that is a session day (true) in the object
Namespace types: SessionDays
Parameters:
this (SessionDays) : - The session days object with the day selection
day (int) : - The day id of the reference day
n (int) : - The number of days after
Returns: - The day id of the nth session day of the week before the given day
method next_day(this)
next_day - The next day that is a session day (true) in the object
Namespace types: SessionDays
Parameters:
this (SessionDays) : - The session days object with the day selection
Returns: - The day id of the next session day of the week
method previous_day(this)
previous_day - The previous day that is session day (true) in the object
Namespace types: SessionDays
Parameters:
this (SessionDays) : - The session days object with the day selection
Returns: - The day id of the previous session day of the week
method get_sec_in_day(this)
get_sec_in_day - Count the seconds since the start of the day this session time represents
Namespace types: SessionTime
Parameters:
this (SessionTime) : - The session time object with the hour and minute of the time of the day
Returns: - The number of seconds passed from the start of the day until that session time
method get_ms_in_day(this)
get_ms_in_day - Count the milliseconds since the start of the day this session time represents
Namespace types: SessionTime
Parameters:
this (SessionTime) : - The session time object with the hour and minute of the time of the day
Returns: - The number of milliseconds passed from the start of the day until that session time
method eq(this, other)
eq - Compare two bars
Namespace types: Bar
Parameters:
this (Bar) : - The bar object with the open and close times
other (Bar) : - The bar object to compare with
Returns: - Whether this bar is equal to the other one
method get_open_time(this)
get_open_time - The open time object
Namespace types: Bar
Parameters:
this (Bar) : - The bar object with the open and close times
Returns: - The open time object
method get_close_time(this)
get_close_time - The close time object
Namespace types: Bar
Parameters:
this (Bar) : - The bar object with the open and close times
Returns: - The close time object
method get_time_range(this)
get_time_range - Get the time range of the bar
Namespace types: Bar
Parameters:
this (Bar) : - The bar object with the open and close times
Returns: - The time range that the bar is in
getBarNow()
getBarNow - Get the current bar object with time and time_close timestamps
Returns: - The current bar
getFixedBarNow()
getFixedBarNow - Get the current bar with fixed width defined by the timeframe. Note: There are case like SPX 15min timeframe where the last session bar is only 10min. This will return a bar of 15 minutes
Returns: - The current bar
method is_in_window(this, win)
is_in_window - Check if the given bar is between the start and end dates of the window
Namespace types: Bar
Parameters:
this (Bar) : - The bar to check if it is between the from and to datetimes of the window
win (DateTimeWindow) : - The time window object with the from and to datetimes
Returns: - Whether the current bar is inside the datetime window
method is_in_timerange(this, rng)
is_in_timerange - Check if the given bar is inside the session time range
Namespace types: Bar
Parameters:
this (Bar) : - The bar to check if it is between the from and to datetimes
rng (SessionTimeRange) : - The session time range object with the start and end time of the daily session
Returns: - Whether the bar is inside the session time range and if this part of the next trading day
method is_in_days(this, days)
is_in_days - Check if the given bar is inside the session days
Namespace types: Bar
Parameters:
this (Bar) : - The bar to check if its day is a trading day
days (SessionDays) : - The session days object with the day selection
Returns: - Whether the current bar day is inside the session
method is_in_session(this, sess)
is_in_session - Check if the given bar is inside the session as defined by the input params (what "not na(time(timeframe.period, this.to_sess_string()) )" should return if you could write it
Namespace types: Bar
Parameters:
this (Bar) : - The bar to check if it is between the from and to datetimes
sess (UserSession) : - The user-defined session object with the day and the time range selection
Returns: - Whether the current time is inside the session
method next_bar(this, offsetBars)
next_bar - Predicts the next bars open and close time based on the charts session
Namespace types: ChartSession
Parameters:
this (ChartSession) : - The chart session object that contains the days and the time range shown in the chart
offsetBars (simple int) : - The number of bars forward
Returns: - Whether the current time is inside the session
DateTimeWindow
DateTimeWindow - Object that represents a datetime window with a beginning and an end
Fields:
fromDateTime (series int) : - The beginning of the datetime window
toDateTime (series int) : - The end of the datetime window
SessionDays
SessionDays - Object that represent the trading days of the week
Fields:
days (map) : - The map that contains all days of the week and their session flag
SessionTime
SessionTime - Object that represents the time (hour and minutes)
Fields:
hourInDay (series int) : - The hour of the day that ranges from 0 to 24
minuteInHour (series int) : - The minute of the hour that ranges from 0 to 59
minuteInDay (series int) : - The minute of the day that ranges from 0 to 1440. They will be calculated based on hourInDay and minuteInHour when method is called
SessionTimeRange
SessionTimeRange - Object that represents a range that extends from the start to the end time
Fields:
startTime (SessionTime) : - The beginning of the time range
endTime (SessionTime) : - The end of the time range
isOvernight (series bool) : - Whether or not this is an overnight time range
UserSession
UserSession - Object that represents a user-defined session
Fields:
days (SessionDays) : - The map of the user-defined trading days
timeRanges (SessionTimeRange ) : - The array with all time ranges of the user-defined session during the trading days
Bar
Bar - Object that represents the bars' open and close times
Fields:
openUnixTime (series int) : - The open time of the bar
closeUnixTime (series int) : - The close time of the bar
chartDayOfWeek (series int)
ChartSession
ChartSession - Object that represents the default session that is shown in the chart
Fields:
days (SessionDays) : - A map with the trading days shown in the chart
timeRange (SessionTimeRange) : - The time range of the session during a trading day
isFinalized (series bool)
`security()` revisited [PineCoders]NOTE
The non-repainting technique in this publication that relies on bar states is now deprecated, as we have identified inconsistencies that undermine its credibility as a universal solution. The outputs that use the technique are still available for reference in this publication. However, we do not endorse its usage. See this publication for more information about the current best practices for requesting HTF data and why they work.
█ OVERVIEW
This script presents a new function to help coders use security() in both repainting and non-repainting modes. We revisit this often misunderstood and misused function, and explain its behavior in different contexts, in the hope of dispelling some of the coder lure surrounding it. The function is incredibly powerful, yet misused, it can become a dangerous WMD and an instrument of deception, for both coders and traders.
We will discuss:
• How to use our new `f_security()` function.
• The behavior of Pine code and security() on the three very different types of bars that make up any chart.
• Why what you see on a chart is a simulation, and should be taken with a grain of salt.
• Why we are presenting a new version of a function handling security() calls.
• Other topics of interest to coders using higher timeframe (HTF) data.
█ WARNING
We have tried to deliver a function that is simple to use and will, in non-repainting mode, produce reliable results for both experienced and novice coders. If you are a novice coder, stick to our recommendations to avoid getting into trouble, and DO NOT change our `f_security()` function when using it. Use `false` as the function's last argument and refrain from using your script at smaller timeframes than the chart's. To call our function to fetch a non-repainting value of close from the 1D timeframe, use:
f_security(_sym, _res, _src, _rep) => security(_sym, _res, _src )
previousDayClose = f_security(syminfo.tickerid, "D", close, false)
If that's all you're interested in, you are done.
If you choose to ignore our recommendation and use the function in repainting mode by changing the `false` in there for `true`, we sincerely hope you read the rest of our ramblings before you do so, to understand the consequences of your choice.
Let's now have a look at what security() is showing you. There is a lot to cover, so buckle up! But before we dig in, one last thing.
What is a chart?
A chart is a graphic representation of events that occur in markets. As any representation, it is not reality, but rather a model of reality. As Scott Page eloquently states in The Model Thinker : "All models are wrong; many are useful". Having in mind that both chart bars and plots on our charts are imperfect and incomplete renderings of what actually occurred in realtime markets puts us coders in a place from where we can better understand the nature of, and the causes underlying the inevitable compromises necessary to build the data series our code uses, and print chart bars.
Traders or coders complaining that charts do not reflect reality act like someone who would complain that the word "dog" is not a real dog. Let's recognize that we are dealing with models here, and try to understand them the best we can. Sure, models can be improved; TradingView is constantly improving the quality of the information displayed on charts, but charts nevertheless remain mere translations. Plots of data fetched through security() being modelized renderings of what occurs at higher timeframes, coders will build more useful and reliable tools for both themselves and traders if they endeavor to perfect their understanding of the abstractions they are working with. We hope this publication helps you in this pursuit.
█ FEATURES
This script's "Inputs" tab has four settings:
• Repaint : Determines whether the functions will use their repainting or non-repainting mode.
Note that the setting will not affect the behavior of the yellow plot, as it always repaints.
• Source : The source fetched by the security() calls.
• Timeframe : The timeframe used for the security() calls. If it is lower than the chart's timeframe, a warning appears.
• Show timeframe reminder : Displays a reminder of the timeframe after the last bar.
█ THE CHART
The chart shows two different pieces of information and we want to discuss other topics in this section, so we will be covering:
A — The type of chart bars we are looking at, indicated by the colored band at the top.
B — The plots resulting of calling security() with the close price in different ways.
C — Points of interest on the chart.
A — Chart bars
The colored band at the top shows the three types of bars that any chart on a live market will print. It is critical for coders to understand the important distinctions between each type of bar:
1 — Gray : Historical bars, which are bars that were already closed when the script was run on them.
2 — Red : Elapsed realtime bars, i.e., realtime bars that have run their course and closed.
The state of script calculations showing on those bars is that of the last time they were made, when the realtime bar closed.
3 — Green : The realtime bar. Only the rightmost bar on the chart can be the realtime bar at any given time, and only when the chart's market is active.
Refer to the Pine User Manual's Execution model page for a more detailed explanation of these types of bars.
B — Plots
The chart shows the result of letting our 5sec chart run for a few minutes with the following settings: "Repaint" = "On" (the default is "Off"), "Source" = `close` and "Timeframe" = 1min. The five lines plotted are the following. They have progressively thinner widths:
1 — Yellow : A normal, repainting security() call.
2 — Silver : Our recommended security() function.
3 — Fuchsia : Our recommended way of achieving the same result as our security() function, for cases when the source used is a function returning a tuple.
4 — White : The method we previously recommended in our MTF Selection Framework , which uses two distinct security() calls.
5 — Black : A lame attempt at fooling traders that MUST be avoided.
All lines except the first one in yellow will vary depending on the "Repaint" setting in the script's inputs. The first plot does not change because, contrary to all other plots, it contains no conditional code to adapt to repainting/no-repainting modes; it is a simple security() call showing its default behavior.
C — Points of interest on the chart
Historical bars do not show actual repainting behavior
To appreciate what a repainting security() call will plot in realtime, one must look at the realtime bar and at elapsed realtime bars, the bars where the top line is green or red on the chart at the top of this page. There you can see how the plots go up and down, following the close value of each successive chart bar making up a single bar of the higher timeframe. You would see the same behavior in "Replay" mode. In the realtime bar, the movement of repainting plots will vary with the source you are fetching: open will not move after a new timeframe opens, low and high will change when a new low or high are found, close will follow the last feed update. If you are fetching a value calculated by a function, it may also change on each update.
Now notice how different the plots are on historical bars. There, the plot shows the close of the previously completed timeframe for the whole duration of the current timeframe, until on its last bar the price updates to the current timeframe's close when it is confirmed (if the timeframe's last bar is missing, the plot will only update on the next timeframe's first bar). That last bar is the only one showing where the plot would end if that timeframe's bars had elapsed in realtime. If one doesn't understand this, one cannot properly visualize how his script will calculate in realtime when using repainting. Additionally, as published scripts typically show charts where the script has only run on historical bars, they are, in fact, misleading traders who will naturally assume the script will behave the same way on realtime bars.
Non-repainting plots are more accurate on historical bars
Now consider this chart, where we are using the same settings as on the chart used to publish this script, except that we have turned "Repainting" off this time:
The yellow line here is our reference, repainting line, so although repainting is turned off, it is still repainting, as expected. Because repainting is now off, however, plots on historical bars show the previous timeframe's close until the first bar of a new timeframe, at which point the plot updates. This correctly reflects the behavior of the script in the realtime bar, where because we are offsetting the series by one, we are always showing the previously calculated—and thus confirmed—higher timeframe value. This means that in realtime, we will only get the previous timeframe's values one bar after the timeframe's last bar has elapsed, at the open of the first bar of a new timeframe. Historical and elapsed realtime bars will not actually show this nuance because they reflect the state of calculations made on their close , but we can see the plot update on that bar nonetheless.
► This more accurate representation on historical bars of what will happen in the realtime bar is one of the two key reasons why using non-repainting data is preferable.
The other is that in realtime, your script will be using more reliable data and behave more consistently.
Misleading plots
Valiant attempts by coders to show non-repainting, higher timeframe data updating earlier than on our chart are futile. If updates occur one bar earlier because coders use the repainting version of the function, then so be it, but they must then also accept that their historical bars are not displaying information that is as accurate. Not informing script users of this is to mislead them. Coders should also be aware that if they choose to use repainting data in realtime, they are sacrificing reliability to speed and may be running a strategy that behaves very differently from the one they backtested, thus invalidating their tests.
When, however, coders make what are supposed to be non-repainting plots plot artificially early on historical bars, as in examples "c4" and "c5" of our script, they would want us to believe they have achieved the miracle of time travel. Our understanding of the current state of science dictates that for now, this is impossible. Using such techniques in scripts is plainly misleading, and public scripts using them will be moderated. We are coding trading tools here—not video games. Elementary ethics prescribe that we should not mislead traders, even if it means not being able to show sexy plots. As the great Feynman said: You should not fool the layman when you're talking as a scientist.
You can readily appreciate the fantasy plot of "c4", the thinnest line in black, by comparing its supposedly non-repainting behavior between historical bars and realtime bars. After updating—by miracle—as early as the wide yellow line that is repainting, it suddenly moves in a more realistic place when the script is running in realtime, in synch with our non-repainting lines. The "c5" version does not plot on the chart, but it displays in the Data Window. It is even worse than "c4" in that it also updates magically early on historical bars, but goes on to evaluate like the repainting yellow line in realtime, except one bar late.
Data Window
The Data Window shows the values of the chart's plots, then the values of both the inside and outside offsets used in our calculations, so you can see them change bar by bar. Notice their differences between historical and elapsed realtime bars, and the realtime bar itself. If you do not know about the Data Window, have a look at this essential tool for Pine coders in the Pine User Manual's page on Debugging . The conditional expressions used to calculate the offsets may seem tortuous but their objective is quite simple. When repainting is on, we use this form, so with no offset on all bars:
security(ticker, i_timeframe, i_source )
// which is equivalent to:
security(ticker, i_timeframe, i_source)
When repainting is off, we use two different and inverted offsets on historical bars and the realtime bar:
// Historical bars:
security(ticker, i_timeframe, i_source )
// Realtime bar (and thus, elapsed realtime bars):
security(ticker, i_timeframe, i_source )
The offsets in the first line show how we prevent repainting on historical bars without the need for the `lookahead` parameter. We use the value of the function call on the chart's previous bar. Since values between the repainting and non-repainting versions only differ on the timeframe's last bar, we can use the previous value so that the update only occurs on the timeframe's first bar, as it will in realtime when not repainting.
In the realtime bar, we use the second call, where the offsets are inverted. This is because if we used the first call in realtime, we would be fetching the value of the repainting function on the previous bar, so the close of the last bar. What we want, instead, is the data from the previous, higher timeframe bar , which has elapsed and is confirmed, and thus will not change throughout realtime bars, except on the first constituent chart bar belonging to a new higher timeframe.
After the offsets, the Data Window shows values for the `barstate.*` variables we use in our calculations.
█ NOTES
Why are we revisiting security() ?
For four reasons:
1 — We were seeing coders misuse our `f_secureSecurity()` function presented in How to avoid repainting when using security() .
Some novice coders were modifying the offset used with the history-referencing operator in the function, making it zero instead of one,
which to our horror, caused look-ahead bias when used with `lookahead = barmerge.lookahead_on`.
We wanted to present a safer function which avoids introducing the dreaded "lookahead" in the scripts of unsuspecting coders.
2 — The popularity of security() in screener-type scripts where coders need to use the full 40 calls allowed per script made us want to propose
a solid method of allowing coders to offer a repainting/no-repainting choice to their script users with only one security() call.
3 — We wanted to explain why some alternatives we see circulating are inadequate and produce misleading behavior.
4 — Our previous publication on security() focused on how to avoid repainting, yet many other considerations worthy of attention are not related to repainting.
Handling tuples
When sending function calls that return tuples with security() , our `f_security()` function will not work because Pine does not allow us to use the history-referencing operator with tuple return values. The solution is to integrate the inside offset to your function's arguments, use it to offset the results the function is returning, and then add the outside offset in a reassignment of the tuple variables, after security() returns its values to the script, as we do in our "c2" example.
Does it repaint?
We're pretty sure Wilder was not asked very often if RSI repainted. Why? Because it wasn't in fashion—and largely unnecessary—to ask that sort of question in the 80's. Many traders back then used daily charts only, and indicator values were calculated at the day's close, so everybody knew what they were getting. Additionally, indicator values were calculated by generally reputable outfits or traders themselves, so data was pretty reliable. Today, almost anybody can write a simple indicator, and the programming languages used to write them are complex enough for some coders lacking the caution, know-how or ethics of the best professional coders, to get in over their heads and produce code that does not work the way they think it does.
As we hope to have clearly demonstrated, traders do have legitimate cause to ask if MTF scripts repaint or not when authors do not specify it in their script's description.
► We recommend that authors always use our `f_security()` with `false` as the last argument to avoid repainting when fetching data dependent on OHLCV information. This is the only way to obtain reliable HTF data. If you want to offer users a choice, make non-repainting mode the default, so that if users choose repainting, it will be their responsibility. Non-repainting security() calls are also the only way for scripts to show historical behavior that matches the script's realtime behavior, so you are not misleading traders. Additionally, non-repainting HTF data is the only way that non-repainting alerts can be configured on MTF scripts, as users of MTF scripts cannot prevent their alerts from repainting by simply configuring them to trigger on the bar's close.
Data feeds
A chart at one timeframe is made up of multiple feeds that mesh seamlessly to form one chart. Historical bars can use one feed, and the realtime bar another, which brokers/exchanges can sometimes update retroactively so that elapsed realtime bars will reappear with very slight modifications when the browser's tab is refreshed. Intraday and daily chart prices also very often originate from different feeds supplied by brokers/exchanges. That is why security() calls at higher timeframes may be using a completely different feed than the chart, and explains why the daily high value, for example, can vary between timeframes. Volume information can also vary considerably between intraday and daily feeds in markets like stocks, because more volume information becomes available at the end of day. It is thus expected behavior—and not a bug—to see data variations between timeframes.
Another point to keep in mind concerning feeds it that when you are using a repainting security() plot in realtime, you will sometimes see discrepancies between its plot and the realtime bars. An artefact revealing these inconsistencies can be seen when security() plots sometimes skip a realtime chart bar during periods of high market activity. This occurs because of races between the chart and the security() feeds, which are being monitored by independent, concurrent processes. A blue arrow on the chart indicates such an occurrence. This is another cause of repainting, where realtime bar-building logic can produce different outcomes on one closing price. It is also another argument supporting our recommendation to use non-repainting data.
Alternatives
There is an alternative to using security() in some conditions. If all you need are OHLC prices of a higher timeframe, you can use a technique like the one Duyck demonstrates in his security free MTF example - JD script. It has the great advantage of displaying actual repainting values on historical bars, which mimic the code's behavior in the realtime bar—or at least on elapsed realtime bars, contrary to a repainting security() plot. It has the disadvantage of using the current chart's TF data feed prices, whereas higher timeframe data feeds may contain different and more reliable prices when they are compiled at the end of the day. In its current state, it also does not allow for a repainting/no-repainting choice.
When `lookahead` is useful
When retrieving non-price data, or in special cases, for experiments, it can be useful to use `lookahead`. One example is our Backtesting on Non-Standard Charts: Caution! script where we are fetching prices of standard chart bars from non-standard charts.
Warning users
Normal use of security() dictates that it only be used at timeframes equal to or higher than the chart's. To prevent users from inadvertently using your script in contexts where it will not produce expected behavior, it is good practice to warn them when their chart is on a higher timeframe than the one in the script's "Timeframe" field. Our `f_tfReminderAndErrorCheck()` function in this script does that. It can also print a reminder of the higher timeframe. It uses one security() call.
Intrabar timeframes
security() is not supported by TradingView when used with timeframes lower than the chart's. While it is still possible to use security() at intrabar timeframes, it then behaves differently. If no care is taken to send a function specifically written to handle the successive intrabars, security() will return the value of the last intrabar in the chart's timeframe, so the last 1H bar in the current 1D bar, if called at "60" from a "D" chart timeframe. If you are an advanced coder, see our FAQ entry on the techniques involved in processing intrabar timeframes. Using intrabar timeframes comes with important limitations, which you must understand and explain to traders if you choose to make scripts using the technique available to others. Special care should also be taken to thoroughly test this type of script. Novice coders should refrain from getting involved in this.
█ TERMINOLOGY
Timeframe
Timeframe , interval and resolution are all being used to name the concept of timeframe. We have, in the past, used "timeframe" and "resolution" more or less interchangeably. Recently, members from the Pine and PineCoders team have decided to settle on "timeframe", so from hereon we will be sticking to that term.
Multi-timeframe (MTF)
Some coders use "multi-timeframe" or "MTF" to name what are in fact "multi-period" calculations, as when they use MAs of progressively longer periods. We consider that a misleading use of "multi-timeframe", which should be reserved for code using calculations actually made from another timeframe's context and using security() , safe for scripts like Duyck's one mentioned earlier, or TradingView's Relative Volume at Time , which use a user-selected timeframe as an anchor to reset calculations. Calculations made at the chart's timeframe by varying the period of MAs or other rolling window calculations should be called "multi-period", and "MTF-anchored" could be used for scripts that reset calculations on timeframe boundaries.
Colophon
Our script was written using the PineCoders Coding Conventions for Pine .
The description was formatted using the techniques explained in the How We Write and Format Script Descriptions PineCoders publication.
Snippets were lifted from our MTF Selection Framework , then massaged to create the `f_tfReminderAndErrorCheck()` function.
█ THANKS
Thanks to apozdnyakov for his help with the innards of security() .
Thanks to bmistiaen for proofreading our description.
Look first. Then leap.
Waindrops [Makit0]█ OVERALL
Plot waindrops (custom volume profiles) on user defined periods, for each period you get high and low, it slices each period in half to get independent vwap, volume profile and the volume traded per price at each half.
It works on intraday charts only, up to 720m (12H). It can plot balanced or unbalanced waindrops, and volume profiles up to 24H sessions.
As example you can setup unbalanced periods to get independent volume profiles for the overnight and cash sessions on the futures market, or 24H periods to get the full session volume profile of EURUSD
The purpose of this indicator is twofold:
1 — from a Chartist point of view, to have an indicator which displays the volume in a more readable way
2 — from a Pine Coder point of view, to have an example of use for two very powerful tools on Pine Script:
• the recently updated drawing limit to 500 (from 50)
• the recently ability to use drawings arrays (lines and labels)
If you are new to Pine Script and you are learning how to code, I hope you read all the code and comments on this indicator, all is designed for you,
the variables and functions names, the sometimes too big explanations, the overall structure of the code, all is intended as an example on how to code
in Pine Script a specific indicator from a very good specification in form of white paper
If you wanna learn Pine Script form scratch just start HERE
In case you have any kind of problem with Pine Script please use some of the awesome resources at our disposal: USRMAN , REFMAN , AWESOMENESS , MAGIC
█ FEATURES
Waindrops are a different way of seeing the volume and price plotted in a chart, its a volume profile indicator where you can see the volume of each price level
plotted as a vertical histogram for each half of a custom period. By default the period is 60 so it plots an independent volume profile each 30m
You can think of each waindrop as an user defined candlestick or bar with four key values:
• high of the period
• low of the period
• left vwap (volume weighted average price of the first half period)
• right vwap (volume weighted average price of the second half period)
The waindrop can have 3 different colors (configurable by the user):
• GREEN: when the right vwap is higher than the left vwap (bullish sentiment )
• RED: when the right vwap is lower than the left vwap (bearish sentiment )
• BLUE: when the right vwap is equal than the left vwap ( neutral sentiment )
KEY FEATURES
• Help menu
• Custom periods
• Central bars
• Left/Right VWAPs
• Custom central bars and vwaps: color and pixels
• Highly configurable volume histogram: execution window, ticks, pixels, color, update frequency and fine tuning the neutral meaning
• Volume labels with custom size and color
• Tracking price dot to be able to see the current price when you hide your default candlesticks or bars
█ SETTINGS
Click here or set any impar period to see the HELP INFO : show the HELP INFO, if it is activated the indicator will not plot
PERIOD SIZE (max 2880 min) : waindrop size in minutes, default 60, max 2880 to allow the first half of a 48H period as a full session volume profile
BARS : show the central and vwap bars, default true
Central bars : show the central bars, default true
VWAP bars : show the left and right vwap bars, default true
Bars pixels : width of the bars in pixels, default 2
Bars color mode : bars color behavior
• BARS : gets the color from the 'Bars color' option on the settings panel
• HISTOGRAM : gets the color from the Bearish/Bullish/Neutral Histogram color options from the settings panel
Bars color : color for the central and vwap bars, default white
HISTOGRAM show the volume histogram, default true
Execution window (x24H) : last 24H periods where the volume funcionality will be plotted, default 5
Ticks per bar (max 50) : width in ticks of each histogram bar, default 2
Updates per period : number of times the histogram will update
• ONE : update at the last bar of the period
• TWO : update at the last bar of each half period
• FOUR : slice the period in 4 quarters and updates at the last bar of each of them
• EACH BAR : updates at the close of each bar
Pixels per bar : width in pixels of each histogram bar, default 4
Neutral Treshold (ticks) : delta in ticks between left and right vwaps to identify a waindrop as neutral, default 0
Bearish Histogram color : histogram color when right vwap is lower than left vwap, default red
Bullish Histogram color : histogram color when right vwap is higher than left vwap, default green
Neutral Histogram color : histogram color when the delta between right and left vwaps is equal or lower than the Neutral treshold, default blue
VOLUME LABELS : show volume labels
Volume labels color : color for the volume labels, default white
Volume Labels size : text size for the volume labels, choose between AUTO, TINY, SMALL, NORMAL or LARGE, default TINY
TRACK PRICE : show a yellow ball tracking the last price, default true
█ LIMITS
This indicator only works on intraday charts (minutes only) up to 12H (720m), the lower chart timeframe you can use is 1m
This indicator needs price, time and volume to work, it will not work on an index (there is no volume), the execution will not be allowed
The histogram (volume profile) can be plotted on 24H sessions as limit but you can plot several 24H sessions
█ ERRORS AND PERFORMANCE
Depending on the choosed settings, the script performance will be highly affected and it will experience errors
Two of the more common errors it can throw are:
• Calculation takes too long to execute
• Loop takes too long
The indicator performance is highly related to the underlying volatility (tick wise), the script takes each candlestick or bar and for each tick in it stores the price and volume, if the ticker in your chart has thousands and thousands of ticks per bar the indicator will throw an error for sure, it can not calculate in time such amount of ticks.
What all of that means? Simply put, this will throw error on the BITCOIN pair BTCUSD (high volatility with tick size 0.01) because it has too many ticks per bar, but lucky you it will work just fine on the futures contract BTC1! (tick size 5) because it has a lot less ticks per bar
There are some options you can fine tune to boost the script performance, the more demanding option in terms of resources consumption is Updates per period , by default is maxed out so lowering this setting will improve the performance in a high way.
If you wanna know more about how to improve the script performance, read the HELP INFO accessible from the settings panel
█ HOW-TO SETUP
The basic parameters to adjust are Period size , Ticks per bar and Pixels per bar
• Period size is the main setting, defines the waindrop size, to get a better looking histogram set bigger period and smaller chart timeframe
• Ticks per bar is the tricky one, adjust it differently for each underlying (ticker) volatility wise, for some you will need a low value, for others a high one.
To get a more accurate histogram set it as lower as you can (min value is 1)
• Pixels per bar allows you to adjust the width of each histogram bar, with it you can adjust the blank space between them or allow overlaping
You must play with these three parameters until you obtain the desired histogram: smoother, sharper, etc...
These are some of the different kind of charts you can setup thru the settings:
• Balanced Waindrops (default): charts with waindrops where the two halfs are of same size.
This is the default chart, just select a period (30m, 60m, 120m, 240m, pick your poison), adjust the histogram ticks and pixels and watch
• Unbalanced Waindrops: chart with waindrops where the two halfs are of different sizes.
Do you trade futures and want to plot a waindrop with the first half for the overnight session and the second half for the cash session? you got it;
just adjust the period to 1860 for any CME ticker (like ES1! for example) adjust the histogram ticks and pixels and watch
• Full Session Volume Profile: chart with waindrops where only the first half plots.
Do you use Volume profile to analize the market? Lucky you, now you can trick this one to plot it, just try a period of 780 on SPY, 2760 on ES1!, or 2880 on EURUSD
remember to adjust the histogram ticks and pixels for each underlying
• Only Bars: charts with only central and vwap bars plotted, simply deactivate the histogram and volume labels
• Only Histogram: charts with only the histogram plotted (volume profile charts), simply deactivate the bars and volume labels
• Only Volume: charts with only the raw volume numbers plotted, simply deactivate the bars and histogram
If you wanna know more about custom full session periods for different asset classes, read the HELP INFO accessible from the settings panel
EXAMPLES
Full Session Volume Profile on MES 5m chart:
Full Session Unbalanced Waindrop on MNQ 2m chart (left side Overnight session, right side Cash Session):
The following examples will have the exact same charts but on four different tickers representing a futures contract, a forex pair, an etf and a stock.
We are doing this to be able to see the different parameters we need for plotting the same kind of chart on different assets
The chart composition is as follows:
• Left side: Volume Labels chart (period 10)
• Upper Right side: Waindrops (period 60)
• Lower Right side: Full Session Volume Profile
The first example will specify the main parameters, the rest of the charts will have only the differences
MES :
• Left: Period size: 10, Bars: uncheck, Histogram: uncheck, Execution window: 1, Ticks per bar: 2, Updates per period: EACH BAR,
Pixels per bar: 4, Volume labels: check, Track price: check
• Upper Right: Period size: 60, Bars: check, Bars color mode: HISTOGRAM, Histogram: check, Execution window: 2, Ticks per bar: 2,
Updates per period: EACH BAR, Pixels per bar: 4, Volume labels: uncheck, Track price: check
• Lower Right: Period size: 2760, Bars: uncheck, Histogram: check, Execution window: 1, Ticks per bar: 1, Updates per period: EACH BAR,
Pixels per bar: 2, Volume labels: uncheck, Track price: check
EURUSD :
• Upper Right: Ticks per bar: 10
• Lower Right: Period size: 2880, Ticks per bar: 1, Pixels per bar: 1
SPY :
• Left: Ticks per bar: 3
• Upper Right: Ticks per bar: 5, Pixels per bar: 3
• Lower Right: Period size: 780, Ticks per bar: 2, Pixels per bar: 2
AAPL :
• Left: Ticks per bar: 2
• Upper Right: Ticks per bar: 6, Pixels per bar: 3
• Lower Right: Period size: 780, Ticks per bar: 1, Pixels per bar: 2
█ THANKS TO
PineCoders for all they do, all the tools and help they provide and their involvement in making a better community
scarf for the idea of coding a waindrops like indicator, I did not know something like that existed at all
All the Pine Coders, Pine Pros and Pine Wizards, people who share their work and knowledge for the sake of it and helping others, I'm very grateful indeed
I'm learning at each step of the way from you all, thanks for this awesome community;
Opensource and shared knowledge: this is the way! (said with canned voice from inside my helmet :D)
█ NOTE
This description was formatted following THIS guidelines
═════════════════════════════════════════════════════════════════════════
I sincerely hope you enjoy reading and using this work as much as I enjoyed developing it :D
GOOD LUCK AND HAPPY TRADING!
Delta Volume Columns Pro [LucF]█ OVERVIEW
This indicator displays volume delta information calculated with intrabar inspection on historical bars, and feed updates when running in realtime. It is designed to run in a pane and can display either stacked buy/sell volume columns or a signal line which can be calculated and displayed in many different ways.
Five different models are offered to reveal different characteristics of the calculated volume delta information. Many options are offered to visualize the calculations, giving you much leeway in morphing the indicator's visuals to suit your needs. If you value delta volume information, I hope you will find the time required to master Delta Volume Columns Pro well worth the investment. I am confident that if you combine a proper understanding of the indicator's information with an intimate knowledge of the volume idiosyncrasies on the markets you trade, you can extract useful market intelligence using this tool.
█ WARNINGS
1. The indicator only works on markets where volume information is available,
Please validate that your symbol's feed carries volume information before asking me why the indicator doesn't plot values.
2. When you refresh your chart or re-execute the script on the chart, the indicator will repaint because elapsed realtime bars will then recalculate as historical bars.
3. Because the indicator uses different modes of calculation on historical and realtime bars, it's critical that you understand the differences between them. Details are provided further down.
4. Calculations using intrabar inspection on historical bars can only be done from some chart timeframes. See further down for a list of supported timeframes.
If the chart's timeframe is not supported, no historical volume delta will display.
█ CONCEPTS
Chart bars
Three different types of bars are used in charts:
1. Historical bars are bars that have already closed when the script executes on them.
2. The realtime bar is the current, incomplete bar where a script is running on an open market. There is only one active realtime bar on your chart at any given time.
The realtime bar is where alerts trigger.
3. Elapsed realtime bars are bars that were calculated when they were realtime bars but have since closed.
When a script re-executes on a chart because the browser tab is refreshed or some of its inputs are changed, elapsed realtime bars are recalculated as historical bars.
Why does this indicator use two modes of calculation?
Historical bars on TradingView charts contain OHLCV data only, which is insufficient to calculate volume delta on them with any level of precision. To mine more detailed information from those bars we look at intrabars , i.e., bars from a smaller timeframe (we call it the intrabar timeframe ) that are contained in one chart bar. If your chart Is running at 1D on a 24x7 market for example, most 1D chart bars will contain 24 underlying 1H bars in their dilation. On historical bars, this indicator looks at those intrabars to amass volume delta information. If the intrabar is up, its volume goes in the Buy bin, and inversely for the Sell bin. When price does not move on an intrabar, the polarity of the last known movement is used to determine in which bin its volume goes.
In realtime, we have access to price and volume change for each update of the chart. Because a 1D chart bar can be updated tens of thousands of times during the day, volume delta calculations on those updates is much more precise. This precision, however, comes at a price:
— The script must be running on the chart for it to keep calculating in realtime.
— If you refresh your chart you will lose all accumulated realtime calculations on elapsed realtime bars, and the realtime bar.
Elapsed realtime bars will recalculate as historical bars, i.e., using intrabar inspection, and the realtime bar's calculations will reset.
When the script recalculates elapsed realtime bars as historical bars, the values on those bars will change, which means the script repaints in those conditions.
— When the indicator first calculates on a chart containing an incomplete realtime bar, it will count ALL the existing volume on the bar as Buy or Sell volume,
depending on the polarity of the bar at that point. This will skew calculations for that first bar. Scripts have no access to the history of a realtime bar's previous updates,
and intrabar inspection cannot be used on realtime bars, so this is the only to go about this.
— Even if alerts only trigger upon confirmation of their conditions after the realtime bar closes, they are repainting alerts
because they would perhaps not have calculated the same way using intrabar inspection.
— On markets like stocks that often have different EOD and intraday feeds and volume information,
the volume's scale may not be the same for the realtime bar if your chart is at 1D, for example,
and the indicator is using an intraday timeframe to calculate on historical bars.
— Any chart timeframe can be used in realtime mode, but plots that include moving averages in their calculations may require many elapsed realtime bars before they can calculate.
You might prefer drastically reducing the periods of the moving averages, or using the volume columns mode, which displays instant values, instead of the line.
Volume Delta Balances
This indicator uses a variety of methods to evaluate five volume delta balances and derive other values from those balances. The five balances are:
1 — On Bar Balance : This is the only balance using instant values; it is simply the subtraction of the Sell volume from the Buy volume on the bar.
2 — Average Balance : Calculates a distinct EMA for both the Buy and Sell volumes, and subtracts the Sell EMA from the Buy EMA.
3 — Momentum Balance : Starts by calculating, separately for both Buy and Sell volumes, the difference between the same EMAs used in "Average Balance" and
an SMA of double the period used for the "Average Balance" EMAs. The difference for the Sell side is subtracted from the difference for the Buy side,
and an RSI of that value is calculated and brought over the −50/+50 scale.
4 — Relative Balance : The reference values used in the calculation are the Buy and Sell EMAs used in the "Average Balance".
From those, we calculate two intermediate values using how much the instant Buy and Sell volumes on the bar exceed their respective EMA — but with a twist.
If the bar's Buy volume does not exceed the EMA of Buy volume, a zero value is used. The same goes for the Sell volume with the EMA of Sell volume.
Once we have our two intermediate values for the Buy and Sell volumes exceeding their respective MA, we subtract them. The final "Relative Balance" value is an ALMA of that subtraction.
The rationale behind using zero values when the bar's Buy/Sell volume does not exceed its EMA is to only take into account the more significant volume.
If both instant volume values exceed their MA, then the difference between the two is the signal's value.
The signal is called "relative" because the intermediate values are the difference between the instant Buy/Sell volumes and their respective MA.
This balance flatlines when the bar's Buy/Sell volumes do not exceed their EMAs, which makes it useful to spot areas where trader interest dwindles, such as consolidations.
The smaller the period of the final value's ALMA, the more easily you will see the balance flatline. These flat zones should be considered no-trade zones.
5 — Percent Balance : This balance is the ALMA of the ratio of the "On Bar Balance" value, i.e., the volume delta balance on the bar (which can be positive or negative),
over the total volume for that bar.
From the balances and marker conditions, two more values are calculated:
1 — Marker Bias : It sums the up/down (+1/‒1) occurrences of the markers 1 to 4 over a period you define, so it ranges from −4 to +4, times the period.
Its calculation will depend on the modes used to calculate markers 3 and 4.
2 — Combined Balances : This is the sum of the bull/bear (+1/−1) states of each of the five balances, so it ranges from −5 to +5.
█ FEATURES
The indicator has two main modes of operation: Columns and Line .
Columns
• In Columns mode you can display stacked Buy/Sell volume columns.
• The buy section always appears above the centerline, the sell section below.
• The top and bottom sections can be colored independently using eight different methods.
• The EMAs of the Buy/Sell values can be displayed (these are the same EMAs used to calculate the "Average Balance").
Line
• Displays one of seven signals: the five balances or one of two complementary values, i.e., the "Marker Bias" or the "Combined Balances".
• You can color the line and its fill using independent calculation modes to pack more information in the display.
You can thus appraise the state of 3 different values using the line itself, its color and the color of its fill.
• A "Divergence Levels" feature will use the line to automatically draw expanding levels on divergence events.
Default settings
Using the indicator's default settings, this is the information displayed:
• The line is calculated on the "Average Balance".
• The line's color is determined by the bull/bear state of the "Percent Balance".
• The line's fill gradient is determined by the advances/declines of the "Momentum Balance".
• The orange divergence dots are calculated using discrepancies between the polarity of the "On Bar Balance" and the chart's bar.
• The divergence levels are determined using the line's level when a divergence occurs.
• The background's fill gradient is calculated on advances/declines of the "Marker Bias".
• The chart bars are colored using advances/declines of the "Relative Balance". Divergences are shown in orange.
• The intrabar timeframe is automatically determined from the chart's timeframe so that a minimum of 50 intrabars are used to calculate volume delta on historical bars.
Alerts
The configuration of the marker conditions explained further is what determines the conditions that will trigger alerts created from this script. Note that simply selecting the display of markers does not create alerts. To create an alert on this script, you must use ALT-A from the chart. You can create multiple alerts triggering on different conditions from this same script; simply configure the markers so they define the trigger conditions for each alert before creating the alert. The configuration of the script's inputs is saved with the alert, so from then on you can change them without affecting the alert. Alert messages will mention the marker(s) that triggered the specific alert event. Keep in mind, when creating alerts on small chart timeframes, that discrepancies between alert triggers and markers displayed on your chart are to be expected. This is because the alert and your chart are running two distinct instances of the indicator on different servers and different feeds. Also keep in mind that while alerts only trigger on confirmed conditions, they are calculated using realtime calculation mode, which entails that if you refresh your chart and elapsed realtime bars recalculate as historical bars using intrabar inspection, markers will not appear in the same places they appeared in realtime. So it's important to understand that even though the alert conditions are confirmed when they trigger, these alerts will repaint.
Let's go through the sections of the script's inputs.
Columns
The size of the Buy/Sell columns always represents their respective importance on the bar, but the coloring mode for tops and bottoms is independent. The default setup uses a standard coloring mode where the Buy/Sell columns are always in the bull/bear color with a higher intensity for the winning side. Seven other coloring modes allow you to pack more information in the columns. When choosing to color the top columns using a bull/bear gradient on "Average Balance", for example, you will have bull/bear colored tops. In order for the color of the bottom columns to continue to show the instant bar balance, you can then choose the "On Bar Balance — Dual Solid Colors" coloring mode to make those bars the color of the winning side for that bar. You can display the averages of the Buy and Sell columns. If you do, its coloring is controlled through the "Line" and "Line fill" sections below.
Line and Line fill
You can select the calculation mode and the thickness of the line, and independent calculations to determine the line's color and fill.
Zero Line
The zero line can display dots when all five balances are bull/bear.
Divergences
You first select the detection mode. Divergences occur whenever the up/down direction of the signal does not match the up/down polarity of the bar. Divergences are used in three components of the indicator's visuals: the orange dot, colored chart bars, and to calculate the divergence levels on the line. The divergence levels are dynamic levels that automatically build from the line's values on divergence events. On consecutive divergences, the levels will expand, creating a channel. This implementation of the divergence levels corresponds to my view that divergences indicate anomalies, hesitations, points of uncertainty if you will. It precludes any attempt to identify a directional bias to divergences. Accordingly, the levels merely take note of divergence events and mark those points in time with levels. Traders then have a reference point from which they can evaluate further movement. The bull/bear/neutral colors used to plot the levels are also congruent with this view in that they are determined by the line's position relative to the levels, which is how I think divergences can be put to the most effective use. One of the coloring modes for the line's fill uses advances/declines in the line after divergence events.
Background
The background can show a bull/bear gradient on six different calculations. As with other gradients, you can adjust its brightness to make its importance proportional to how you use it in your analysis.
Chart bars
Chart bars can be colored using seven different methods. You have the option of emptying the body of bars where volume does not increase, as does my TLD indicator, and you can choose whether you want to show divergences.
Intrabar Timeframe
This is the intrabar timeframe that will be used to calculate volume delta using intrabar inspection on historical bars. You can choose between four modes. The three "Auto-steps" modes calculate, from the chart's timeframe, the intrabar timeframe where the said number of intrabars will make up the dilation of chart bars. Adjustments are made for non-24x7 markets. "Fixed" mode allows you to select the intrabar timeframe you want. Checking the "Show TF" box will display in the lower-right corner the intrabar timeframe used at any given moment. The proper selection of the intrabar timeframe is important. It must achieve maximal granularity to produce precise results while not unduly slowing down calculations, or worse, causing runtime errors. Note that historical depth will vary with the intrabar timeframe. The smaller the timeframe, the shallower historical plots you will be.
Markers
Markers appear when the required condition has been confirmed on a closed bar. The configuration of the markers when you create an alert is what determines when the alert will trigger. Five markers are available:
• Balances Agreement : All five balances are either bullish or bearish.
• Double Bumps : A double bump is two consecutive up/down bars with +/‒ volume delta, and rising Buy/Sell volume above its average.
• Divergence confirmations : A divergence is confirmed up/down when the chosen balance is up/down on the previous bar when that bar was down/up, and this bar is up/down.
• Balance Shifts : These are bull/bear transitions of the selected signal.
• Marker Bias Shifts : Marker bias shifts occur when it crosses into bull/bear territory.
Periods
Allows control over the periods of the different moving averages used to calculate the balances.
Volume Discrepancies
Stock exchanges do not report the same volume for intraday and daily (or higher) resolutions. Other variations in how volume information is reported can also occur in other markets, namely Forex, where volume irregularities can even occur between different intraday timeframes. This will cause discrepancies between the total volume on the bar at the chart's timeframe, and the total volume calculated by adding the volume of the intrabars in that bar's dilation. This does not necessarily invalidate the volume delta information calculated from intrabars, but it tells us that we are using partial volume data. A mechanism to detect chart vs intrabar timeframe volume discrepancies is provided. It allows you to define a threshold percentage above which the background will indicate a difference has been detected.
Other Settings
You can control here the display of the gray dot reminder on realtime bars, and the display of error messages if you are using a chart timeframe that is not greater than the fixed intrabar timeframe, when you use that mode. Disabling the message can be useful if you only use realtime mode at chart timeframes that do not support intrabar inspection.
█ RAMBLINGS
On Volume Delta
Volume is arguably the best complement to interpret price action, and I consider volume delta to be the most effective way of processing volume information. In periods of low-volatility price consolidations, volume will typically also be lower than normal, but slight imbalances in the trend of the buy/sell volume balance can sometimes help put early odds on the direction of the break from consolidation. Additionally, the progression of the volume imbalance can help determine the proximity of the breakout. I also find volume delta and the number of divergences very useful to evaluate the strength of trends. In trends, I am looking for "slow and steady", i.e., relatively low volatility and pauses where price action doesn't look like world affairs are being reassessed. In my personal mythology, this type of trend is often more resilient than high-volatility breakouts, especially when volume balance confirms the general agreement of traders signaled by the low-volatility usually accompanying this type of trend. The volume action on pauses will often help me decide between aggressively taking profits, tightening a stop or going for a longer-term movement. As for reversals, they generally occur in high-volatility areas where entering trades is more expensive and riskier. While the identification of counter-trend reversals fascinates many traders to no end, they represent poor opportunities in my view. Volume imbalances often precede reversals, but I prefer to use volume delta information to identify the areas following reversals where I can confirm them and make relatively low-cost entries with better odds.
On "Buy/Sell" Volume
Buying or selling volume are misnomers, as every unit of volume transacted is both bought and sold by two different traders. While this does not keep me from using the terms, there is no such thing as “buy only” or “sell only” volume. Trader lingo is riddled with peculiarities.
Divergences
The divergence detection method used here relies on a difference between the direction of a signal and the polarity (up/down) of a chart bar. When using the default "On Bar Balance" to detect divergences, however, only the bar's volume delta is used. You may wonder how there can be divergences between buying/selling volume information and price movement on one bar. This will sometimes be due to the calculation's shortcomings, but divergences may also occur in instances where because of order book structure, it takes less volume to increase the price of an asset than it takes to decrease it. As usual, divergences are points of interest because they reveal imbalances, which may or may not become turning points. To your pattern-hungry brain, the divergences displayed by this indicator will — as they do on other indicators — appear to often indicate turnarounds. My opinion is that reality is generally quite sobering and I have no reliable information that would tend to prove otherwise. Exercise caution when using them. Consequently, I do not share the overwhelming enthusiasm of traders in identifying bullish/bearish divergences. For me, the best course of action when a divergence occurs is to wait and see what happens from there. That is the rationale underlying how my divergence levels work; they take note of a signal's level when a divergence occurs, and it's the signal's behavior from that point on that determines if the post-divergence action is bullish/bearish.
Superfluity
In "The Bed of Procrustes", Nassim Nicholas Taleb writes: To bankrupt a fool, give him information . This indicator can display lots of information. While learning to use a new indicator inevitably requires an adaptation period where we put it through its paces and try out all its options, once you have become used to it and decide to adopt it, rigorously eliminate the components you don't use and configure the remaining ones so their visual prominence reflects their relative importance in your analysis. I tried to provide flexible options for traders to control this indicator's visuals for that exact reason — not for window dressing.
█ LIMITATIONS
• This script uses a special characteristic of the `security()` function allowing the inspection of intrabars — which is not officially supported by TradingView.
It has the advantage of permitting a more robust calculation of volume delta than other methods on historical bars, but also has its limits.
• Intrabar inspection only works on some chart timeframes: 3, 5, 10, 15 and 30 minutes, 1, 2, 3, 4, 6, and 12 hours, 1 day, 1 week and 1 month.
The script’s code can be modified to run on other resolutions.
• When the difference between the chart’s timeframe and the intrabar timeframe is too great, runtime errors will occur. The Auto-Steps selection mechanisms should avoid this.
• All volume is not created equally. Its source, components, quality and reliability will vary considerably with sectors and instruments.
The higher the quality, the more reliably volume delta information can be used to guide your decisions.
You should make it your responsibility to understand the volume information provided in the data feeds you use. It will help you make the most of volume delta.
█ NOTES
For traders
• The Data Window shows key values for the indicator.
• While this indicator displays some of the same information calculated in my Delta Volume Columns ,
I have elected to make it a separate publication so that traders continue to have a simpler alternative available to them. Both code bases will continue to evolve separately.
• All gradients used in this indicator determine their brightness intensities using advances/declines in the signal—not their relative position in a pre-determined scale.
• Volume delta being relative, by nature, it is particularly well-suited to Forex markets, as it filters out quite elegantly the cyclical volume data characterizing the sector.
If you are interested in volume delta, consider having a look at my other "Delta Volume" indicators:
• Delta Volume Realtime Action displays realtime volume delta and tick information on the chart.
• Delta Volume Candles builds volume delta candles on the chart.
• Delta Volume Columns is a simpler version of this indicator.
For coders
• I use the `f_c_gradientRelativePro()` from the PineCoders Color Gradient Framework to build my gradients.
This function has the advantage of allowing begin/end colors for both the bull and bear colors. It also allows us to define the number of steps allowed for each gradient.
I use this to modulate the gradients so they perform optimally on the combination of the signal used to calculate advances/declines,
but also the nature of the visual component the gradient applies to. I use fewer steps for choppy signals and when the gradient is used on discrete visual components
such as volume columns or chart bars.
• I use the PineCoders Coding Conventions for Pine to write my scripts.
• I used functions modified from the PineCoders MTF Selection Framework for the selection of timeframes.
█ THANKS TO:
— The devs from TradingView's Pine and other teams, and the PineCoders who collaborate with them. They are doing amazing work,
and much of what this indicator does could not be done without their recent improvements to Pine.
— A guy called Kuan who commented on a Backtest Rookies presentation of their Volume Profile indicator using a `for` loop.
This indicator started from the intrabar inspection technique illustrated in Kuan's snippet.
— theheirophant , my partner in the exploration of the sometimes weird abysses of `security()`’s behavior at intrabar timeframes.
— midtownsk8rguy , my brilliant companion in mining the depths of Pine graphics.