snowseed-iClv
Download scriptThis tool is designed as a visual aid for traders to analyze price action dynamics by measuring the relationship between closing prices and the high/low extremes of a specific timeframe. Instead of just looking at raw price, it translates market movement into a smoothed numerical value displayed in a separate window below your main chart.
Core Logic and Calculation
The indicator functions by evaluating the "position" of the closing price relative to the candle's range over a user-defined period. It specifically looks at how much the price moved upward from the bottom versus how much it retreated from the top before the period ended.
- Price Positioning: For every bar on the chart, the script identifies the highest and lowest points reached within a specific window (determined by the ClvPeriod setting).
- The Balance Calculation: It compares the distance between the "Low" and the "Close" against the distance between the "High" and the "Close." This helps determine if the price ended the period with bullish momentum (closer to the highs) or bearish momentum (closer to the lows).
- Normalization: The resulting value is normalized into a standardized scale. This allows the indicator to provide a consistent visual representation regardless of how volatile or large the actual price movements are.
Smoothing and Visualization
To make the data easier for a human to read, the script applies two distinct layers of processing:
- Trend Filtering: Because raw price action can be "noisy" (jagged and erratic), the indicator uses a Moving Average logic. This takes the raw calculation and smooths it out, helping to filter out minor fluctuations and highlight the primary trend of the price positioning.
- Dynamic Updating: The script is designed to constantly recalculate as new market data arrives. It ensures that the most recent information is reflected on the chart while maintaining a buffer of historical data so the lines remain continuous and unbroken.
How to Interpret the Results
Because the indicator maps these values into a specific range, it serves as a "momentum map." When the line moves higher in the separate window, it indicates that the price is consistently closing near the top of its recent ranges (bullish sentiment). When the line moves lower, it suggests that the price is struggling to stay above the lows and is closing closer to the bottom of its range (bearish sentiment).
By adjusting the two main parameters—the period of observation and the length of the moving average—you can choose whether you want to see short-term "bursts" of momentum or long-term, structural shifts in market sentiment.
#property copyright "Copyright S.Projects"
#property link "http://trader.snowseed.com"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Red
#property indicator_minimum 0
#property indicator_maximum 1
// indicator parameters
extern int nClvPeriod = 27;
extern int nClvMa = 7;
// At the very beginning of the chart, before the MA
// had enough data to draw itself properly, do not display
int nDrawBeginBar;
// indicator buffers
double arrExtMapBuffer[];
// We are using this variable to store the number of bars
// for which the chart is already drawn. The difference
// between the total number of bars and this number is
// the number of bars to draw (usually, 1)
int nExtCountedBars = 0;
////////////////////////
int init()
{
string strIndicatorShortName;
strIndicatorShortName = "CLV(" + nClvPeriod +
", " + nClvMa + ")";
IndicatorShortName(strIndicatorShortName);
// drawing settings
SetIndexStyle(0, DRAW_LINE);
SetIndexShift(0, 0);
IndicatorDigits(4);
// Skip drawing at the beginning of a chart
nDrawBeginBar = MathMax(nClvPeriod, nClvMa);
SetIndexDrawBegin(0, nDrawBeginBar);
// indicator buffers mapping
SetIndexBuffer(0, arrExtMapBuffer);
return(0);
}
///////////////////////////
int start()
{
// Not enough points on a chart
if(Bars <= nDrawBeginBar)
return(0);
// See if we have new data to plot (if a new tick came,
// and it is the first tick of a new bar
nExtCountedBars = IndicatorCounted();
if(nExtCountedBars < 0)
return(-1);
// Last counted bar will be recounted
// Note, that here we force the last bar to be recounted.
// You may comment this code, if you only want to do it
// at the beginning of a new bar. I only keep this code
// for compatibility with a little mess created by official
// tutorials: see comment below (*)
if(nExtCountedBars > 0)
nExtCountedBars--;
Clv();
return(0);
}
///////////////////
// For the selected period:
// CLV = ((Close - Low) - (High - Close)) / (High - Low)
void Clv()
{
// (*) Note the line below. If we use
// "int nPos = Bars - nExtCountedBars;" the last bar
// will be recounted every tick. If there is no "- 1",
// only the beginning of a new bar triggers the code.
int nPos = Bars - nExtCountedBars;
// Note, that nPos is always > 0. It means, that Close[nPos]
// is never Close[0], so we don't make a mistake by "looking
// into the future". Note also, that we assign the value
// to the arrExtMapBuffer[nPos]. It means, that when calling
// this indicator from the expert, we need to use shift
// >= 1. If we want to be able to call it with shift == 0,
// we need to rewrite the code below, using
// arrExtMapBuffer[nPos - 1]. The Close[...] and all other
// functions obtaining data, should still use [nPos].
while(nPos > 0 && nPos < ArraySize(Low))
{
double dClose = Close[nPos];
double dLow = Low[ArrayMinimum(Low, nClvPeriod, nPos)];
double dHigh = High[ArrayMaximum(High, nClvPeriod, nPos)];
arrExtMapBuffer[nPos] = 0;
if(dHigh - dLow != 0)
arrExtMapBuffer[nPos] =
(((dClose - dLow) - (dHigh - dClose))
/ (dHigh - dLow)) / 2 + 0.5;
else
arrExtMapBuffer[nPos] = 0;
nPos--;
}
if(nClvMa > 1)
Ema(arrExtMapBuffer);
}
///////////////////
void Ema(double& arr[])
{
double dPr = 2.0 / (nClvMa + 1);
int nPos = Bars - nExtCountedBars;
while(nPos > 0)
{
if(nPos == Bars - 2)
arrExtMapBuffer[nPos + 1] = arr[nPos + 1];
arrExtMapBuffer[nPos] = arr[nPos] * dPr +
arrExtMapBuffer[nPos + 1] * (1 - dPr);
nPos--;
}
}
///////////////////
Comments