How to Create a Support and Resistance Indicator in MQL5?

Introduction to Support and Resistance in Trading

Understanding Support and Resistance Levels

Support and resistance are fundamental concepts in technical analysis. Support represents a price level where a downtrend is expected to pause due to a concentration of buyers. Resistance, conversely, is a price level where an uptrend is expected to pause because of a concentration of sellers. These levels are not exact prices, but rather zones where buying or selling pressure is anticipated.

Importance of Identifying Key Levels in MQL5

Identifying support and resistance levels programmatically in MQL5 allows for the automation of trading strategies. An MQL5 indicator can automatically detect these levels, freeing up traders to focus on higher-level decision-making and strategy refinement. By translating these subjective visual assessments into concrete numerical values, we can use Expert Advisors (EAs) to execute trades based on these key levels.

Overview of the Indicator’s Functionality

The support and resistance indicator we’ll create will identify potential support and resistance zones based on historical price data. The core logic will involve identifying significant highs and lows (pivot points) and drawing lines or zones on the chart to represent these levels. The indicator will allow customization of parameters such as the period used to identify pivot points, and the price source (e.g., close, high, low).

MQL5 Implementation: Core Logic and Functions

Defining Input Parameters (Periods, Price Source)

Input parameters allow users to customize the indicator’s behavior. In MQL5, these are defined using the input keyword:

input int PivotPeriod = 14;   // Period for identifying pivot points
input ENUM_APPLIED_PRICE PriceType = PRICE_CLOSE; // Price used for calculations

PivotPeriod determines how many bars to the left and right of a potential pivot point are considered when confirming its significance. PriceType specifies which price (close, open, high, low, etc.) to use in the calculations. ENUM_APPLIED_PRICE is an MQL5 enumeration defining the available price types.

Identifying Highs and Lows (Pivot Points)

The core of the indicator lies in identifying significant highs and lows. A pivot high is a bar where the high is greater than or equal to the highs of the PivotPeriod bars before and after it. A pivot low is a bar where the low is less than or equal to the lows of the PivotPeriod bars before and after it. This logic is implemented in the IsPivotHigh and IsPivotLow functions.

bool IsPivotHigh(int index, int period)
{
   for(int i = 1; i <= period; i++)
   {
      if(High[index] < High[index + i] || High[index] < High[index - i])
         return(false);
   }
   return(true);
}

bool IsPivotLow(int index, int period)
{
   for(int i = 1; i <= period; i++)
   {
      if(Low[index] > Low[index + i] || Low[index] > Low[index - i])
         return(false);
   }
   return(true);
}

Calculating Support and Resistance Levels

Once pivot points are identified, support and resistance levels are calculated. In this example, we’ll simply use the price at the pivot point as the level. A more sophisticated approach might involve averaging prices within a certain range of the pivot point or using a more complex algorithm to determine the level’s strength.

Coding the Indicator in MQL5

Creating the Custom Indicator (iCustom)

To create a custom indicator, create a new file in MetaEditor. The OnInit() function is called when the indicator is initialized. Here, we set up indicator buffers and any necessary initial calculations.

#property indicator_chart_window
#property indicator_buffers 2 // Example: Two buffers for support and resistance values (optional)
#property indicator_plots   2

input int PivotPeriod = 14;   // Period for identifying pivot points
input ENUM_APPLIED_PRICE PriceType = PRICE_CLOSE; // Price used for calculations

double SupportBuffer[];
double ResistanceBuffer[];

int OnInit()
{
   SetIndexBuffer(0, ResistanceBuffer, INDICATOR_DATA);
   SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, clrRed);  // Example: Resistance is a red line

   SetIndexBuffer(1, SupportBuffer, INDICATOR_DATA);
   SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 2, clrGreen); // Example: Support is a green line

   IndicatorSetString(INDICATOR_SHORTNAME, "Support and Resistance");

   ArraySetAsSeries(ResistanceBuffer, true);
   ArraySetAsSeries(SupportBuffer, true);

   return(INIT_SUCCEEDED);
}

#property directives define the indicator’s properties. OnInit() is the initialization function.

Drawing Support and Resistance Lines on the Chart

The OnCalculate() function is called on each tick, or when the chart is refreshed. This is where the main calculations and drawing occur.

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[])
{
   int start = prev_calculated > 0 ? prev_calculated - 1 : PivotPeriod; // Start from the last calculated bar or PivotPeriod

   for(int i = start; i < rates_total; i++)
   {
      double price = iClose(Symbol(), Period(), i); // Get the closing price
      if(PriceType == PRICE_HIGH) price = iHigh(Symbol(), Period(), i);
      else if (PriceType == PRICE_LOW) price = iLow(Symbol(), Period(), i);
      else if (PriceType == PRICE_OPEN) price = iOpen(Symbol(), Period(), i);
      // Check for pivot highs and lows and set buffer values accordingly (using IsPivotHigh and IsPivotLow functions)
      if (IsPivotHigh(i, PivotPeriod)) {
          ResistanceBuffer[i] = high[i];
      } else {
          ResistanceBuffer[i] = EMPTY_VALUE; // Do not draw if not a pivot
      }
      if (IsPivotLow(i, PivotPeriod)) {
          SupportBuffer[i] = low[i];
      } else {
          SupportBuffer[i] = EMPTY_VALUE; // Do not draw if not a pivot
      }
   }

   return(rates_total);
}

In OnCalculate(), we iterate through the bars, check for pivot points using IsPivotHigh and IsPivotLow, and set the corresponding buffer values. EMPTY_VALUE is used to prevent drawing lines where no support or resistance level exists.

Handling Multiple Timeframes (optional)

To access data from other timeframes, use the iClose(), iHigh(), iLow(), iOpen() family of functions with a specific ENUM_TIMEFRAMES value. For example, iClose(Symbol(), PERIOD_H1, 0) gets the closing price of the current symbol on the 1-hour timeframe for the most recent bar.

Alerts and Notifications (optional)

You can add alerts when the price crosses a support or resistance level using the Alert() or PlaySound() functions. Implement this logic within the OnCalculate() function, checking if the current price has crossed a previously identified support or resistance level.

if(Close[i] > ResistanceBuffer[i - 1] && ResistanceBuffer[i-1] != EMPTY_VALUE) {
  Alert("Price crossed resistance at ", ResistanceBuffer[i-1]);
}

Advanced Techniques and Customization

Dynamic Support and Resistance

Instead of static lines, consider using moving averages or other dynamic calculations to create dynamically adjusting support and resistance zones. This can be particularly useful in trending markets.

Filtering False Breakouts

Implement filtering mechanisms to reduce false breakout signals. This could involve requiring a certain percentage price movement beyond the level, confirmation from other indicators, or volume analysis.

Combining with Other Indicators

Enhance the indicator by combining it with other technical indicators like RSI, MACD, or volume analysis. This can improve the accuracy of support and resistance identification and provide more robust trading signals.

Testing and Optimization

Backtesting the Indicator in Strategy Tester

Use the MetaTrader Strategy Tester to backtest the indicator’s performance. This allows you to evaluate its effectiveness over historical data and identify potential weaknesses.

Optimizing Parameters for Different Markets

The PivotPeriod and other input parameters should be optimized for different markets and timeframes. Use the Strategy Tester’s optimization feature to find the best parameter values for specific instruments.

Troubleshooting and Debugging Tips

  • Use the MetaEditor debugger to step through the code and identify errors.
  • Print intermediate values to the Experts log using the Print() function to track the indicator’s calculations.
  • Test the indicator on different symbols and timeframes to ensure its robustness.

Leave a Reply