How to Create an MQL5 Indicator: Buffers, OnCalculate, iCustom, and CopyBuffer

目次

1. What Is mql5-create-indicator?

In MQL5, “create indicator” means building a program that performs custom calculations based on price data and visualizes the results on a chart.
The goal of this article is to help you reach the following state:

  • You can create your own custom indicator
  • You can display it on a chart
  • You understand the basic mechanism, especially Buffer and OnCalculate

In MQL5, an indicator is not only a display tool. It is also important as a data-generation component that can work with an EA, or Expert Advisor, for automated trading. Once you understand this part, your options for EA development become much wider.


1.1 What Is an MQL5 Indicator?

An MQL5 indicator is a program that processes and visualizes price data, such as Open, High, Low, and Close.

Common examples:

  • Moving Average (MA)
  • RSI
  • MACD

All of these follow the same structure: “price data → calculation → display.”

The key points are:

  • An indicator does not place trades
  • It displays information used for decision-making
  • Calculation results are stored in arrays called Buffers

A common beginner confusion:

ConceptRole
IndicatorAnalysis and display
EATrading and execution

If you do not understand this difference, the design can break down from the start.


1.2 What You Can Do with mql5-create-indicator

Creating an indicator allows you to do the following:

  • Visualize custom logic, such as special signals
  • Support entry decisions
  • Connect with an EA using iCustom, explained later

The most important point is “EA integration.”

double value = iCustom(Symbol(), Period(), "MyIndicator", 0, 0);

In this way, an EA can retrieve values from your custom indicator.

In other words, you can create this division of roles:

  • Indicator → handles calculation
  • EA → handles execution

This is very important in real projects because separating logic improves maintainability.


1.3 Points Beginners Should Understand First

The two most important ideas in indicator creation are the following.

1. Display and calculation are separate

  • Calculation is done in OnCalculate
  • Display is done through IndicatorBuffer

As a code structure, the flow is:

Calculate → store in an array → draw automatically

That is the basic flow.

2. OnCalculate is the center of everything

OnCalculate is the function called every time price data is updated.

What you do here is simple:

  • Get data
  • Calculate
  • Put the result into an array

3. Common beginner traps (important)

The following issues happen very often:

  • No value is assigned to the Buffer → nothing is displayed
  • Array index mistake → error
  • Poor understanding of OnCalculate → behavior is not as intended

The most common problem is “I wrote the code, but nothing appears.”
In about 90% of cases, the cause is an unset Buffer or a missing value assignment.

2. Overall Flow for Creating an Indicator

Creating an indicator may look complicated, but in practice you only need to follow a fixed sequence of steps.
This section explains the process in the order “overview → execution steps” so beginners do not get lost.


2.1 Creation Steps (Overview)

First, understand the overall flow.

1. Open MetaEditor
2. Create a new indicator
3. Write the code
4. Compile
5. Apply it to a chart in MT5

The important point is to understand the big picture before writing code.

Beginners often fail because they:

  • Start writing code immediately
  • Do not know where the file is saved
  • Do not know how to check the indicator after compiling

To avoid this, first lock in the workflow.


2.2 Understanding the File Structure

Indicators are saved in a specific MT5 folder.

Basic structure:

MQL5/
 └ Indicators/
     └ YourIndicator.mq5

Key points:

  • .mq5 is the source code
  • After compiling, an .ex5 file is generated as the executable file

When you create the file in MetaEditor, it is automatically saved in this location.

Important notes

  • If you use the wrong folder, the indicator will not appear in MT5
  • If you do not compile it, it cannot be used

2.3 Shortest Steps to Run It (For Beginners)

If you follow these steps as written, the indicator will be displayed.

Steps

  1. Start MetaTrader 5
  2. Open “Tools → MetaQuotes Language Editor”
  3. Select “New → Custom Indicator”
  4. Enter a name, for example MyIndicator
  5. Leave the wizard settings at their defaults
  6. Save the generated code as is
  7. Compile it with F7
  8. Return to MT5
  9. From Navigator → Indicators, drag the created indicator onto the chart

Minimum code for a quick operation check

#property indicator_chart_window

double buffer[];

int OnInit()
{
   SetIndexBuffer(0, buffer);
   return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
   for(int i = 0; i < rates_total; i++)
   {
      buffer[i] = price[i];
   }
   return(rates_total);
}

This code is an indicator that simply displays the price as is.


Common Mistakes (Important)

Here are the points where beginners often get stuck.

1. Not compiling

  • → It will not appear in MT5

2. It does not appear in Navigator

  • → The folder is wrong, or compilation failed

3. Nothing appears even after dragging it onto the chart

  • → Buffer setup mistake, explained in the next chapter

4. Ignoring errors

  • → Always check the compile log

Practical Note

  • The code generated by the wizard is a template
  • In real work, you will always customize it
  • At first, prioritize making something that works

3. Understanding How Indicators Work with Minimal Code

In this section, we break down the minimal code shown in the previous chapter and accurately understand how an indicator works.
Once you understand this, you can customize it later by adding logic.


3.1 Basic Structure of an Indicator (Overview)

An MQL5 indicator consists of the following three elements:

1. Property definitions (display settings)
2. Initialization processing (OnInit)
3. Calculation processing (OnCalculate)

Here is the minimal code again.

#property indicator_chart_window

double buffer[];

int OnInit()
{
   SetIndexBuffer(0, buffer);
   return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
   for(int i = 0; i < rates_total; i++)
   {
      buffer[i] = price[i];
   }
   return(rates_total);
}

This is the smallest structure for drawing the price as is.

MQL5 indicator data flow diagram showing how price data is processed in OnCalculate, calculated values are stored in an indicator buffer, and visualized on a trading chart, with code example using SetIndexBuffer and buffer[i] assignment, highlighting execution flow and system structure for MetaTrader 5 custom indicator development.

3.2 Role of #property (Display Settings)

#property indicator_chart_window

This specifies where the indicator is displayed.

  • indicator_chart_window → on the main chart
  • indicator_separate_window → in a separate subwindow, such as RSI

Note

  • If you choose the wrong setting, the indicator may be “displayed but not visible”
  • Beginners should start with chart_window

3.3 IndicatorBuffer (The Actual Display Data)

double buffer[];

This is an array that stores the data drawn on the chart.

SetIndexBuffer(0, buffer);

This single line is extremely important.

Meaning:

  • It registers the buffer array as the drawing target

Core idea

  • Putting values into the Buffer means they are drawn on the chart
  • If there are no values, nothing is displayed

Common mistakes

  • Forgetting SetIndexBuffer → nothing is displayed
  • Using multiple arrays but mismatching their indexes

3.4 OnInit (Initialization Processing)

int OnInit()
{
   SetIndexBuffer(0, buffer);
   return(INIT_SUCCEEDED);
}

OnInit is a function that runs only once when the indicator is loaded.

What you do here:

  • Register Buffers
  • Initialize parameters
  • Set drawing options

Practical points

  • Do not run heavy processing here; keep it lightweight
  • Configuration mistakes are fixed at this stage

3.5 OnCalculate (Most Important)

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])

This is the heart of the indicator.

Role

  • It is called every time new data arrives
  • All calculation processing is done here

Meaning of the arguments, simplified for beginners

  • rates_total: current number of bars
  • prev_calculated: number of bars calculated up to the previous call
  • price[]: price data, used as the input here

3.6 Loop Processing (The Actual Drawing Logic)

for(int i = 0; i < rates_total; i++)
{
   buffer[i] = price[i];
}

This is the actual processing.

Meaning:

  • Set a value for every bar
  • The result is drawn as is

Core understanding

Data → calculation → assign to buffer[i] → draw

3.7 Beginner Traps You Will Almost Certainly Hit (Important)

This section is very important.

1. No value is assigned to buffer

→ Nothing is displayed. This is the most common issue.

2. Index mismatch

→ array out of range error

3. Not understanding rates_total

→ Wrong loop range

4. Not using prev_calculated

→ Full recalculation every time, causing unnecessary load


3.8 Practical Performance Note

For beginners, the first priority is to make it work. In real projects, however, the following matters.

Bad example: full recalculation every time

for(int i = 0; i < rates_total; i++)

Improved example: differential calculation

int start = prev_calculated > 0 ? prev_calculated - 1 : 0;

for(int i = start; i < rates_total; i++)
{
   buffer[i] = price[i];
}

This prevents unnecessary recalculation.

4. IndicatorBuffer and Drawing Settings (Practical Level)

This section explains practical use of IndicatorBuffer, which determines both the indicator’s appearance and data structure.
Once you understand this, you can do more than display a single line. You can create multiple lines, signal displays, and custom drawing.


4.1 IndicatorBuffer Basics (Review and Extension)

An IndicatorBuffer is an array that stores values drawn on the chart.

double buffer[];
SetIndexBuffer(0, buffer);

In real projects, however, using multiple Buffers is standard.

Examples:

  • Moving Average: one line
  • MACD: two lines plus histogram
  • Signal display: arrows

4.2 How to Use Multiple Buffers

To draw multiple lines, define multiple Buffers.

Example: two lines

double buffer1[];
double buffer2[];

int OnInit()
{
   SetIndexBuffer(0, buffer1);
   SetIndexBuffer(1, buffer2);
   return(INIT_SUCCEEDED);
}

OnCalculate side

for(int i = 0; i < rates_total; i++)
{
   buffer1[i] = price[i];
   buffer2[i] = price[i] * 1.01;
}

Points

  • Indexes such as 0, 1, and 2 become the display order
  • If the buffer and index mapping is wrong, it becomes a bug

4.3 Drawing Style Settings (Color, Line, Type)

Display is controlled through properties.

#property indicator_plots 2
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrBlue
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrRed

Common drawing types

TypeDescription
DRAW_LINELine
DRAW_HISTOGRAMBar chart
DRAW_ARROWArrow

Example: arrow display

#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrGreen

4.4 Importance of EMPTY_VALUE (Very Important)

For parts you do not want to draw, always use the following:

buffer[i] = EMPTY_VALUE;

Reason

  • If a value exists, it will be drawn
  • It can cause unwanted lines to connect

Example: conditional display

if(price[i] > price[i-1])
   buffer[i] = price[i];
else
   buffer[i] = EMPTY_VALUE;

4.5 Beginner Traps (Important)

This chapter is a common source of bugs.

1. indicator_plots is not set

→ Multiple Buffers are not displayed

2. Wrong SetIndexBuffer order

→ Displays are swapped

3. EMPTY_VALUE is not used

→ Unintended lines appear

4. Misunderstanding buffer size

→ ArrayResize is not needed because it is managed automatically


4.6 Basics of Arrow and Signal Indicators

For trading signals, arrows are commonly used.

Example: display an arrow on an upward move

#property indicator_type1 DRAW_ARROW

for(int i = 1; i < rates_total; i++)
{
   if(price[i] > price[i-1])
      buffer[i] = price[i];
   else
      buffer[i] = EMPTY_VALUE;
}

Notes

  • You may need to set an arrow code, such as Wingdings
  • The display position is determined by the price value

4.7 Practical View: Separating the Design

In real projects, the following structure is recommended.

Calculation logic → assign to buffer → display settings

Bad examples:

  • Calculation and display are mixed together
  • Conditional branching is too complex

Good examples:

  • Move logic into functions
  • Use the buffer only for final output

4.8 Performance and Stability

Multiple Buffers can affect processing load.

Points to watch

  • Do not create unnecessary Buffers
  • Use differential calculation consistently
  • Optimize conditional branches

5. Advanced OnCalculate Usage (Differential Calculation and Practical Logic)

This section explains differential calculation using prev_calculated and practical logic design so you can use OnCalculate at a real project level.
The conclusion is simple: optimizing OnCalculate is the core of performance and stability.


5.1 Core Meaning of prev_calculated (Most Important)

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])

Conclusion

  • prev_calculated is the number of bars calculated up to the previous call

In other words:

You only need to calculate the newly added part

That is the main idea.


5.2 Basic Pattern for Differential Calculation

Bad pattern, common among beginners

for(int i = 0; i < rates_total; i++)
{
   buffer[i] = price[i];
}

Problems:

  • Recalculates all data every time
  • Creates unnecessary CPU load
  • Can cause latency risk when used with an EA

Good pattern, practical standard

int start = prev_calculated > 0 ? prev_calculated - 1 : 0;

for(int i = start; i < rates_total; i++)
{
   buffer[i] = price[i];
}

Points

  • Recalculate only the immediately previous bar for safety
  • Process only new data

5.3 Why Subtract 1?

prev_calculated - 1

Reason:

  • The immediately previous bar may still be updated
  • If it is not recalculated, values can become misaligned

Examples

  • A bar updating during ticks
  • An unfinished candle

Conclusion

Recalculating from one bar back is the standard approach

5.4 Handling the begin Parameter

const int begin

Role

  • The calculation start position, depending on the indicator

At the beginner stage:

It is generally okay to ignore it

When it is used in practice

  • Integration with another indicator
  • Control of the calculation range

5.5 How to Write Practical Logic (Important)

Indicator logic becomes stable when structured as follows:

1. Get the required data
2. Check the condition
3. Assign to the buffer

Example: simple trend detection

for(int i = start; i < rates_total; i++)
{
   if(price[i] > price[i-1])
      buffer[i] = price[i];
   else
      buffer[i] = EMPTY_VALUE;
}

5.6 Safe Array Access (Required)

This is where beginners most often create errors.

Bad example

if(price[i] > price[i-1])

Problem:

  • It crashes at i=0 because i-1 does not exist

Safe writing style

for(int i = MathMax(start, 1); i < rates_total; i++)
{
   if(price[i] > price[i-1])
      buffer[i] = price[i];
}

Conclusion

Always perform boundary checks when referencing arrays

5.7 Common Mistakes (Important)

This chapter covers issues that appear often in real work.

1. Not using prev_calculated

→ Unnecessary recalculation

2. Wrong start position

→ Missing calculations or bugs

3. i=0 problem

→ array out of range

4. EMPTY_VALUE is not set

→ Unwanted drawing artifacts


5.8 Performance Design (Practical)

This is very important when using the indicator with an EA.

Examples of negative impact

  • High-frequency calculation → increased CPU load
  • VPS latency → delayed execution

Improvements

  • Use differential calculation consistently
  • Reduce unnecessary loops
  • Keep calculation load light

5.9 Practical Summary

OnCalculate is evaluated by the following criteria:

CriterionDescription
AccuracyWhether the calculation is correct
StabilityWhether errors are avoided
Lightweight designWhether unnecessary processing is avoided

6. Indicator Implementation Examples (Moving Average and Signal Addition)

In this section, you will use what you have learned to implement indicators that are actually usable.
The conclusion is that as long as you follow the pattern “calculation logic → buffer assignment,” you can apply it in many ways.


6.1 Implementing a Moving Average Indicator (Basic)

First, implement the most basic indicator: a Simple Moving Average (SMA).

Code example (SMA)

#property indicator_chart_window
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrBlue

input int period = 14;

double buffer[];

int OnInit()
{
   SetIndexBuffer(0, buffer);
   return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
   int start = prev_calculated > 0 ? prev_calculated - 1 : period;

   for(int i = start; i < rates_total; i++)
   {
      double sum = 0.0;

      for(int j = 0; j < period; j++)
      {
         sum += price[i - j];
      }

      buffer[i] = sum / period;
   }

   return(rates_total);
}

Point explanation

  • period: averaging period, entered by the user
  • i - j: reference to past data
  • Cannot calculate before period bars exist → start is adjusted because bars before the period cannot be calculated

Common mistakes

  • Calculating before enough bars exist for period → error
  • Nested loops are heavy → inefficient for long periods

6.2 Performance Improvement (Practical View)

The SMA above is easy to understand, but it is not efficient.

Improvement idea

Reuse the previous total → reduce to O(n)

However, beginners should first prioritize making it work correctly.


6.3 Signal Indicator (Arrow Display)

Next is an example that displays trading signals.

Conditions

  • Upward move: BUY signal
  • Downward move: SELL signal

Code example

#property indicator_chart_window
#property indicator_plots 1
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrGreen

double buffer[];

int OnInit()
{
   SetIndexBuffer(0, buffer);
   return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
   int start = prev_calculated > 0 ? prev_calculated - 1 : 1;

   for(int i = start; i < rates_total; i++)
   {
      if(price[i] > price[i-1])
         buffer[i] = price[i];
      else
         buffer[i] = EMPTY_VALUE;
   }

   return(rates_total);
}

6.4 Practical Arrow Display Settings (Important)

Arrows usually need visual adjustment.

Additional setting example

PlotIndexSetInteger(0, PLOT_ARROW, 233);

Notes

  • 233: up arrow in Wingdings
  • The value may differ depending on the environment

6.5 Combined Logic (Moving Average + Signal)

In real work, multiple conditions are combined.

Example: MA cross

if(ma_fast[i] > ma_slow[i] && ma_fast[i-1] <= ma_slow[i-1])
   buffer[i] = price[i];
else
   buffer[i] = EMPTY_VALUE;

Core idea

Condition is true → assign value → display

6.6 Beginner Traps (Important)

Errors increase sharply at this stage.

1. Array range mistakes

→ i-1 / i-period

2. EMPTY_VALUE is not set

→ Arrows appear on every bar

3. Conditions are vague

→ Unintended signals

4. Logic and display are mixed

→ Debugging becomes difficult


6.7 Practical Design Guidelines

Indicator design becomes stable when you follow these rules.

1. Separate the calculation logic
2. Use buffers only for output
3. Keep conditions clear

6.8 Design for Future EA Integration

This matters if you plan to use the indicator in an EA later.

Points

  • Fix buffer numbers
  • Make signal values clear
  • Disable invalid states with EMPTY_VALUE

Example

double signal = iCustom(Symbol(), Period(), "MyIndicator", 0, 0);

7. Common Errors and Troubleshooting (Cause → Fix)

This section organizes common problems in MQL5 indicator creation in a clear cause → fix format.
The conclusion is that most problems come down to three areas: Buffer, array range, and compilation.


7.1 Nothing Is Displayed (Most Common)

Symptom

  • Nothing appears after applying the indicator

Main causes

  • No value is assigned to the Buffer
  • SetIndexBuffer is not set
  • EMPTY_VALUE is used incorrectly
  • Drawing type is not set

Fix

SetIndexBuffer(0, buffer);
buffer[i] = price[i];

Checklist

  • Does the buffer contain real numeric values?
  • Is OnCalculate running?
  • Is indicator_plots set?

7.2 It Does Not Appear in Navigator

Symptom

  • The indicator does not appear in the MT5 indicator list

Main causes

  • It has not been compiled
  • There is a compile error
  • It is saved in the wrong location

Fix

  • Compile with F7
  • Check the error log
  • Confirm it is in the Indicators folder

7.3 array out of range Error

Symptom

  • Runtime crash or error

Main cause

price[i-1]
  • It is being referenced when i=0

Fix

for(int i = MathMax(start, 1); i < rates_total; i++)

Core idea

Always perform boundary checks for array access

7.4 indicator buffers Related Errors

Symptom

  • The display is broken or only partially shown

Main causes

  • The number of buffers and indicator_plots do not match
  • The SetIndexBuffer order is wrong

Fix

#property indicator_plots 2
SetIndexBuffer(0, buffer1);
SetIndexBuffer(1, buffer2);

7.5 Values Are Shifted or Delayed

Symptom

  • The display appears one step late

Main causes

  • prev_calculated is not considered
  • Differential calculation is wrong

Fix

int start = prev_calculated > 0 ? prev_calculated - 1 : 0;

7.6 Values Appear on Every Bar (Incorrect Display)

Symptom

  • Unintended lines or arrows appear in large numbers

Main cause

  • EMPTY_VALUE is not used

Fix

buffer[i] = EMPTY_VALUE;

7.7 Compile Errors (Basic)

Symptom

  • The code cannot be compiled

Main causes

  • Syntax error
  • Type mismatch
  • Missing semicolon

Fix

  • Always read the error message
  • Check the line number

7.8 Performance Problem (Heavy Processing)

Symptom

  • MT5 feels slow or nearly freezes

Main causes

  • Full recalculation loop
  • Unnecessary processing

Fix

int start = prev_calculated > 0 ? prev_calculated - 1 : 0;

7.9 Problems When Connecting with an EA

Symptom

  • Values cannot be retrieved with iCustom

Main causes

  • Wrong buffer number
  • EMPTY_VALUE is returned
  • Parameter mismatch

Fix

iCustom(Symbol(), Period(), "IndicatorName", ...);

Checkpoints

  • Confirm the buffer index
  • Match input parameters
  • Use the exact file name

7.10 Practical Strategy to Avoid Problems

In real work, follow this process carefully.

1. Build small, using the minimum structure
2. Check operation
3. Add features gradually

Bad approach

  • Writing complex logic all at once

Good approach

  • Validate step by step

7.11 Error Priority (Practical Judgment)

The priority order is:

  1. Crash, such as array out of range
  2. Nothing is displayed
  3. Value misalignment
  4. Performance

8. How to Connect with an EA (Basics of iCustom and CopyBuffer)

This section explains how to use your custom indicator from an EA.
The conclusion is that the real value of creating indicators in MQL5 is not only the display itself, but the ability to reuse them from an EA.
In real projects, separating the indicator as the analysis component and the EA as the execution component improves maintainability and reproducibility.


8.1 Basic Structure for Using an Indicator from an EA

In MQL5, when calling a custom indicator from an EA, the main flow is:

1. Load the indicator with iCustom
2. Get the handle
3. Retrieve values with CopyBuffer
4. Use the values in trading conditions

The important point is that you do not directly read the indicator itself; you read the Buffer values.


8.2 What Is iCustom?

iCustom is a function that calls a custom indicator from an EA or another MQL5 program.
Its return value is the indicator “handle.” A handle is like an identifier used to operate the target.

Basic form

int handle = iCustom(Symbol(), Period(), "MyIndicator");

Meaning

  • Symbol(): current currency pair
  • Period(): current timeframe
  • "MyIndicator": indicator name

Notes

  • The file name must be written accurately
  • If it is in a subfolder, the path must be included
  • It will not work unless it has been compiled

8.3 iCustom When input Parameters Exist

If your custom indicator has input parameters, pass them on the iCustom side in the same order.

Example: indicator side

input int period = 14;

EA side

int handle = iCustom(Symbol(), Period(), "MyIndicator", 14);

Important

  • If the order is wrong, the indicator may behave incorrectly
  • If the type is wrong, the value may not be passed correctly
  • If you add parameters later, the EA side also needs to be updated

This is a point where beginners often get stuck.
Remember this rule: the indicator’s input definitions and the EA-side iCustom arguments must match.


8.4 Getting Values with CopyBuffer

The handle returned by iCustom alone is not enough to use numeric values.
You get the actual Buffer values with CopyBuffer.

Basic form

double values[];

if(CopyBuffer(handle, 0, 0, 3, values) > 0)
{
   Print(values[0]);
}

Meaning of the arguments

  • handle: handle obtained with iCustom
  • 0: Buffer number
  • 0: start position for retrieval, the latest bar
  • 3: number of values to retrieve
  • values: receiving array

Practical meaning

  • values[0]: latest bar
  • values[1]: one bar ago
  • values[2]: two bars ago

8.5 How to Think About Buffer Numbers

For EA integration, you must make clear which Buffer contains which value.

Example

  • Buffer 0: main line
  • Buffer 1: signal line
  • Buffer 2: BUY signal
  • Buffer 3: SELL signal

EA side

double buy_signal[];
double sell_signal[];

CopyBuffer(handle, 2, 0, 1, buy_signal);
CopyBuffer(handle, 3, 0, 1, sell_signal);

Notes

  • If you change a Buffer number later, the EA breaks
  • Mixing display Buffers and integration Buffers makes management difficult

In real projects, it is best to fix the Buffer design at the beginning.


8.6 Basic Example of Signal Judgment

A design where the indicator side puts a value only on signal bars and uses EMPTY_VALUE otherwise works well with EA integration.

Indicator-side concept

if(buy_condition)
   buy_buffer[i] = Close[i];
else
   buy_buffer[i] = EMPTY_VALUE;

EA-side judgment example

double buy_signal[];

if(CopyBuffer(handle, 2, 0, 1, buy_signal) > 0)
{
   if(buy_signal[0] != EMPTY_VALUE)
   {
      Print("BUY signal");
   }
}

With this structure, the EA can judge the signal simply by checking
whether the value is not EMPTY_VALUE.


8.7 Common Mistakes and Notes

The following mistakes are very common in EA integration.

1. Wrong file name in iCustom

  • The extension is usually not needed
  • If the folder hierarchy is wrong, retrieval fails

2. Wrong Buffer number in CopyBuffer

  • You may read a different Buffer than intended
  • This can cause the chart display to look correct while the EA judgment is wrong

3. Mishandling the latest bar

  • values[0] may include the unfinished candle
  • If you want confirmed-bar logic, using values[1] may be effective

4. Poor understanding of EMPTY_VALUE

  • You may treat no signal as 0 by mistake
  • EMPTY_VALUE and numeric 0 are different

5. Not checking handle creation failure

  • Without an INVALID_HANDLE check, the cause can become hard to identify

8.8 How to Check Handle Creation Failure

Always validate the result of iCustom.

int handle = iCustom(Symbol(), Period(), "MyIndicator");

if(handle == INVALID_HANDLE)
{
   Print("indicator handle create failed");
   return;
}

Why this check is needed

  • File name mistake
  • Parameter mismatch
  • Initialization failure on the indicator side

If you do not add this check, it becomes difficult to identify the cause even when a later CopyBuffer call fails.


8.9 Practical Design Guidelines

If you create an indicator with EA integration in mind, the following guidelines are effective.

1. Fix Buffer numbers
2. Manage signal Buffers with EMPTY_VALUE
3. Clearly state whether to use confirmed or unfinished bars
4. Keep the input structure stable

Important for reproducibility

  • Keep chart display and trading judgment rules consistent
  • Do not change Buffer numbers later
  • Do not make signal definitions vague

This is not just a matter of coding style. It is a design choice that helps prevent differences between backtest results and live operation results.


8.10 Beginner-Friendly Conclusion

The minimum set beginners should remember is:

  • Call the indicator with iCustom
  • Read values with CopyBuffer
  • Judge whether a signal exists using EMPTY_VALUE

If you understand these three points, you have enough foundation to integrate your custom indicator into an EA.

9. Summary (How to Reach Practical Level Quickly)

Based on everything covered so far, this section organizes the core of MQL5 indicator creation and the shortest path to improvement.
The conclusion is that the important factor is not the amount of code, but structural understanding and reproducible design.


9.1 Core Nature of an Indicator (Redefined)

An MQL5 indicator works with the following structure:

Price data → calculate in OnCalculate → store in Buffer → draw automatically

If you understand this flow, you can create any type of indicator.

Core elements

  • OnCalculate: calculation engine
  • IndicatorBuffer: output
  • Drawing settings: appearance

9.2 Three Minimum Points to Remember (Important)

Beginners should first focus only on the following.

1. Values appear when you put them into the Buffer

  • Most display problems come from this point

2. OnCalculate controls everything

  • It is the center of calculation and updates

3. EMPTY_VALUE controls drawing

  • It is required for signal-type indicators

9.3 Thinking Required at a Practical Level

Simply making it run is not enough.
In real work, you need the following viewpoints.

1. Differential calculation for performance

Use prev_calculated → reduce unnecessary calculation

2. Array safety for stability

i-1 / i-period → always perform boundary checks

3. Buffer design for reusability

Fix the structure with EA integration in mind

9.4 Shortest Learning Route for Beginners

To improve efficiently, order matters.

Recommended steps

1. Create a minimal indicator
2. Confirm that it is displayed
3. Add conditional branches
4. Extend it to multiple Buffers
5. Connect it with an EA

Bad route

  • Writing complex logic from the beginning
  • Copying and pasting without understanding

9.5 Common Patterns Where Beginners Get Stuck

Many beginners stop at these points.

Pattern 1

“Nothing is displayed, and I do not know why.”

→ The cause is often an unset Buffer

Pattern 2

“I am afraid of errors, so I cannot move forward.”

→ Understanding array boundaries solves this

Pattern 3

“The logic is too complex.”

→ It should be divided into smaller parts


9.6 Most Important Practical Points

At a professional level, the following are evaluated.

ItemDescription
ReproducibilityWhether the same result can be reproduced
StabilityWhether errors are avoided
Lightweight designWhether unnecessary processing is avoided
ExtensibilityWhether it can connect with an EA

9.7 Long-Term Applications

Indicator creation can develop into the following areas:

  • Automated trading with EAs
  • Signal delivery
  • Analysis tools
  • Combined logic using AI or statistics

The especially important viewpoint is to treat indicators as reusable components.


9.8 Conclusion (Practical Summary)

Ultimately, the important points are:

1. Understand the structure
2. Build small
3. Ensure reproducibility

If you follow these three points, you can develop stable indicators.


9.9 What to Do Next (Practical Actions)

After reading this article, the next steps are clear.

  • Run the minimal code yourself
  • Add one piece of your own logic
  • Try retrieving the value from an EA

This turns your understanding from “knowledge” into “skill.”


FAQ

Q1. Why is my MQL5 indicator not displayed?

A. In many cases, the Buffer has not been registered or no value has been assigned to it. First check SetIndexBuffer and the assignment logic inside OnCalculate.


Q2. What does OnCalculate do?

A. OnCalculate receives price data, performs calculations, and stores the results in a Buffer. It is the core function of an indicator.


Q3. What is EMPTY_VALUE?

A. EMPTY_VALUE is a special value used when you do not want to draw anything on a bar. It is essential for signal displays.


Q4. Should I always use prev_calculated?

A. It is not strictly required, but in real projects you should use it for performance and stability. It reduces unnecessary recalculation.


Q5. How do I use an indicator from an EA?

A. Call the indicator with iCustom and retrieve its values with CopyBuffer. Those values can then be used in trading logic.


Q6. How can I prevent an array out of range error?

A. Always check index boundaries, especially when using references such as i-1 or i-period. Use safeguards such as MathMax where needed.


Q7. How do I display multiple lines?

A. Define multiple IndicatorBuffers and match them correctly with indicator_plots and SetIndexBuffer. Keep each Buffer number consistent.


Q8. What indicator should beginners create first?

A. Start with a simple indicator that displays the price as is. This makes it easier to understand the basic structure of OnCalculate and Buffer.