MQL5 OnInit and OnDeinit: EA Lifecycle, Cleanup, and Stable VPS Operation

目次

1. What Are OnInit and OnDeinit in MQL5?

In MQL5, OnInit and OnDeinit are the most basic functions for controlling the lifecycle of an EA (Expert Advisor) or indicator, from startup to shutdown.
In short, their roles are as follows.

  • OnInit: Initialization logic that runs only once at startup
  • OnDeinit: Cleanup logic that runs when the program stops

If you do not understand these two functions correctly, the following problems can occur.

  • The indicator does not work correctly
  • Memory or handle resources are not released
  • Problems occur when a VPS restarts
  • The EA stops unexpectedly

In other words, this is one of the most important points directly tied to EA stability and reproducibility.


1.1 Role of OnInit (Initialization)

OnInit is a function that runs only once when an EA or indicator is attached to a chart.

It mainly handles the following tasks.

  • Creating indicator handles, such as iMA
  • Initializing arrays and variables
  • Checking input parameters
  • Setting the initial state

Basic Structure (Minimal Example)

int OnInit()
{
   Print("Initialization started");

   return(INIT_SUCCEEDED);
}

Important Points Beginners Often Miss

  • Always return a value with the int type
  • On success: INIT_SUCCEEDED
  • On failure: INIT_FAILED

Common Mistakes

  • Not writing a return value, which can cause a compile error or unstable behavior
  • Returning INIT_SUCCEEDED even when an error occurs
  • Allowing OnTick to run even though initialization has failed

In real projects, the basic rule is: if initialization fails, stop immediately.


1.2 Role of OnDeinit (Cleanup)

OnDeinit is a function that runs when an EA or indicator is stopped.

It is mainly called in the following situations.

  • When the EA is removed from the chart
  • When MT5 is closed
  • When the program is recompiled
  • When parameters are changed
  • When the chart is changed, such as the symbol or timeframe

Basic Structure

void OnDeinit(const int reason)
{
   Print("Cleanup: reason=", reason);
}

Role

  • Release indicator handles
  • Delete objects such as lines and text
  • Output logs

Common Mistakes

  • Leaving it empty, which can cause resource leaks and unreleased memory
  • Not releasing handles, which can cause issues during long-running operation
  • Ignoring reason, which makes debugging difficult

Especially in VPS operation, the quality of cleanup logic has a major effect on stability.


1.3 Difference from OnTick (A Common Beginner Confusion)

The point that confuses beginners most often is the difference from OnTick.

FunctionExecution TimingPurpose
OnInitOnce at startupInitialization
OnTickEvery tickTrading logic
OnDeinitAt shutdownCleanup

Mental Model (Important)

Startup -> OnInit (once)
      ↓
Market running -> OnTick (thousands of times)
      ↓
Stop -> OnDeinit (once)

Common Misunderstandings

  • Writing trade execution logic in OnInit: not recommended
  • Creating indicators every time in OnTick: inefficient and likely to cause bugs
  • Not using OnDeinit: long-term operation can break down

The correct design is:
Prepare in OnInit -> process in OnTick -> clean up in OnDeinit

MQL5 OnInit OnTick OnDeinit lifecycle diagram showing indicator handle initialization, tick-based execution, and resource release in MetaTrader 5 Expert Advisor system architecture

Key Takeaways from This Section

  • OnInit: initialization, once
  • OnDeinit: cleanup, once
  • OnTick: main processing, every tick
  • Separating these three roles creates a stable EA

2. How to Use OnInit and Its Basic Syntax

OnInit is the initialization function that runs only once when an EA starts.
A design mistake here directly leads to EA bugs, incorrect behavior, or startup failure.

This section explains OnInit in a practical form you can use in real projects.


2.1 Basic Syntax (Sample Code)

The basic syntax of OnInit is as follows.

int OnInit()
{
   // Initialization logic

   return(INIT_SUCCEEDED);
}

Meaning of the Return Value (Important)

Return ValueMeaning
INIT_SUCCEEDEDInitialization completed successfully -> the EA runs
INIT_FAILEDInitialization failed -> the EA stops

Important Practical Points

  • You can control whether the EA starts by using the return value
  • Always return INIT_FAILED when an error occurs
  • This prevents an EA from running in a broken state

Bad Example (Common Mistake)

int OnInit()
{
   // Even though initialization failed...
   return(INIT_SUCCEEDED);
}

This is one of the most dangerous bugs
because it can cause behavior that is hard to diagnose.


2.2 Specific Examples of Initialization Tasks

OnInit is for preparation.
Its tasks can mainly be grouped into the following three categories.


1. Creating Indicator Handles

int ma_handle;

int OnInit()
{
   ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

   if(ma_handle == INVALID_HANDLE)
   {
      Print("Failed to get iMA handle");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

Points

  • Functions such as iMA return a handle, which is an identifier
  • On failure, the result is INVALID_HANDLE
  • Always check it

2. Initializing Arrays and Variables

double buffer[];

int OnInit()
{
   ArraySetAsSeries(buffer, true); // Make it a time series array
   ArrayResize(buffer, 100);       // Allocate size

   return(INIT_SUCCEEDED);
}

Points

  • Uninitialized arrays can cause bugs
  • Be aware of time series arrays, where the newest value is index 0

3. Validating Input Parameters

input int period = 20;

int OnInit()
{
   if(period <= 0)
   {
      Print("period must be 1 or greater");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

Points

  • Stop immediately when parameters are invalid
  • Prevent an EA from running with abnormal values

2.3 Common Mistakes (Important)

OnInit can easily become a source of bugs.
The following issues are especially common.


1. Ignoring Handle Acquisition Failure

ma_handle = iMA(...);
// No check -> crash later

Result: an error occurs in CopyBuffer.


2. Not Outputting Print Logs

// The cause of the error is unknown

At minimum, add this.

Print("OnInit started");

3. Making Initialization Too Heavy

  • Large loops
  • Heavy calculations
  • External processing

Result

  • Delayed startup
  • Instability in VPS environments

Countermeasure

  • Move heavy processing across OnTick where appropriate

4. Not Using INIT_FAILED

This is the most common mistake.

// Letting the EA run even after failure

Correct approach

if(error)
{
   return(INIT_FAILED);
}

2.4 Practical Template (Ready to Use)

int ma_handle;

int OnInit()
{
   Print("OnInit started");

   // Create handle
   ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

   if(ma_handle == INVALID_HANDLE)
   {
      Print("Error: failed to get iMA handle");
      return(INIT_FAILED);
   }

   Print("OnInit succeeded");
   return(INIT_SUCCEEDED);
}

This is the minimum safe design pattern.


Key Takeaways from This Section

  • OnInit is one-time preparation logic at startup
  • The return value determines whether the EA lives or stops
  • Handle acquisition and parameter checks are required
  • Always return INIT_FAILED on error

3. How to Use OnDeinit and Practice Cleanup Logic

OnDeinit is the function that runs at the moment an EA or indicator stops.
It is often underestimated, but in real projects it directly affects stable operation, restart resilience, and bug prevention.

In short, OnDeinit has the following three roles.

  • Release used resources, such as memory and handles
  • Delete chart objects
  • Log the stop reason for debugging

3.1 Basic Syntax (Understand the Meaning of reason)

void OnDeinit(const int reason)
{
   Print("OnDeinit executed: reason=", reason);
}

What Is reason?

It is a code that indicates why the program stopped.

ConstantDescription
REASON_REMOVERemoved from the chart
REASON_RECOMPILERecompiled
REASON_CHARTCHANGESymbol or timeframe changed
REASON_PARAMETERSParameters changed
REASON_ACCOUNTAccount changed
REASON_CLOSEMT5 closed

Practical Points

  • Logging reason helps identify the cause of unexpected shutdowns
  • This is especially important in VPS operation, where restarts and disconnections must be tracked

3.2 Releasing Handles (Most Important)

Handles created in OnInit must be released in OnDeinit.

Example: Indicator Handle

void OnDeinit(const int reason)
{
   if(ma_handle != INVALID_HANDLE)
   {
      IndicatorRelease(ma_handle);
      Print("iMA handle released");
   }
}

Why This Is Necessary

  • If a handle is not released, it remains in memory
  • It can cause bugs or crashes during long-running operation
  • The impact is especially noticeable in VPS environments

This is especially important in environments where the EA is restarted repeatedly.


3.3 Deleting Chart Objects

If the EA displays lines or text, those objects remain unless you delete them.

Example: Deleting Objects

void OnDeinit(const int reason)
{
   ObjectsDeleteAll(0, 0, -1);
   Print("Objects deleted");
}

Common Problems

  • Unwanted objects remain on the chart
  • Displays are duplicated after restart
  • Visual bugs occur

The rule is simple: always clean up display objects.


3.4 Common Mistakes in Real Projects


1. Leaving OnDeinit Empty

void OnDeinit(const int reason)
{
   // Do nothing
}

Problems

  • Memory leaks
  • Remaining objects
  • No useful debugging information

2. Not Releasing Handles

Result

  • CopyBuffer errors increase
  • Long-term operation becomes unstable

3. Ignoring reason

Problems

  • You cannot tell why the EA stopped
  • You cannot trace VPS-related problems

4. Deleting Everything Every Time

ObjectsDeleteAll(0, 0, -1);

Caution

  • This may also delete objects created by other indicators

Countermeasures

  • Manage objects with a name prefix
  • Delete only the objects created by your own EA

3.5 Practical Template (Safe Design)

void OnDeinit(const int reason)
{
   Print("OnDeinit started reason=", reason);

   // Release handle
   if(ma_handle != INVALID_HANDLE)
   {
      IndicatorRelease(ma_handle);
      Print("Handle released");
   }

   // Delete objects if needed
   // ObjectsDeleteAll(0, 0, -1);

   Print("OnDeinit finished");
}

This structure provides the minimum level of stability.


3.6 Importance in VPS Operation (Practical View)

OnDeinit is especially important in the following cases.

Cases

  • VPS restart
  • MT5 crash
  • Network disconnection followed by reconnection

Problems That Can Occur

  • Resources are not released
  • State becomes inconsistent
  • Abnormal behavior occurs when the EA restarts

Countermeasures

  • Design OnInit and OnDeinit as a pair
  • Assume the state must be reset

Key Takeaways from This Section

  • OnDeinit is cleanup processing at shutdown
  • Handle release is required and is the most important point
  • Use reason to understand why the EA stopped
  • If ignored, long-term operation can break down

4. Practical Design Combining OnInit and OnDeinit

Understanding OnInit and OnDeinit separately is not enough.
In real projects, a stable EA is created only when they are designed as a pair.

In short, the basic design is as follows.

  • OnInit: secure resources and build the initial state
  • OnTick: execute logic
  • OnDeinit: release resources and reset state

By separating these three points, reproducibility, stability, and maintainability improve significantly.


4.1 Basic Design Pattern (Template)

First, here is a structure you can use directly in real projects.

int ma_handle;

// Initialization
int OnInit()
{
   Print("OnInit started");

   ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

   if(ma_handle == INVALID_HANDLE)
   {
      Print("Error: failed to get handle");
      return(INIT_FAILED);
   }

   Print("OnInit succeeded");
   return(INIT_SUCCEEDED);
}

// Main processing
void OnTick()
{
   double buffer[];

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

   double ma = buffer[0];

   // Trading logic example
   if(ma > Close[0])
   {
      // Sell condition, etc.
   }
}

// Cleanup
void OnDeinit(const int reason)
{
   Print("OnDeinit started reason=", reason);

   if(ma_handle != INVALID_HANDLE)
   {
      IndicatorRelease(ma_handle);
   }

   Print("OnDeinit finished");
}

4.2 Why This Design Matters (Structural Understanding)

This design is not just a formality. It is risk management itself.

Bad Design (Common)

void OnTick()
{
   int handle = iMA(...); // Created every time
}

Problems

  • Handles are created endlessly
  • Memory pressure increases
  • Operation becomes unstable

Good Design

  • Create once in OnInit
  • Only use it in OnTick
  • Release it in OnDeinit

Result

  • Lower load
  • Better reproducibility
  • Fewer bugs

4.3 How to Think About State Management (Important)

In EA design, you must think about state.

What Is State?

  • Handles
  • Arrays
  • Position information
  • Flag variables

Principle

  • OnInit creates the state
  • OnTick uses the state
  • OnDeinit destroys the state

Example: Flag Management

bool is_initialized = false;

int OnInit()
{
   is_initialized = true;
   return(INIT_SUCCEEDED);
}

void OnTick()
{
   if(!is_initialized) return;
}

This prevents incorrect behavior before initialization is complete.


4.4 Common Design Mistakes in Real Projects


1. Writing Initialization in OnTick

Problem

  • It runs every tick, which is wasteful, heavy, and bug-prone

2. Not Considering OnDeinit

Problem

  • State breaks after restart
  • Abnormal behavior occurs on a VPS

3. Overusing Global Variables

Problem

  • The state becomes unclear
  • Bugs become difficult to trace

4. Not Considering Reinitialization Cases

OnInit is executed again in the following cases.

  • Parameter changes
  • Timeframe changes
  • Recompilation

In other words

  • Design as if initialization can happen any number of times

4.5 Important Practical Design Guidelines (Reproducibility First)

1. Initialization Failure Means Immediate Stop

Do not run in an invalid state.


2. Manage Resources in Pairs

  • Create -> release
  • Allocate -> release

3. Reduce Side Effects

  • Keep OnTick as close to pure processing as possible

4. Design for VPS Operation

  • Restart
  • Disconnection
  • Reconnection

Build a structure that can handle all of them.


4.6 The Core Idea (Advanced View)

OnInit and OnDeinit are not just functions.

They are the lifecycle management of the EA itself.

Whether you understand this changes the following significantly.

  • Bug rate
  • Reproducibility
  • Operational stability

Key Takeaways from This Section

  • OnInit, OnTick, and OnDeinit must be designed separately
  • Manage resources through creation and release
  • Pay attention to state management
  • Design with reinitialization in mind

5. Beginner Pitfalls and How to Fix Them

The syntax of OnInit and OnDeinit is simple, but in real projects, problems such as “it does not run” or “the bug has no clear cause” happen often.
This section organizes the issues beginners and intermediate developers actually face by cause, impact, and solution.


5.1 Not Realizing That OnInit Has Failed

Symptoms

  • The EA does not run, and OnTick is not executed
  • It looks like no error has occurred

Cause

  • OnInit returns INIT_FAILED
  • Or an internal error occurs

Typical Example

int OnInit()
{
   int handle = iMA(...);

   if(handle == INVALID_HANDLE)
   {
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

Handle acquisition fails, so the EA does not start.


Solution

Print("OnInit started");
Print("handle=", handle);

Always output logs.


Practical Point

  • When the EA does not run, suspect OnInit failure first
  • Always check the Experts log

5.2 Not Knowing That OnDeinit Is Being Called

Symptoms

  • The EA resets by itself
  • State disappears
  • Settings are not applied

Cause

OnDeinit occurs after the following operations.

  • Timeframe change
  • Symbol change
  • Parameter change
  • Recompilation

Solution

void OnDeinit(const int reason)
{
   Print("OnDeinit reason=", reason);
}

This makes it visible when and why the EA stopped.


Practical Point

Design the EA as if it can be restarted any number of times.


5.3 Not Knowing the Cause of CopyBuffer Errors

Symptoms

  • CopyBuffer fails
  • Values cannot be retrieved

Cause (This Is the Cause in Most Cases)

  • Handle acquisition failed in OnInit
  • The handle is invalid, meaning INVALID_HANDLE

Bad Example

CopyBuffer(ma_handle, 0, 0, 1, buffer);

This runs even when ma_handle is invalid.


Solution

if(ma_handle == INVALID_HANDLE)
{
   Print("Invalid handle");
   return;
}

Practical Point

When CopyBuffer errors occur, check OnInit first.


5.4 Problems Caused by Forgetting Resource Release

Symptoms

  • Operation becomes unstable over time
  • The EA becomes heavy
  • MT5 crashes

Cause

  • IndicatorRelease is not called
  • Handles keep increasing

Solution

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

Practical Point

This is almost required for VPS operation.


5.5 Writing Initialization Logic in OnTick

Symptoms

  • Operation is slow
  • Behavior is unstable
  • Bugs are hard to understand

Bad Example

void OnTick()
{
   int handle = iMA(...);
}

Problem

  • A handle is created every tick
  • Memory pressure increases
  • The design is inefficient

Solution

Move it to OnInit.


Practical Point

Separate initialization into OnInit and processing into OnTick.


5.6 Ignoring reason and Making Debugging Impossible

Symptoms

  • You do not know why the EA stopped
  • You cannot trace VPS problems

Solution

Print("Stop reason=", reason);

Practical Point

  • REASON_RECOMPILE -> recompilation
  • REASON_CHARTCHANGE -> chart change

If you check the log, you can identify the cause.


5.7 Design That Does Not Consider Reinitialization

Symptoms

  • The EA breaks after parameter changes
  • The EA does not run after restart

Cause

  • The initialization assumption is broken
  • State remains unexpectedly

Solution

  • Always return to the initial state in OnInit
  • Completely reset in OnDeinit

Practical Point

Design as if startup and shutdown can happen any number of times.


Key Takeaways from This Section

  • When the EA does not run, suspect OnInit first
  • CopyBuffer errors are often handle problems
  • OnDeinit is called frequently
  • If initialization and processing are not separated, the design breaks down
  • Design with VPS operation in mind

6. Complete Practical Template and Design Summary

Based on the points above, here is a safe design template you can use directly in real projects.
The goals are as follows.

  • Prevent initialization mistakes
  • Keep the EA stable during long-term VPS operation
  • Create a structure that can be debugged

6.1 Complete Template (Ready to Use)

int ma_handle = INVALID_HANDLE;

// Initialization
int OnInit()
{
   Print("OnInit started");

   // Create indicator handle
   ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

   if(ma_handle == INVALID_HANDLE)
   {
      Print("Error: failed to get iMA handle");
      return(INIT_FAILED);
   }

   Print("OnInit succeeded");
   return(INIT_SUCCEEDED);
}

// Main processing
void OnTick()
{
   // Handle check
   if(ma_handle == INVALID_HANDLE)
   {
      Print("Invalid handle");
      return;
   }

   double buffer[];

   // Get data
   if(CopyBuffer(ma_handle, 0, 0, 1, buffer) <= 0)
   {
      Print("CopyBuffer failed");
      return;
   }

   double ma = buffer[0];

   // Sample logic
   if(ma > Close[0])
   {
      // Sell condition example
   }
}

// Cleanup
void OnDeinit(const int reason)
{
   Print("OnDeinit started reason=", reason);

   // Release handle
   if(ma_handle != INVALID_HANDLE)
   {
      IndicatorRelease(ma_handle);
      ma_handle = INVALID_HANDLE;
   }

   Print("OnDeinit finished");
}

6.2 Design Intent of This Template (Important)

1. Centralized State Management

int ma_handle = INVALID_HANDLE;

Manage state globally.
Make the invalid state explicit with INVALID_HANDLE.


2. Stop When Initialization Fails

return(INIT_FAILED);

Prevent operation in an invalid state.
Protect reproducibility.


3. Defensive Code in OnTick

if(ma_handle == INVALID_HANDLE)
{
   return;
}

Stop safely even in an unexpected state.


4. Release and Reset

IndicatorRelease(ma_handle);
ma_handle = INVALID_HANDLE;

Prevent double release.
Support reinitialization.


6.3 Design Checklist for Real Projects

When you build an EA, check whether it satisfies the following points.


Initialization (OnInit)

  • Handle acquisition is checked
  • Parameters are validated
  • INIT_FAILED is returned on error

Execution (OnTick)

  • Initialization state is checked
  • The return value of CopyBuffer is checked
  • Unnecessary initialization is not performed

Shutdown (OnDeinit)

  • Handles are released
  • reason is output to the log
  • State is reset

6.4 Common Design Failure Patterns


Pattern 1: It Runs but Is Unstable

Cause

  • Insufficient error checks
  • Handles are not released

Pattern 2: It Breaks After Restart

Cause

  • The design leaves state behind
  • OnDeinit is not handled

Pattern 3: It Stops for an Unknown Reason

Cause

  • reason is not logged
  • OnInit failure is overlooked

6.5 Best Design for Long-Term VPS Operation

Required Conditions

  • Restart resilience, assuming OnInit can run again
  • Memory management, with required release logic
  • Log output, so causes can be traced

Practical Evaluation Criteria

  • Does it stay stable for more than three consecutive days?
  • Does it behave the same way after restart, meaning reproducibility?
  • Can you identify the cause when an error occurs?

6.6 Core Summary (Advanced View)

OnInit and OnDeinit are not just functions.

They are EA lifecycle control.

If this design is wrong, the following can happen.

  • The EA works in backtests but breaks in live operation
  • It becomes unstable on a VPS
  • Unknown bugs occur

When this part is designed correctly, the following improve.

  • Stability becomes stronger
  • Debugging becomes easier
  • The EA can withstand real operation

Key Takeaways from This Section

  • Use the template as the base for design
  • Separate initialization, execution, and shutdown
  • Be strict about state management and resource management
  • Design with VPS operation in mind

7. FAQ

Q1. Can OnInit fail to execute?

A. In normal operation, OnInit is executed.
However, if INIT_FAILED is returned inside OnInit, OnTick will not run afterward.
In practice, when an EA does not run, first suspect that OnInit has failed.


Q2. When is OnDeinit called?

A. It is called when the EA stops or changes.
Common cases include the following.

  • EA removal
  • MT5 shutdown
  • Recompilation
  • Parameter changes
  • Chart changes

It is called more often than many developers expect, so ignoring it is risky.


Q3. Is it okay to run heavy processing in OnInit?

A. In general, it is not recommended.
Reasons:

  • Startup becomes slow
  • It may become unstable in a VPS environment

It is safer to distribute heavy processing to OnTick or other appropriate logic.


Q4. Will the EA still work if OnDeinit does nothing?

A. It may work in the short term, but it can become a long-term problem.

  • Memory leaks
  • Increasing handles
  • Instability

Especially for VPS operation, OnDeinit should be implemented.


Q5. What is the difference between OnInit and OnTick?

A. Their execution frequency and roles are completely different.

  • OnInit: once, for initialization
  • OnTick: every tick, for logic processing

Writing too much processing in OnInit or initializing resources in OnTick is a design mistake.


Q6. What causes CopyBuffer to fail?

A. In many cases, the cause is an OnInit failure.

  • The handle is INVALID_HANDLE
  • Initialization is incomplete

The shortest path is to review OnInit first when CopyBuffer errors occur.


Q7. Should I always use the reason parameter in OnDeinit?

A. In real projects, it is almost required.

Reasons:

  • You can identify why the EA stopped
  • It helps with VPS troubleshooting

Even logging it is valuable.


Q8. Can OnInit run multiple times?

A. Yes, it can.

It is executed again in the following cases.

  • Parameter changes
  • Timeframe changes
  • Recompilation

It is risky to assume it only happens once.
You must design with reinitialization in mind.


FAQ Summary (Practical Judgment)

  • Not running -> check OnInit
  • Unstable -> check OnDeinit
  • CopyBuffer error -> check the handle
  • VPS operation -> lifecycle design is required