MQL5 iCustom Guide: Use Custom Indicators in Expert Advisors

1. What Is iCustom? Its Role in MQL5

iCustom is a MQL5 function used to call a custom indicator from an EA or script. In MetaTrader 5 (MT5), indicators are normally displayed on charts, but an EA can also read their calculated results. iCustom is the function used for that connection.

  • Use signals from your own indicator for EA trading decisions
  • Integrate distributed or purchased external indicators into an EA
  • Separate complex logic into the indicator side

iCustom does not directly return an indicator value. It returns an indicator handle, which is an ID used to access the indicator instance. Actual values are retrieved later with CopyBuffer().

  1. Get an indicator handle with iCustom
  2. Get calculated values from the indicator buffer with CopyBuffer()
MQL5 iCustom and CopyBuffer workflow diagram showing how an Expert Advisor retrieves custom indicator data via indicator handle initialization and buffer extraction, then evaluates trading signals using chart-based indicator values.

1.1 Overview of iCustom

MethodDetails
Standard indicator functionsiMA, iRSI, iMACD, and others
Custom indicatorsiCustom

Standard indicators have dedicated functions, but custom or external indicators must be called with iCustom.

MQL5/Indicators/MyIndicator.ex5
int handle;

handle = iCustom(
   _Symbol,
   PERIOD_CURRENT,
   "MyIndicator"
);

This loads MyIndicator for the current symbol and timeframe, then stores its handle in handle.

1.2 Typical Uses

Typical uses include separating signal generation from trade execution, integrating external indicators, and keeping complex logic reusable and visible on the chart.

Indicator
↓
Signal calculation
↓
EA
↓
Trade execution

1.3 Basics to Know First

Before using iCustom, understand indicator handles, indicator buffers, and CopyBuffer(). A handle identifies the indicator instance. Buffers store calculated values such as main lines, signal lines, histograms, or arrow signals.

CopyBuffer(handle, buffer_index, start_pos, count, array);

Common mistakes are treating iCustom as a value-returning function, calling it on every tick, and specifying the wrong indicator path.

2. Basic Syntax and Parameters of iCustom

iCustom loads a custom indicator and returns its handle. If loading fails, the return value is INVALID_HANDLE.

int iCustom(
   string           symbol,
   ENUM_TIMEFRAMES  period,
   string           name,
   ...
);

2.1 Basic Example

int handle;

handle = iCustom(
   _Symbol,
   PERIOD_CURRENT,
   "MyIndicator"
);
ArgumentDetails
_SymbolCurrent currency pair
PERIOD_CURRENTCurrent timeframe
"MyIndicator"Indicator file

"MyIndicator" points to MQL5/Indicators/MyIndicator.ex5. The .ex5 extension is normally not needed.

2.2 symbol

The first argument is the symbol. Use _Symbol for the current chart or a literal symbol such as "EURUSD".

2.3 period

The second argument is the timeframe, such as PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_D1, or PERIOD_CURRENT.

iCustom(_Symbol, PERIOD_H1, "MyIndicator");

2.4 name

The third argument is the indicator file name. If the indicator is in a subfolder, specify it like "MyFolder/MyIndicator".

2.5 Indicator Parameters

Pass indicator input values after the indicator name in the exact order they are defined.

input int Period = 14;
input double Multiplier = 2.0;
int handle = iCustom(
   _Symbol,
   PERIOD_CURRENT,
   "MyIndicator",
   14,
   2.0
);
int handle = iCustom(_Symbol, PERIOD_CURRENT, "MyIndicator");

if(handle == INVALID_HANDLE)
{
   Print("Indicator load failed");
}

3. How to Retrieve Indicator Values with CopyBuffer()

The handle alone does not contain indicator values. Use CopyBuffer() to retrieve actual calculated values from a buffer.

int CopyBuffer(
   int       indicator_handle,
   int       buffer_num,
   int       start_pos,
   int       count,
   double    buffer[]
);
ArgumentDetails
indicator_handleHandle from iCustom
buffer_numBuffer number
start_posStarting position
countNumber of values
buffer[]Array to store values
int handle;
double value[];

handle = iCustom(_Symbol, PERIOD_CURRENT, "MyIndicator");

ArraySetAsSeries(value,true);

CopyBuffer(handle,0,0,1,value);

double latest = value[0];

Buffer numbers depend on the indicator. For a MACD-style indicator, buffer 0 may be MACD, buffer 1 may be signal, and buffer 2 may be histogram. Always confirm the correct buffer.

CopyBuffer(handle,0,0,3,value);

With a series array, value[0] is the latest bar, value[1] is the previous bar, and value[2] is two bars ago.

int handle;
double buffer[];

int OnInit()
{
   handle = iCustom(_Symbol,PERIOD_CURRENT,"MyIndicator");

   if(handle == INVALID_HANDLE)
   {
      Print("Indicator load failed");
      return(INIT_FAILED);
   }

   ArraySetAsSeries(buffer,true);

   return(INIT_SUCCEEDED);
}

void OnTick()
{
   if(CopyBuffer(handle,0,0,2,buffer) <= 0)
      return;

   if(buffer[0] > buffer[1])
   {
      Print("Signal detected");
   }
}

Always set arrays with ArraySetAsSeries(buffer,true), verify the buffer number, check the return value, and allow for indicators that have not finished calculating yet.

4. Calling Indicators with Multiple Parameters

Many custom indicators define periods, thresholds, shifts, counts, or multipliers with input. In iCustom, values after the indicator name correspond to those input variables by order, not by name.

input int FastPeriod = 12;
input int SlowPeriod = 26;
input int SignalPeriod = 9;
int handle = iCustom(
   _Symbol,
   PERIOD_CURRENT,
   "MyIndicator",
   12,
   26,
   9
);

Match both order and type. This includes int, double, bool, string, and enumeration types such as ENUM_....

// Incorrect example
int handle = iCustom(_Symbol, PERIOD_CURRENT, "MyIndicator", true);

You generally cannot skip earlier parameters to set only a later parameter. Write all previous values in order.

input int InpPeriod = 14;
input double InpMultiplier = 2.0;
input bool InpUseFilter = true;

int handle;

int OnInit()
{
   handle = iCustom(
      _Symbol,
      PERIOD_CURRENT,
      "MyIndicator",
      InpPeriod,
      InpMultiplier,
      InpUseFilter
   );

   if(handle == INVALID_HANDLE)
   {
      Print("iCustom failed");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

This structure makes settings visible in the EA, easier to change during backtesting, and easier to compare with the indicator side.

5. How to Retrieve Indicator Buffers

The EA must know which buffer contains which value. iCustom does not explain the meaning of each buffer.

  • Buffer 0: main line
  • Buffer 1: signal line
  • Buffer 2: arrow signal
  • Buffer 3: auxiliary value
double MainBuffer[];
double SignalBuffer[];

int OnInit()
{
   SetIndexBuffer(0, MainBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, SignalBuffer, INDICATOR_DATA);
   return(INIT_SUCCEEDED);
}

When source code is unavailable, check documentation, chart behavior, logs, and test output. External indicators can be hard to verify completely.

double val[];
ArraySetAsSeries(val, true);

if(CopyBuffer(handle, 0, 0, 1, val) <= 0)
{
   Print("CopyBuffer failed");
   return;
}

double current_value = val[0];

For arrow or signal indicators, check EMPTY_VALUE. A value may exist only on signal bars.

double sig[];
ArraySetAsSeries(sig, true);

if(CopyBuffer(handle, 1, 0, 1, sig) > 0)
{
   if(sig[0] != EMPTY_VALUE)
   {
      Print("シグナルあり");
   }
}

6. Common iCustom Errors and Fixes

Troubleshoot in this order: check whether iCustom returned INVALID_HANDLE, verify the path and arguments, check BarsCalculated(handle), check CopyBuffer(), and read the log.

int handle = iCustom(_Symbol, PERIOD_CURRENT, "MyIndicator");

if(handle == INVALID_HANDLE)
{
   Print("iCustom failed");
   return(INIT_FAILED);
}

INVALID_HANDLE is commonly caused by a wrong file name, wrong subfolder path, mismatched parameter count, wrong input order, or invalid resource reference.

cannot load custom indicator often means the specified name and actual location do not match. Check that the file is under MQL5/Indicators, subfolders are included, the extension is omitted, and the compiled file exists.

double vals[];
ArraySetAsSeries(vals, true);

if(BarsCalculated(handle) < 2)
   return;

if(CopyBuffer(handle, 0, 0, 2, vals) <= 0)
   return;

array out of range usually happens when the EA reads more elements than were copied or reads after CopyBuffer() failed.

void OnDeinit(const int reason)
{
   if(handle != INVALID_HANDLE)
      IndicatorRelease(handle);
}

7. Important Notes When Using iCustom

For stable live operation, create handles in OnInit(), confirm calculation completion, release handles, and keep symbol/timeframe handling consistent.

int handle = INVALID_HANDLE;

int OnInit()
{
   handle = iCustom(_Symbol, PERIOD_CURRENT, "MyIndicator");

   if(handle == INVALID_HANDLE)
   {
      Print("iCustom failed");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

Do not call CopyBuffer() before calculation is complete. This is especially important immediately after startup, after switching symbols or timeframes, with multi-timeframe references, and when history is not loaded.

if(BarsCalculated(handle) <= 0)
   return;

External indicators may have drawing buffers, judgment buffers, and auxiliary buffers. The visible chart display may not match the buffer the EA should read.

int copied = CopyBuffer(handle, 0, 0, 2, vals);

if(copied <= 0)
{
   Print("CopyBuffer failed");
   return;
}

Print("val0=", vals[0], " val1=", vals[1]);

8. Advanced Use of iCustom in EA Design

iCustom is most useful as a connection point between signal calculation and order processing. This separation improves testing, reproducibility, and maintenance.

  • Custom indicator: calculates signals or judgment values
  • EA: reads values with iCustom and CopyBuffer(), then handles orders, exits, and money management
double buy_sig[];
ArraySetAsSeries(buy_sig, true);

if(CopyBuffer(handle, 0, 0, 1, buy_sig) > 0)
{
   if(buy_sig[0] != EMPTY_VALUE)
   {
      // 買いエントリー候補
   }
}

iCustom can also be used for filter indicators, such as trend direction, volatility thresholds, restricted trading hours, or abnormal-value checks.

int trend_handle;
int entry_handle;
int exit_handle;

Keep roles clear: entry, filter, and exit. Because automated trading involves market risk and environment-dependent behavior, verify the logic with backtesting, forward testing, and logs before live use.

9. FAQ: Common Questions About iCustom

9.1 What does iCustom return?

iCustom returns an indicator handle, not an indicator value. Use CopyBuffer() to retrieve actual values.

9.2 Where should iCustom be called?

Call it in OnInit(). Creating handles on every tick increases load and makes management unstable.

9.3 Why does CopyBuffer() fail?

Common causes include unfinished calculation, wrong buffer number, wrong array direction, insufficient history, or a failed handle.

9.4 How do you find the buffer number?

Check SetIndexBuffer(index, buffer) in the source code. If source is unavailable, use documentation, logs, and test output.

9.5 What is EMPTY_VALUE?

EMPTY_VALUE means no value exists for that bar. Many signal indicators use it on non-signal bars.

9.6 Can iCustom use another timeframe?

Yes. For example, iCustom(_Symbol, PERIOD_H1, "MyIndicator") references an H1 indicator, but update timing and history loading must be handled carefully.

9.7 Can iCustom be used with external indicators?

Yes, if the indicator is under MQL5/Indicators, parameters are passed in the correct order, and buffer numbers are understood.

9.8 Why does iCustom return INVALID_HANDLE?

It is usually caused by a wrong name, wrong path, mismatched input parameters, an uncompiled indicator, or an invalid resource specification.

if(handle == INVALID_HANDLE)
{
   Print("Indicator load failed");
}