//+------------------------------------------------------------------+ //| GHOST OBV POST.mq5 | //| Copyright © 2022, Casper and Sharky | //| https://www.mql5.com/ | //+------------------------------------------------------------------+ #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 3 //--- plot OBV #property indicator_label1 "OBV" #property indicator_type1 DRAW_LINE #property indicator_color1 clrAqua #property indicator_style1 STYLE_SOLID #property indicator_width1 2 //--- plot Bull Fractal #property indicator_label2 "Bull Fractal" #property indicator_type2 DRAW_ARROW #property indicator_color2 clrGreen #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //--- plot Bear Fractal #property indicator_label3 "Bear Fractal" #property indicator_type3 DRAW_ARROW #property indicator_color3 clrRed #property indicator_style3 STYLE_SOLID #property indicator_width3 1 //--- input parameters input group "OBV" input ENUM_APPLIED_VOLUME Inp_OBV_applied_volume = VOLUME_TICK; // OBV: volume type for calculation //--- indicator buffers double obvBuffer[]; double bearFractalBuffer[]; double bullFractalBuffer[]; //--- handles and global variables bool initError = false; // error on In int obvHandle = INVALID_HANDLE; // variable for storing the handle of the iOBV indicator int barsCalculated = 0; // we will keep the number of values in the On Balance Volume indicator int bullDivCounter = 0; int bearDivCounter = 0; double validBullFractals[]; double validBearFractals[]; double validBullFractalTimes[]; double validBearFractalTimes[]; int bullFractalCounter = 0; int bearFractalCounter = 0; int prevValidBullFractalCounter = 0; int prevValidBearFractalCounter = 0; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0, obvBuffer, INDICATOR_DATA); SetIndexBuffer(1, bullFractalBuffer, INDICATOR_DATA); SetIndexBuffer(2, bearFractalBuffer, INDICATOR_DATA); //--- indicator digits and set shortname/data window IndicatorSetInteger(INDICATOR_DIGITS, 2); IndicatorSetString(INDICATOR_SHORTNAME, "OBV "); //--- setting a code from the Wingdings charset as the property of PLOT_ARROW PlotIndexSetInteger(1, PLOT_ARROW, 218); // wingding 218 is a bullish fractal arrow PlotIndexSetInteger(2, PLOT_ARROW, 217); // wingding 217 is a bearish fractal arrow //--- arrow shifts when drawing PlotIndexSetInteger(1, PLOT_ARROW_SHIFT, 10); PlotIndexSetInteger(2, PLOT_ARROW_SHIFT, -10); //--- sets drawing line empty value PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); //--- create handle of the iOBV indicator obvHandle = iOBV(Symbol(), Period(), Inp_OBV_applied_volume); //--- if the handle is not created if(obvHandle == INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iOBV indicator for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early initError = true; return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(initError) return(0); // Returns 0 to prev_calculated for the next run. if(rates_total < 5) return(0); //--- number of values copied from the iOBV indicator int valuesToCopy; //--- determine the number of values calculated in the indicator int calculated = BarsCalculated(obvHandle); if(calculated <= 0) { PrintFormat("BarsCalculated() returned %d, error code %d", calculated, GetLastError()); return(0); } //--- if it is the first start of calculation of the indicator or if the number of values in the iOBV indicator changed //---or if it is necessary to calculated the indicator for two or more bars (it means something has changed in the price history) if(prev_calculated == 0 || calculated != barsCalculated || rates_total > prev_calculated+1) { //--- if the iobvBuffer array is greater than the number of values in the iOBV indicator for symbol/period, then we don't copy everything //--- otherwise, we copy less than the size of indicator buffers if(calculated > rates_total) valuesToCopy = rates_total; else valuesToCopy = calculated; } else { //--- it means that it's not the first time of the indicator calculation, and since the last call of OnCalculate() //--- for calculation not more than one bar is added valuesToCopy = (rates_total - prev_calculated)+1; } //--- fill the arrays with values of the iOBV indicator //--- if FillArrayFromBuffer returns false, it means the information is nor ready yet, quit operation if(!FillArrayFromBuffer(obvBuffer, obvHandle, valuesToCopy)) return(0); //--- memorize the number of values in the On Balance Volume indicator barsCalculated = calculated; //--- Fractals int start; //--- clean up arrays if(prev_calculated < 7) { start = 2; ArrayInitialize(bearFractalBuffer, EMPTY_VALUE); ArrayInitialize(bullFractalBuffer, EMPTY_VALUE); } else start = rates_total-5; //--- main cycle of calculations for(int i = start; i < rates_total-3 && !IsStopped(); i++) { //--- Bull Fractal if(obvBuffer[i] < obvBuffer[i+1] && obvBuffer[i] < obvBuffer[i+2] && obvBuffer[i] <= obvBuffer[i-1] && obvBuffer[i] <= obvBuffer[i-2]) { ArrayResize(validBullFractals, bullFractalCounter+1); ArrayResize(validBullFractalTimes, bullFractalCounter+1); bullFractalBuffer[i] = obvBuffer[i]; //PrintFormat("bullFractalCounter: %d", bullFractalCounter); validBullFractals[bullFractalCounter] = bullFractalBuffer[i]; validBullFractalTimes[bullFractalCounter] = time[i]; bullFractalCounter++; } else bullFractalBuffer[i] = EMPTY_VALUE; //--- Bear Fractal if(obvBuffer[i] > obvBuffer[i+1] && obvBuffer[i] > obvBuffer[i+2] && obvBuffer[i] >= obvBuffer[i-1] && obvBuffer[i] >= obvBuffer[i-2]) { ArrayResize(validBearFractals, bearFractalCounter+1); ArrayResize(validBearFractalTimes, bearFractalCounter+1); bearFractalBuffer[i] = obvBuffer[i]; validBearFractals[bearFractalCounter] = bearFractalBuffer[i]; validBearFractalTimes[bearFractalCounter] = time[i]; bearFractalCounter++; } else bearFractalBuffer[i] = EMPTY_VALUE; } //--- Bull Fractal/Div if(bullDivCounter == 0) PrintFormat("Bull Fractal Counter: %d", ArraySize(validBullFractals)); string bullDivPrefix = "Bull_Div_Trend_Line_"; for(int i = 1; i < ArraySize(validBullFractals) - prevValidBullFractalCounter; i++) { if(validBullFractals[i] > validBullFractals[i-1]) { string name = bullDivPrefix + bullDivCounter; ObjectCreate(0, name, OBJ_TREND, 1, validBullFractalTimes[i-1], validBullFractals[i-1], validBullFractalTimes[i], validBullFractals[i]); ObjectSetInteger(0, name, OBJPROP_COLOR, clrGreen); bullDivCounter++; } } prevValidBullFractalCounter = ArraySize(validBullFractals); if(bearDivCounter == 0) PrintFormat("Bull Fractal Counter: %d", ArraySize(validBearFractals)); //--- Bear Fractal/Div string bearDivPrefix = "Bear_Div_Trend_Line_"; for(int i = 1; i < ArraySize(validBearFractals) - prevValidBearFractalCounter; i++) { if(validBearFractals[i] < validBearFractals[i-1]) { string name = bearDivPrefix + bearDivCounter; ObjectCreate(0, name, OBJ_TREND, 1, validBearFractalTimes[i-1], validBearFractals[i-1], validBearFractalTimes[i], validBearFractals[i]); ObjectSetInteger(0, name, OBJPROP_COLOR, clrRed); bearDivCounter++; } } prevValidBearFractalCounter = ArraySize(validBearFractals); //--- for(int i = rates_total-3; i < rates_total; i++) { bullFractalBuffer[i] = EMPTY_VALUE; bearFractalBuffer[i] = EMPTY_VALUE; } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| Filling indicator buffers from the iOBV indicator | //+------------------------------------------------------------------+ bool FillArrayFromBuffer(double &buffer[], // indicator buffer of OBV values int handle, // handle of the iOBV indicator int amount // number of copied values ) { //--- reset error code ResetLastError(); //--- fill a part of the iobvBuffer array with values from the indicator buffer that has 0 index if(CopyBuffer(handle, 0, 0, amount, buffer) < 0) { //--- if the copying fails, tell the error code PrintFormat("Failed to copy data from the iOBV indicator, error code %d", GetLastError()); //--- quit with zero result - it means that the indicator is considered as not calculated return(false); } //--- everything is fine return(true); } //+------------------------------------------------------------------+ //| Indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(obvHandle != INVALID_HANDLE) IndicatorRelease(obvHandle); } //+------------------------------------------------------------------+