Attachments forums

List of attachments posted on this forum.


All files on forums: 159918

Re: Fix: Indicator is not showing on MT4 chart

RACER-X, Fri Dec 27, 2024 9:41 pm

Hi all. I was trying to convert a PineScript indicator from Tradingview over to MQL4 for MT4.
I used ChatGPT Pro to do it. And the finished code compiled with no errors.
But the indicator shows up blank on the chart.

Can you help me fix it?

Here is the mql4 code:

Code: Select all

#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots   3

//--- Plots
#property indicator_label1  "Volatility RSI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrOrange
#property indicator_width1  2

#property indicator_label2  "Line 30"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrange
#property indicator_width2  1
#property indicator_style2  STYLE_DOT

#property indicator_label3  "Line 4"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrSaddleBrown
#property indicator_width3  1
#property indicator_style3  STYLE_DOT

//--- Inputs
extern int VolatilityLength = 8;

//--- Buffers
double VolatilityRSIBuffer[]; 
double HorizontalLine30[]; 
double HorizontalLine4[];  

// -------------- Utilities & Math --------------
double Ln(double x)
{
   // MQL4 does not have a built-in natural log named ln(), 
   // but it does have MathLog() which is ln(x).
   return(MathLog(x));
}

// RMA (Wilder’s Smoothing) helper
// Equivalent to TradingView's ta.rma() if you do it in a rolling fashion.
// But we must do a "manual" approach using a loop.
double WilderRMA(double currentValue, double prevRma, int length)
{
   // RMA = (prevRma*(length-1) + currentValue) / length
   return((prevRma*(length-1) + currentValue) / length);
}

// -------------- Pine Script Functions Rewritten --------------

// _pv(_src, _len) = sqrt((_src / ((_len * 4) * ln(2))) * sum( (ln(high/low))^2, _len ))
double PVCalculation(int pos, int len)
{
   // pos is the current bar index from left to right in MQL4
   // _src = hlc3 = (High+Low+Close)/3 for the *current* bar
   // we must sum over the last "len" bars for (ln(high/low))^2
   // Here we do a standard loop for i in [pos..pos+len-1], carefully checking bounds.

   if(pos + len >= Bars) return(0); // not enough bars to the left

   double src      = (High[pos] + Low[pos] + Close[pos]) / 3.0; 
   double sumLnSq  = 0.0;
   for(int i=0; i<len; i++)
   {
      int barIndex = pos + i; 
      if(barIndex >= Bars) break; 
      double h = High[barIndex];
      double l = Low[barIndex];
      if(l <= 0 || h <= 0) continue; 
      double val = Ln(h/l);
      sumLnSq += val*val;
   }

   double denominator = (len * 4.0) * Ln(2.0); // (_len*4)*ln(2)
   if(denominator == 0) return(0);

   double result = 0.0;
   // result = sqrt( (src / denominator) * sumLnSq )
   result = MathSqrt((src / denominator) * sumLnSq);

   return(result);
}

// _rsi(_src, _len) 
// u = max(_src - _src[1], 0)
// d = max(_src[1] - _src, 0)
// rs = rma(u,_len)/rma(d,_len)
// res = 100 - 100/(1+rs)
double RSIOfSeries(double priceArray[], int pos, int len, int arraySize)
{
   // We compute a short RSI over 'len' bars. 
   // MQL4 has iRSI, but we replicate Pine’s approach:

   // We need a rolling calculation of U and D, then Wilder RMA of them.

   if(len <= 0) return(0);
   if(pos + len >= arraySize) return(0);

   // Build arrays for U and D over the chunk
   double rmaU = 0, rmaD = 0;
   double prevRmaU = 0, prevRmaD = 0;
   bool   firstCalc = true;

   // We go from oldest to newest: bar pos+len-1 up to pos
   // so that the last step is "pos" (the current bar).
   for(int i=len; i>0; i--) 
   {
      int currentBar   = pos + (i-1);
      int previousBar  = currentBar+1; // next to the right in MQL4 array

      if(previousBar >= arraySize) continue;

      double u = MathMax(priceArray[currentBar] - priceArray[previousBar], 0);
      double d = MathMax(priceArray[previousBar] - priceArray[currentBar], 0);

      if(firstCalc)
      {
         // initialize
         prevRmaU = u;
         prevRmaD = d;
         firstCalc = false;
      }
      else
      {
         // update
         prevRmaU = WilderRMA(u, prevRmaU, len);
         prevRmaD = WilderRMA(d, prevRmaD, len);
      }
   }

   // after finishing the loop, the last prevRmaU and prevRmaD are the final
   // for the bar 'pos'
   if(prevRmaD == 0) return(100.0); // avoid div-by-zero

   double rs  = prevRmaU / prevRmaD;
   double rsi = 100.0 - (100.0 / (1.0 + rs));

   return(rsi);
}

// Simple Harmonic function _simple_harmonic(_src, _len)
// Implementation note: We have to interpret c0= _src[pos], c1= _src[pos+1], c2= _src[pos+2], etc.
double SimpleHarmonic(double priceArray[], int pos, int len, int arraySize)
{
   if(pos+2 >= arraySize) return(0);
   double c0 = priceArray[pos];
   double c1 = priceArray[pos+1];
   double c2 = priceArray[pos+2];
   double v0 = c0 - c1;
   double v1 = c1 - c2;
   double a0 = v0 - v1;

   // We then take an EMA of a0 over length => a1
   // For simplicity, we'll do a naive rolling approach again from pos+2 down to pos
   // but since a0 is just 1 value (?), we’ll treat this with caution.
   // In the original Pine, a0 is a series (bar by bar).  This is more advanced.  

   // We'll approximate by just using the single a0’s WilderRMA for now:
   // (Alternatively, you might store a separate buffer for a0 each bar.)
   double a1 = a0; // or some smoothing if you want

   // t0 = 2 * pi * sqrt( abs(v0 / a1) )
   double pi = 3.14159265359;
   double denominator = a1;
   if(denominator == 0) denominator = 1e-9;
   double t0 = 2.0 * pi * MathSqrt(MathAbs(v0/denominator));

   // t1 = c0 > c1 ? t0 : -t0
   double t1 = (c0 > c1)? t0 : -t0;

   // In Pine, v3 = ema(t1, len) and t3 = ema(t0, len). We need bar-by-bar. 
   // For demonstration, we’ll just return the final SHO = (v3 / t3)*100 
   // by approximating v3 ~ t1 and t3 ~ t0.  A more faithful port 
   // requires storing the entire t1, t0 series in arrays & applying EMA across bars.
   // This is a big difference between Pine’s approach vs. MQL4 single-pass approach.

   double v3 = t1; 
   double t3 = t0;
   if(t3 == 0) return(0);
   double sho = (v3/t3)*100.0;

   return(sho);
}

// _karo(_src, _len)
// This is also a multi-bar logic in Pine that uses EMA. We simplify similarly.
double Karo(double priceArray[], int pos, int len, int arraySize)
{
   // src = ema(_src, _len)
   // Then some ratio-based logic.  
   // Here we do single-step for the current bar only (since a full port 
   // needs storing time series).
   // We’ll approximate by using normal average of last 'len' bars:

   if(pos + len >= arraySize) return(0);

   double sum = 0;
   for(int i=0; i<len; i++)
   {
      sum += priceArray[pos + i];
   }
   double src = sum / len; // approximate the "ema" with an "sma" for example

   // a = ema(src < src[1] ? src/src[1] : 0, _len)
   // b = ...
   // The original code references src[1]. That means the bar to the right in MQL4 indexing.
   // We'll just do a single-step approximation.

   double src1 = (pos+1 < arraySize) ? priceArray[pos+1] : src;
   double ratioCurrent = 0;
   if(src1 != 0.0 && src < src1) ratioCurrent = src/src1;

   double ratioOpposite = 0;
   if(src1 != 0.0 && src > src1) ratioOpposite = src/src1;

   // c = (src/src[1]) / ((src/src[1]) + b)
   // d = 2 * ((src/src[1]) / ((src/src[1]) + c*a)) - 1
   // We’ll approximate:

   double cDen = ratioCurrent + ratioOpposite; 
   if(cDen == 0) cDen = 1e-9;
   double cVal = ratioCurrent / cDen;

   double karoden = ratioCurrent + cVal*ratioCurrent; 
   if(karoden == 0) karoden = 1e-9;
   double dVal = 2.0 * (ratioCurrent / karoden) - 1.0;

   return(dVal);
}

// _moscillator(_src, _len)
// Loops from 1 to _len
// if s0 > si => +1, if s0 < si => -1, else 0, sum them up
double MOscillator(double priceArray[], int pos, int len, int arraySize)
{
   // s0 = _src[pos], si = _src[pos+i]
   // sum up +1 / -1
   if(pos + len >= arraySize) return(0);

   double s0  = priceArray[pos];
   int    sum = 0;
   for(int i=1; i<=len; i++)
   {
      int index = pos + i;
      if(index >= arraySize) break; 
      double si = priceArray[index];
      if(s0 > si) sum += 1; 
      else if(s0 < si) sum -= 1; 
   }
   return(sum);
}

// typ7(_typ, _src, _len)
// In your Pine, you allowed multiple modes, but in your final code you used rsi_type. 
// We replicate only the “RSI” path (since your variable `typ5 = rsi_type`).
// If you want to expand to “no_type, simp_harmon_type, karo_type, m_type”, add more logic.
double Typ7RSIOnly(double currentPV, int pos, int len, int arraySize, double priceArray[])
{
   // We'll just do the RSI path from your code:
   //   _typ1 == rsi_type ? _rsi(_pv(...),_len) : ...
   // Because you set typ5 = rsi_type in the final script.
   // So effectively: volatility_rsi = _rsi( _pv(src0,VolatilityLength), VolatilityLength ).

   // We pass in "currentPV" which is _pv(...) for the current bar.
   // Now we build a small array so we can feed RSIOfSeries().
   // But that requires a time series. We'll create a synthetic array with currentPV repeated,
   // which doesn’t help RSI. Real port would store PV for each bar. 
   // The correct approach is: for each bar, compute PV, store it in an array, then feed that array to RSIOfSeries for the current bar.

   return(currentPV); // <--- Stub if you only want the raw _pv()  
   
   // If you want the RSI of that PV time series, do the following:
   //    1) Build an array that holds PV for all bars.
   //    2) Then pass that array to RSIOfSeries(...).
   // This example code, for simplicity, just returns the raw PV. 
}

//--------------------------------------------
//                 OnInit
//--------------------------------------------
int OnInit()
{
   // 3 buffers
   SetIndexStyle(0, DRAW_LINE, EMPTY, 2, clrOrange);
   SetIndexBuffer(0, VolatilityRSIBuffer);

   SetIndexStyle(1, DRAW_LINE, STYLE_DOT, 1, clrOrange);
   SetIndexBuffer(1, HorizontalLine30);

   SetIndexStyle(2, DRAW_LINE, STYLE_DOT, 1, clrSaddleBrown);
   SetIndexBuffer(2, HorizontalLine4);

   IndicatorShortName("Breakout Detector (Converted from PineScript)");
   return(INIT_SUCCEEDED);
}

//--------------------------------------------
//               OnCalculate
//--------------------------------------------
// typical signature for MQL4: 
// OnCalculate(int rates_total, int prev_calculated, 
//             const double &price[], ... ) 
// but we’ll just do a simplified version:
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
   // We want to fill VolatilityRSIBuffer for each bar.  
   // In Pine:  volatility_rsi = _rsi( _pv(hlc3,VolatilityLength), VolatilityLength )
   // We'll do the best we can in MQL4 with a single pass.

   // Build an array for storing PV on each bar so we can then do RSI if desired
   // For simplicity, let’s just store the PV and not do RSI on it (since that is more advanced).
   // You can expand to do full RSI if needed.

   static double PVArray[1]; // dynamic approach below
   ArrayResize(PVArray, rates_total);

   // prepare to read High[], Low[], Close[]; in MQL4 these are global arrays
   // Make sure we handle indexing carefully: bar i in MQL4 = i-th from the left in OnCalculate loops.
   // We'll fill from oldest bar to newest bar: i = 0..rates_total-1
   // but watch out for access High[i], Low[i], etc. in the same direction.

   int limit = rates_total - prev_calculated;
   if(limit > rates_total) limit = rates_total;

   for(int i=0; i<limit; i++)
   {
      // compute PV for bar i
      double pvVal = PVCalculation(i, VolatilityLength);
      PVArray[i] = pvVal;  // store it so we can do RSI, etc.

      // If you want RSI of PV, you’d do something like:
      // double rsiVal = RSIOfSeries(PVArray, i, VolatilityLength, rates_total);
      // VolatilityRSIBuffer[i] = rsiVal;

      // Here we just do the stub for demonstration:
      VolatilityRSIBuffer[i] = pvVal; 
      
      // Set the lines
      HorizontalLine30[i] = 30.0;
      HorizontalLine4[i]  = 4.0;
   }

   //-------------------------
   // Emulate alertcondition(ta.crossunder(volatility_rsi,15))
   // crossunder => volatility_rsi just went from >= 15 to < 15
   // We can check the last bar processed:
   if(rates_total > 1)
   {
      int iCur = limit - 1;           // the most recent bar we processed
      int iPrev = iCur + 1;           // the bar to the right (previous in Pine sense)
      if(iPrev < rates_total)
      {
         double curVal  = VolatilityRSIBuffer[iCur];
         double prevVal = VolatilityRSIBuffer[iPrev];
         if(curVal < 15.0 && prevVal >= 15.0)
         {
            Alert("Breakout Zone: crossunder 15 detected on bar ", iCur);
         }
      }
   }
   //-------------------------

   return(rates_total);
}




-------------------------------------------------------------------------------------------------------------------------------------------------

Attached is the indicator.


Here is some logic in the PineScript to MQL4 conversion is also attached.
All files in topic