Please help converting this “Quantile based signed volume analysis 1.1” indicator from mq5.
The original code include several prices to be used as input but it is really not necessary and just the close would do it.
//------------------------------------------------------------------
//
//------------------------------------------------------------------
#property copyright "www.forex-tsd.com"
#property link "www.forex-tsd.com"
#property indicator_separate_window
#property indicator_buffers 9
#property indicator_plots 4
#property indicator_label1 "Quantile volume"
#property indicator_type1 DRAW_FILLING
#property indicator_color1 clrDodgerBlue,clrSandyBrown
#property indicator_label2 "Quantile volume"
#property indicator_type2 DRAW_FILLING
#property indicator_color2 clrDodgerBlue,clrSandyBrown
#property indicator_label3 "Quantile volume"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrGray
#property indicator_style3 STYLE_SOLID
#property indicator_width3 2
#property indicator_label4 "Quantile volume"
#property indicator_type4 DRAW_LINE
#property indicator_color4 clrGray
#property indicator_style4 STYLE_SOLID
#property indicator_width4 2
//
//
//
//
//
enum enPrices
{
pr_close, // Close
pr_open, // Open
pr_high, // High
pr_low, // Low
pr_median, // Median
pr_typical, // Typical
pr_weighted, // Weighted
pr_average, // Average (high+low+open+close)/4
pr_medianb, // Average median body (open+close)/2
pr_tbiased, // Trend biased price
pr_tbiased2, // Trend biased price - extremes
pr_haclose, // Heiken ashi close
pr_haopen , // Heiken ashi open
pr_hahigh, // Heiken ashi high
pr_halow, // Heiken ashi low
pr_hamedian, // Heiken ashi median
pr_hatypical, // Heiken ashi typical
pr_haweighted, // Heiken ashi weighted
pr_haaverage, // Heiken ashi average
pr_hamedianb, // Heiken ashi median body
pr_hatbiased, // Heiken ashi trend biased price
pr_hatbiased2 // Heiken ashi trend biased price - extremes
};
input int Periods = 20; // Calculation period
input enPrices Price = pr_close; // Price
input double UpQuantilePercent = 90; // Upper quantile percent
input double DownQuantilePercent = 10; // Lower quantile percent
input double AtrUpperPercent = 75; // Upper atr percent
input double AtrLowerPercent = 25; // Lower atr percent
input bool LimitToZeroes = true; // Limit display to zero level?
input bool UseRealVolume = false; // Use real volume?
//
//
//
//
//
double fillUp[];
double fillUpz[];
double fillDn[];
double fillDnz[];
double bufferUp[];
double bufferDn[];
double quantUp[];
double quantDn[];
double prices[];
//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//
int OnInit()
{
SetIndexBuffer(0,fillUp ,INDICATOR_DATA); SetIndexBuffer(1,fillUpz,INDICATOR_DATA);
SetIndexBuffer(2,fillDn ,INDICATOR_DATA); SetIndexBuffer(3,fillDnz,INDICATOR_DATA);
SetIndexBuffer(4,bufferUp,INDICATOR_DATA);
SetIndexBuffer(5,bufferDn,INDICATOR_DATA);
SetIndexBuffer(6,quantUp ,INDICATOR_CALCULATIONS);
SetIndexBuffer(7,quantDn ,INDICATOR_CALCULATIONS);
SetIndexBuffer(8,prices ,INDICATOR_CALCULATIONS);
//
//
//
//
//
IndicatorSetString(INDICATOR_SHORTNAME,"Quantile based signed volume analysis 1 ("+(string)Periods+","+(string)UpQuantilePercent+","+(string)DownQuantilePercent+")");
return(0);
}
void OnDeinit(const int reason) { return; }
//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//
int OnCalculate (const int rates_total, // size of input time series
const int prev_calculated, // bars handled in previous call
const datetime& time[], // Time
const double& open[], // Open
const double& high[], // High
const double& low[], // Low
const double& close[], // Close
const long& tick_volume[], // Tick Volume
const long& volume[], // Real Volume
const int& spread[] // Spread
)
{
for (int i=(int)MathMax(prev_calculated-1,0); i<rates_total; i++)
{
prices[i] = getPrice(Price,open,close,high,low,rates_total,i);
quantUp[i] = iQuantile(Periods,UpQuantilePercent ,i);
quantDn[i] = iQuantile(Periods,DownQuantilePercent,i);
//
//
//
//
//
bufferUp[i] = 0;
bufferDn[i] = 0;
for (int k=0; k<Periods && i-k-1>=0; k++)
{
double atr = MathMax(close[i-k-1],high[i-k])-MathMin(close[i-k-1],low[i-k]);
double sign = 0;
if (prices[i-k] > low[i-k]+atr*AtrUpperPercent/100.0 && prices[i-k]>prices[i-k-1]) sign = 1;
if (prices[i-k] < low[i-k]+atr*AtrLowerPercent/100.0 && prices[i-k]<prices[i-k-1]) sign = -1;
if (!UseRealVolume)
{
if (prices[i-k] > quantUp[i-k]) bufferUp[i]+= sign*tick_volume[i-k]*prices[i-k]*atr;
if (prices[i-k] < quantDn[i-k]) bufferDn[i]+= sign*tick_volume[i-k]*prices[i-k]*atr;
}
else
{
if (prices[i-k] > quantUp[i-k]) bufferUp[i]+= sign*volume[i-k]*prices[i-k]*atr;
if (prices[i-k] < quantDn[i-k]) bufferDn[i]+= sign*volume[i-k]*prices[i-k]*atr;
}
}
if (LimitToZeroes)
{
bufferUp[i] = MathMax(bufferUp[i],0);
bufferDn[i] = MathMin(bufferDn[i],0);
}
fillUp[i] = bufferUp[i]; fillUpz[i] = 0;
fillDn[i] = bufferDn[i]; fillDnz[i] = 0;
}
return(rates_total);
}
//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//
double quantileArray[];
double iQuantile(int period, double qp, int i)
{
if (ArraySize(quantileArray)!=period) ArrayResize(quantileArray,period);
for(int k=0; k<period && (i-k)>=0; k++) quantileArray[k] = prices[i-k];
ArraySort(quantileArray);
//
//
//
//
//
double index = (period-1)*qp/100.00;
int ind = (int)index;
double delta = index - ind;
if (ind == NormalizeDouble(index,5))
return( quantileArray[ind]);
else return((1.0-delta)*quantileArray[ind]+delta*quantileArray[ind+1]);
}
//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//
//
double workHa[][4];
double getPrice(int price, const double& open[], const double& close[], const double& high[], const double& low[], int bars, int i, int instanceNo=0)
{
if (price>=pr_haclose)
{
if (ArrayRange(workHa,0)!= bars) ArrayResize(workHa,bars);
//
//
//
//
//
double haOpen;
if (i>0)
haOpen = (workHa[i-1][instanceNo+2] + workHa[i-1][instanceNo+3])/2.0;
else haOpen = (open[i]+close[i])/2;
double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0;
double haHigh = MathMax(high[i], MathMax(haOpen,haClose));
double haLow = MathMin(low[i] , MathMin(haOpen,haClose));
if(haOpen <haClose) { workHa[i][instanceNo+0] = haLow; workHa[i][instanceNo+1] = haHigh; }
else { workHa[i][instanceNo+0] = haHigh; workHa[i][instanceNo+1] = haLow; }
workHa[i][instanceNo+2] = haOpen;
workHa[i][instanceNo+3] = haClose;
//
//
//
//
//
switch (price)
{
case pr_haclose: return(haClose);
case pr_haopen: return(haOpen);
case pr_hahigh: return(haHigh);
case pr_halow: return(haLow);
case pr_hamedian: return((haHigh+haLow)/2.0);
case pr_hamedianb: return((haOpen+haClose)/2.0);
case pr_hatypical: return((haHigh+haLow+haClose)/3.0);
case pr_haweighted: return((haHigh+haLow+haClose+haClose)/4.0);
case pr_haaverage: return((haHigh+haLow+haClose+haOpen)/4.0);
case pr_hatbiased:
if (haClose>haOpen)
return((haHigh+haClose)/2.0);
else return((haLow+haClose)/2.0);
case pr_hatbiased2:
if (haClose>haOpen) return(haHigh);
if (haClose<haOpen) return(haLow);
return(haClose);
}
}
//
//
//
//
//
switch (price)
{
case pr_close: return(close[i]);
case pr_open: return(open[i]);
case pr_high: return(high[i]);
case pr_low: return(low[i]);
case pr_median: return((high[i]+low[i])/2.0);
case pr_medianb: return((open[i]+close[i])/2.0);
case pr_typical: return((high[i]+low[i]+close[i])/3.0);
case pr_weighted: return((high[i]+low[i]+close[i]+close[i])/4.0);
case pr_average: return((high[i]+low[i]+close[i]+open[i])/4.0);
case pr_tbiased:
if (close[i]>open[i])
return((high[i]+close[i])/2.0);
else return((low[i]+close[i])/2.0);
case pr_tbiased2:
if (close[i]>open[i]) return(high[i]);
if (close[i]<open[i]) return(low[i]);
return(close[i]);
}
return(0);
}
Many Thanks!