Trouble scaling Moving Average into the 0-100 scale subwindow

 

Hello everyone as the subject says am trying to code or customise  a moving average and display it in the subwindow ive  tried several ways to tuckle the challenge without success as i show in the image below the yellow ma i placed manually and is the desirable output the red ma is however what my code is producing after ive tried to put its values on 0-100 scale  , its not properly scaling  the subwindow need help suggestions solutions or possible alternative to achieve this goal . I must also state as in the window the desrired output has a larger value than the scale we put in i want to achieve such but be able to dynamically scale it to the the define 0-100 scale source code below, your help will be greatly appreciated

Custom MA FalconMomentum

sourrce code

//+------------------------------------------------------------------+
//|                                        Custom Moving Average.mq5 |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"

//--- indicator settings
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red
//--- input parameters
input int            InpMAPeriod=14;         // Period
input int            InpMAShift=0;           // Shift
input ENUM_MA_METHOD InpMAMethod=MODE_EMA;  // Method
// Input parameter for min/max period
input int MinMaxPeriod = 14;
input int InpSlowing = 3;

// Buffers for min and max values
double MinBuffer[];
double MaxBuffer[];
//--- indicator buffer
double ExtLineBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
// Global variables for smoothed min and max
double smoothed_min = 0;
double smoothed_max = 0;
bool first_calculation = true;
int smoothPeriod = 20;
//
void OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
//--- set accuracy
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
//--- set first bar from what index will be drawn
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,InpMAPeriod);
//--- line shifts when drawing
   PlotIndexSetInteger(0,PLOT_SHIFT,InpMAShift);
   
//--- name for DataWindow
   string short_name;
   switch(InpMAMethod)
     {
      case MODE_EMA :
         short_name="FalconMommentum";
         break;
      case MODE_LWMA :
         short_name="LWMA";
         break;
      case MODE_SMA :
         short_name="SMA";
         break;
      case MODE_SMMA :
         short_name="SMMA";
         break;
      default :
         short_name="unknown ma";
     }
   //--- indicator buffers mapping
   SetIndexBuffer(0, ExtLineBuffer, INDICATOR_DATA);
   ArrayResize(MinBuffer, 100000); // give a large initial size. It will resize in Oncalculate
   ArrayResize(MaxBuffer, 100000); // give a large initial size. It will resize in Oncalculate
   //--- set accuracy
   IndicatorSetInteger(INDICATOR_DIGITS, 2);
   //--- set levels
   IndicatorSetInteger(INDICATOR_LEVELS, 2);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 80);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, 20);
   //--- set maximum and minimum for subwindow
   IndicatorSetDouble(INDICATOR_MINIMUM, 0);
   IndicatorSetDouble(INDICATOR_MAXIMUM, 100);
   //--- name for DataWindow and indicator subwindow label
  // short_name = StringFormat("CustomMA(%d,%d,%d)", InpMAPeriod, MinMaxPeriod, InpSlowing);
   IndicatorSetString(INDICATOR_SHORTNAME, short_name);
   PlotIndexSetString(0, PLOT_LABEL, "Main");
   //--- sets first bar from what index will be drawn
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpMAPeriod + InpMAPeriod - 2);
  }
//+------------------------------------------------------------------+
//|  Moving Average                                                  |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
   ArrayResize(MinBuffer, rates_total);
   ArrayResize(MaxBuffer, rates_total);
   
   if (rates_total < InpMAPeriod - 1 + begin)
     return (0);

   if (prev_calculated == 0)
     {
      ArrayInitialize(ExtLineBuffer, 0);
     // PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpMAPeriod - 1 + begin);
     }

   switch (InpMAMethod)
     {
      case MODE_EMA:
        CalculateEMA(rates_total, prev_calculated, begin, price);
        break;
      case MODE_LWMA:
        CalculateLWMA(rates_total, prev_calculated, begin, price);
        break;
      case MODE_SMMA:
        CalculateSmoothedMA(rates_total, prev_calculated, begin, price);
        break;
      case MODE_SMA:
        CalculateSimpleMA(rates_total, prev_calculated, begin, price);
        break;
     }

   // Calculate min and max
   // Calculate min and max over MinMaxPeriod
   
   for (int i = InpMAMethod - 1; i < rates_total; i++)
     {
      double min_val = -100000.0; // Large initial value
      double max_val = 100000.0; // Large initial value
      for (int j = i - InpMAMethod + 1; j <= i; j++)
        {
         if (ExtLineBuffer[j] < min_val)
           min_val = ExtLineBuffer[j];
         if (ExtLineBuffer[j] > max_val)
           max_val = ExtLineBuffer[j];
        }
      MinBuffer[i] = min_val;
      MaxBuffer[i] = max_val;
     }

   // Normalize the values using MinBuffer and MaxBuffer
    for(int i = InpMAMethod-1; i < rates_total && !IsStopped(); i++)
    {
    int start_k = MathMax(0, i - InpSlowing + 1); 
        double sum_low = 0.0;
        double sum_high = 0.0;
        for(int k = start_k; k<=i; k++)
        {
            sum_low += (ExtLineBuffer[k] - MinBuffer[k]);
            sum_high += (MaxBuffer[k] - MinBuffer[k]);
        }
        if(sum_high == 0.0)
        {
            ExtLineBuffer[i] = 100.0;
        }else{
            ExtLineBuffer[i] = sum_low / sum_high * 100;
        }
    }

   // ... your existing level and scaling code ...

    // Calculate dynamic levels
    double level20 = 20;
    double level80 = 80;

    // Set levels.
    IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, level80);
    IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, level20);

   // Print min and max values for debugging
   Print("Min MA value: ", smoothed_min, ", Max MA value: ", smoothed_max);
   return (rates_total);
  }
//|   simple moving average                                          |
//+------------------------------------------------------------------+
void CalculateSimpleMA(int rates_total,int prev_calculated,int begin,const double &price[])
  {
   int i,start;
//--- first calculation or number of bars was changed
   if(prev_calculated==0)
     {
      start=InpMAPeriod+begin;
      //--- set empty value for first start bars
      for(i=0; i<start-1; i++)
         ExtLineBuffer[i]=0.0;
      //--- calculate first visible value
      double first_value=0;
      for(i=begin; i<start; i++)
         first_value+=price[i];
      first_value/=InpMAPeriod;
      ExtLineBuffer[start-1]=first_value;
     }
   else
      start=prev_calculated-1;
//--- main loop
   for(i=start; i<rates_total && !IsStopped(); i++)
      ExtLineBuffer[i]=ExtLineBuffer[i-1]+(price[i]-price[i-InpMAPeriod])/InpMAPeriod;
  }
//+------------------------------------------------------------------+
//|  exponential moving average                                      |
//+------------------------------------------------------------------+
void CalculateEMA(int rates_total,int prev_calculated,int begin,const double &price[])
  {
   int    i,start;
   double SmoothFactor=2.0/(1.0+InpMAPeriod);
//--- first calculation or number of bars was changed
   if(prev_calculated==0)
     {
      start=InpMAPeriod+begin;
      ExtLineBuffer[begin]=price[begin];
      for(i=begin+1; i<start; i++)
         ExtLineBuffer[i]=price[i]*SmoothFactor+ExtLineBuffer[i-1]*(1.0-SmoothFactor);
     }
   else
      start=prev_calculated-1;
//--- main loop
   for(i=start; i<rates_total && !IsStopped(); i++)
      ExtLineBuffer[i]=price[i]*SmoothFactor+ExtLineBuffer[i-1]*(1.0-SmoothFactor);
  }
//+------------------------------------------------------------------+
//|  linear weighted moving average                                  |
//+------------------------------------------------------------------+
void CalculateLWMA(int rates_total,int prev_calculated,int begin,const double &price[])
  {
   int    weight=0;
   int    i,l,start;
   double sum=0.0,lsum=0.0;
//--- first calculation or number of bars was changed
   if(prev_calculated<=InpMAPeriod+begin+2)
     {
      start=InpMAPeriod+begin;
      //--- set empty value for first start bars
      for(i=0; i<start; i++)
         ExtLineBuffer[i]=0.0;
     }
   else
      start=prev_calculated-1;

   for(i=start-InpMAPeriod,l=1; i<start; i++,l++)
     {
      sum   +=price[i]*l;
      lsum  +=price[i];
      weight+=l;
     }
   ExtLineBuffer[start-1]=sum/weight;
//--- main loop
   for(i=start; i<rates_total && !IsStopped(); i++)
     {
      sum             =sum-lsum+price[i]*InpMAPeriod;
      lsum            =lsum-price[i-InpMAPeriod]+price[i];
      ExtLineBuffer[i]=sum/weight;
     }
  }
//+------------------------------------------------------------------+
//|  smoothed moving average                                         |
//+------------------------------------------------------------------+
void CalculateSmoothedMA(int rates_total,int prev_calculated,int begin,const double &price[])
  {
   int i,start;
//--- first calculation or number of bars was changed
   if(prev_calculated==0)
     {
      start=InpMAPeriod+begin;
      //--- set empty value for first start bars
      for(i=0; i<start-1; i++)
         ExtLineBuffer[i]=0.0;
      //--- calculate first visible value
      double first_value=0;
      for(i=begin; i<start; i++)
         first_value+=price[i];
      first_value/=InpMAPeriod;
      ExtLineBuffer[start-1]=first_value;
     }
   else
      start=prev_calculated-1;
//--- main loop
   for(i=start; i<rates_total && !IsStopped(); i++)
      ExtLineBuffer[i]=(ExtLineBuffer[i-1]*(InpMAPeriod-1)+price[i])/InpMAPeriod;
  }
//+------------------------------------------------------------------+
 
ZhuwaoAbel:

Hello everyone as the subject says am trying to code or customise  a moving average and display it in the subwindow ive  tried several ways to tuckle the challenge without success as i show in the image below the yellow ma i placed manually and is the desirable output the red ma is however what my code is producing after ive tried to put its values on 0-100 scale  , its not properly scaling  the subwindow need help suggestions solutions or possible alternative to achieve this goal . I must also state as in the window the desrired output has a larger value than the scale we put in i want to achieve such but be able to dynamically scale it to the the define 0-100 scale source code below, your help will be greatly appreciated

sourrce code

with a first glance :

      double min_val = -100000.0; // Large initial value
      double max_val = 100000.0; // Large initial value

switch those around , the min val should be huge at first so new minimums can be captured 

i use min=INT_MAX and max=INT_MIN 
 
Lorentzos Roussos #:

with a first glance :

switch those around , the min val should be huge at first so new minimums can be captured 

i use min=INT_MAX and max=INT_MIN 
i have changed the values and now  the indicator is showing a vertical line ,
 

You can't properly normalize a moving average because of the fact that the magnitude of prices can vary a lot over time (the same way a price chart does). So why in Gods name do you need to normalize the moving average from 0 to 100? A moving average isn't an oscillator, as it doesn't have a fixed range. There was however some scientific codes which create an oscillator based on a moving average, one comes to mind called "Angle of averages"

 
Conor Mcnamara #:

You can't properly normalize a moving average because of the fact that the magnitude of prices can vary a lot over time (the same way a price chart does). So why in Gods name do you need to normalize the moving average from 0 to 100? A moving average isn't an oscillator, it doesn't have a fixed range. There was however some scientific codes which create an oscillator based on a moving average, one comes to mind called "Angle of averages"

perhaps he could measure the delta of a fast and slow ma divided by the atr but still he'd have to limit it.

 

I decided to do a raw min/max normalization like he was trying to do, I seem to get it working, but looks a bit odd. Added the dynamic levels as well


the levels correspond to 80 and 20 of the normalized plot, as the levels mark 20% above the minimum and 20% below the maximum

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[])
  {
   ArrayResize(MinBuffer, rates_total);
   ArrayResize(MaxBuffer, rates_total);
   
   if (rates_total < InpMAPeriod - 1)
     return (0);
     
   int limit;

   if (prev_calculated == 0)
     {
      ArrayInitialize(NormalizedBuffer, 0);
      
      limit = prev_calculated;
     }
     else{    
      limit = prev_calculated - 1;
     }

   switch (InpMAMethod)
     {
      case MODE_EMA:
        CalculateEMA(rates_total, prev_calculated, 1, close);
        break;
      case MODE_LWMA:
        CalculateLWMA(rates_total, prev_calculated, 1, close);
        break;
      case MODE_SMMA:
        CalculateSmoothedMA(rates_total, prev_calculated, 1, close);
        break;
      case MODE_SMA:
        CalculateSimpleMA(rates_total, prev_calculated, 1, close);
        break;
     }
   
   int baseline = 20;
   int scaling_factor = 50;

   int lookback = 50;  // window size
   
   for (int i = limit; i < rates_total; i++)
   {
      double min_val = DBL_MAX;
      double max_val = -DBL_MAX;
   
      int start = MathMax(0, i - lookback + 1);
      
      // Find min/max within the rolling window
      for (int j = start; j <= i; j++)
      {
         if (ExtLineBuffer[j] < min_val) min_val = ExtLineBuffer[j]; // update var
         if (ExtLineBuffer[j] > max_val) max_val = ExtLineBuffer[j]; // update var
      }
   
      MinBuffer[i] = min_val;
      MaxBuffer[i] = max_val;
   
     // NormalizedBuffer[i] = ((ExtLineBuffer[i] - min_val) / (max_val - min_val)) * 100;   
      
      NormalizedBuffer[i] = baseline + (((ExtLineBuffer[i] - min_val) / (max_val - min_val)) * scaling_factor);

   }
      
   static double max_normalized = -DBL_MAX;
   static double min_normalized = DBL_MAX;
   
   for (int i = limit; i < rates_total; i++)
   {
       if (NormalizedBuffer[i] < min_normalized) min_normalized = NormalizedBuffer[i];
       if (NormalizedBuffer[i] > max_normalized) max_normalized = NormalizedBuffer[i];
   }
   
   // calculate range
   double range = max_normalized - min_normalized;
   
   // dynamic levels
   double level20 = min_normalized + (range * 0.2);  // 20% above min
   double level80 = max_normalized - (range * 0.2);  // 20% below max
   
   // Set levels
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, level80);
   IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, level20);
   

   // Print min and max values for debugging
 //  Print("Min MA value: ", smoothed_min, ", Max MA value: ", smoothed_max);
   return (rates_total);
  }


this is for science only. If you would say that a moving average by itself has a 50% win rate, then normalizing it might bring the win rate down to 30% because of confusing slopes

 
Conor Mcnamara #:

I decided to do a raw min/max normalization like he was trying to do, I seem to get it working, but looks a bit odd. Added the dynamic levels as well


the levels correspond to 80 and 20 of the normalized plot, as the levels mark 20% above the minimum and 20% below the maximum


this is for science only. If you would say that a moving average by itself has a 50% win rate, then normalizing it might bring the win rate down to 30% because of confusing slopes

i guess you are right so ive opted to use a template since its once placed manually you got  no such problems just have to retreive existing indicators from the chart

  LoadTemplates1(BaseTemplateName,LowerTemplateName,BaseTimeframe,Symbol());

Now the idea here is to just retain the original values place manually 

  Preloaded temp

but as in the image above the the desired ma14 has a larger buffer values 20445.03712 which makes it kind of hard to pin point when it is above the 80 level or below the 20level which is the new problem or what i was trying to avoid but is my best channce to achieve the objective

 
You can't do that.
You can't visualize the MA in the range 0-100.

What you see is just an adaptation that metatrader does because you are using 2 indicators that have totally different range values into the same subwindow.

Your MA never touches 20 or 80 values.

The only thing you can do is to normalize the MA values to express it like an oscillator, but will be totally different of what you see right now.

 
Fabio Cavalloni #:
You can't do that.
You can't visualize the MA in the range 0-100.

What you see is just an adaptation that metatrader does because you are using 2 indicators that have totally different range values into the same subwindow.

Your MA never touches 20 or 80 values.

The only thing you can do is to normalize the MA values to express it like an oscillator, but will be totally different of what you see right now.

Did you not see my post? You can do it...as I did it. But it's not exactly a wise thing to do.
 
ZhuwaoAbel #:

but as in the image above the the desired ma14 has a larger buffer values 20445.03712 which makes it kind of hard to pin point when it is above the 80 level or below the 20level which is the new problem or what i was trying to avoid but is my best channce to achieve the objective

Nobody clearly knows your objective. What is your main objective? When you start any topic, that should be made clear. Without knowing your true objective, its impossible for you to be helped.

It sounds like you want to put an MA on a stochastic or RSI, but you haven't mentioned those indicators so nobody actually knows what your goal is.
 
Conor Mcnamara #:
Did you not see my post? You can do it...as I did it. But it's not exactly a wise thing to do.

"The only thing you can do is to normalize the MA values to express it like an oscillator, but will be totally different of what you see right now."