1.
✅ General Template (You can copy-paste into any AI prompt):
"Can you explain this code in detail like I don’t have experience in
programing? I want to understand each function step by step,
including the logic, math, and how it works overall. Please also
highlight which parts of the code contain useful values or signals that
I can extract and use in a trading bot. Use simple language, real-world
analogies, and examples if possible."
What the code is for (if you know):
o "It is an indicator that I want to connect with a trading bot."
What you care about most:
o "Focus more on the logic and flow."
o "Explain the math in the functions."
o “Explain which part contain data that I can extract and connect with my
trading bot”
// Set up buffers
SetIndexBuffer(0, short_buffer, INDICATOR_DATA);
SetIndexBuffer(1, inter_buffer, INDICATOR_DATA);
SetIndexBuffer(2, last_inter_swing_buffer, INDICATOR_DATA);
SetIndexBuffer(3, short_swing_price, INDICATOR_CALCULATIONS);
SetIndexBuffer(4, short_swing_bar, INDICATOR_CALCULATIONS);
SetIndexBuffer(5, inter_swing_price, INDICATOR_CALCULATIONS);
SetIndexBuffer(6, inter_swing_bar, INDICATOR_CALCULATIONS);
Feature Array Buffer (in indicators)
A variable that stores multiple A special array used to display values
✅ What it is
values (like a list). on the chart.
Store any kind of data Store data that will be plotted (like
✅ Purpose
(calculation, flags, etc.) lines, histograms).
✅ Shown on ✅ Yes – if connected with
❌ No
Chart? SetIndexBuffer()
Show Moving Average, RSI,
✅ Example Use Keep track of signal strength
Histogram
So, does OnCalculate() run every tick?
Yes, by default, OnCalculate() is called on every new tick.
That means everything inside OnCalculate() runs every tick — unless you
add logic to control it.
When does OnCalculate() complete?
Your OnCalculate() function runs once per indicator update, which usually
happens:
o On every new tick (price update),
o Or when you scroll the chart or reload it,
o Or when some event triggers indicator recalculation.
Inside OnCalculate(), the entire for-loop runs from start to finish for all bars
you programmed it for (e.g., from i = 0 to rates_total - 1).
Only after the entire loop finishes, the OnCalculate() function completes and
returns.
🧠 Final Thought
INDICATOR_CALCULATIONS = Behind the scenes data
INDICATOR_DATA = Draw this line
INDICATOR_COLOR_INDEX = Use this for coloring bars or lines
🧠 BUFFER:
In indicators, buffers must be double[], and each one must be linked properly to
a drawing style (e.g., value, color, etc.). If you accidentally use the wrong type,
MT5 might not show any lines or cause errors.
This Print() line gives you a sanity check while coding.
Why would you get an INVALID_HANDLE?
You passed wrong or unsupported parameters to iMA().
There was a temporary resource issue in MT5.
You asked for data on a symbol or timeframe that does not exist.
You tried to use iMA() before the market data was fully loaded.
How it works practically:
You get the handle using:
mql5
CopiarEditar
fastMAHandle = iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA,
PRICE_CLOSE);
This tells MT5: "Prepare to calculate the SMA with these settings."
MT5 does the actual math internally and continuously updates the MA
values as new data (candles) arrive.
You then use that handle to request the current MA values, usually in
OnCalculate() using something like CopyBuffer():
mql5
CopiarEditar
CopyBuffer(fastMAHandle, 0, 0, rates_total, fastMA);
This copies the calculated MA values from MT5’s internal memory into your fastMA[]
array.
🔁 Step-by-Step:
1. You create the handle:
mql5
CopiarEditar
fastMAHandle = iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
This tells MT5:
“Please create a calculator for the Fast Moving Average using these settings.”
Now fastMAHandle is just a reference to that internal calculator — but it doesn’t contain
the actual values yet.
2. You copy the actual values into fastMA[]:
This usually happens inside the OnCalculate() function:
mql5
CopiarEditar
CopyBuffer(fastMAHandle, 0, 0, rates_total, fastMA);
Let’s break this line down:
Part Meaning
CopyBuffer(...) Function that copies data from an indicator (like iMA)
fastMAHandle The handle (ID) of the MA you created earlier
The buffer index (always 0 for iMA — because iMA only has
0 (2nd param)
1 output)
0 (3rd param) Start from the current (latest) candle
rates_total (4th How many values to copy (usually all visible candles)
Part Meaning
param)
fastMA (last param) Where to store the copied values (your custom array)
📦 So yes:
After this line runs, fastMA[] contains real moving average values from MT5.
You can use fastMA[i] like any other array — for coloring, signals, crossovers,
etc.
✅ Summary:
Variable Role
fastMAHandle A "remote control" to access MT5’s internal MA logic
CopyBuffer() Grabs the MA values using that remote
fastMA[] Your local array where the MA values are stored
Start
▼
[OnInit() runs]
├──► Declare buffers (fastMA, fastColorBuffer)
├──► SetIndexBuffer() → Tell MT5 what buffers do
├──► Create handle with iMA()
│ └── fastMAHandle = iMA(...)
└──► Check if handle is valid
└── if INVALID_HANDLE → Stop
▼
[OnCalculate() runs every tick]
├──► CopyBuffer() pulls MA values from MT5 engine
│ └── fastMA[] is now filled with data
├──► Run loop: check signals, assign colors
│ └── Update fastColorBuffer[]
└──► MT5 uses buffers to plot lines with colors
End
🔁 When Is OnInit() Called?
Event OnInit() Runs?
First time indicator is loaded ✅ Yes
User changes parameters ✅ Yes
Chart timeframe is changed ✅ Yes
Chart symbol is changed ✅ Yes
Terminal restarts ✅ Yes
Each new tick ❌ No (that's OnCalculate()'s job)
🔚 What is OnDeinit()?
OnDeinit() is a special function that runs once when your indicator or expert advisor is
removed, or the platform/chart is closed, or the indicator is reloaded.
Think of it as a cleanup function—used to release resources or perform final tasks
before your code exits.
🧼 What does OnDeinit() do?
You don’t have to use it, but it’s useful if your indicator:
1. Creates indicator handles (like iMA, iRSI)
→ You should release them with IndicatorRelease().
2. Opens files
→ Close them properly to avoid file lock or corruption.
3. Uses objects or graphical elements
→ Delete or clear them if needed.
4. Needs to reset settings
→ Sometimes useful to remove or reset chart elements.
📌 Tips
If you don’t allocate any resources (handles, files, objects), you may not need
OnDeinit().
It’s a best practice to clean up when using iMA, iRSI, etc., to prevent memory
leaks.
Great question!
In this function definition:
mql5
CopiarEditar
int OnCalculate(const int rates_total,
const int prev_calculated,
...
✅ const int vs int
Both are int types, but the keyword const means "constant", or "read-only" inside the
function.
🔒 const means: this value cannot be changed in the function.
So:
mql5
CopiarEditar
const int rates_total
Means: inside OnCalculate(), the variable rates_total is given by MetaTrader and
you are not allowed to modify it.
If you try to write:
mql5
CopiarEditar
rates_total = 100; // ❌ Compiler will show error
It will fail, because it's a const.
🤔 Why use const?
1. Protect input values — so the function doesn’t accidentally change important
data.
2. Improve code safety and clarity — makes it clear that rates_total and
prev_calculated are input-only.
3. Better optimization — the compiler can optimize more easily.
⚙️Why not just use int?
You could, but:
It would allow you to accidentally modify rates_total.
It makes the code slightly more dangerous and harder to read.
MQL5 and other modern programming languages encourage const to avoid bugs.
TL;DR
const int = an integer you cannot change inside the function.
It’s safer and clearer than just int.
Let me know if you want a simple demo showing what happens when you try to change a
const variable!
What is static?
Normally, when you declare a variable inside a function, it is created anew every time
the function runs, and destroyed when the function ends.
Example:
mql5
CopiarEditar
void MyFunction() {
int x = 0; // This x is new every call
x++;
Print(x);
}
Every time you call MyFunction(), x will reset to 0 and then become 1.
But with static:
A static variable remembers its value between function calls.
Example:
mql5
CopiarEditar
void MyFunction() {
static int x = 0; // Created only once
x++;
Print(x);
}
The first time you call MyFunction(), x is 0, then increments to 1 and prints 1.
The second time you call MyFunction(), x still holds 1, increments to 2, and
prints 2.
And so on...
If the values you're getting from iCustom(...) are unrealistically large, such as
2147483647, 1e+308, or similar outliers, it usually means something went wrong.
Here's a clear list of the most common causes:
🔴 Common Reasons Why iCustom() Returns "Big"/Wrong
Values
1. ❌ Invalid Buffer Index
You're trying to access a buffer (like index 2) that was not set using
SetIndexBuffer(...).
Symptom: iCustom(...) returns garbage values or extreme numbers like
1e+308.
2. ⚠️Incorrect shift (bar index)
Using a shift value that’s out of bounds (e.g., asking for bar 0 when the indicator
hasn't calculated that far yet).
Fix: Ensure OnCalculate() calculates up to rates_total - 1.
3. ⚠️Not Enough History Loaded
On a fresh chart, not enough candles have loaded for the custom indicator to
initialize properly.
Symptom: Values appear as EMPTY_VALUE, NaN, or extreme.
4. ❌ Incorrect Parameters in iCustom() Call
Passing the wrong number or order of parameters (iCustom(NULL, 0,
"MyIndi", ..., buffer_index, shift)).
Symptom: Indicator fails silently or returns junk values.
5. ⚠️Your Indicator Buffers Were Not Filled
You forgot to assign values like buffer[i] = ... inside the loop.
Fix: Ensure your indicator writes to buffer[i] for every i.
6. ❌ Your Indicator Returned EMPTY_VALUE
You explicitly assigned EMPTY_VALUE to a buffer index (e.g., when skipping
invalid candles).
EMPTY_VALUE = DBL_MAX = 1.7976931348623158e+308 → Looks huge!
7. ⚠️Time Mismatch Between Chart and Indicator
If your chart is on one timeframe (e.g., M1), and your indicator logic assumes
another timeframe (e.g., M15), values may appear wrong or delayed.
8. ⚠️Wrong Return Type Usage
Using int buffer values as double, or vice versa. Especially when dealing with
INDICATOR_COLOR_INDEX.
✅ How to Debug Step-by-Step
1. ✅ Check that SetIndexBuffer(...) is correctly assigned for that buffer index.
2. ✅ Verify OnCalculate(...) actually fills that buffer.
3. ✅ Confirm that iCustom(..., buffer_index, shift) uses the correct
buffer_index.
4. ✅ Use Print(...) inside your indicator to log buffer values at runtime (e.g.,
Print("fastMA[0] = ", fastMA[0])).
5. ✅ Log what iCustom(...) is returning in your bot to catch anomalies early.
Example of a Bug
If your bot says:
mql5
CopiarEditar
double val = iCustom(NULL, 0, "MyIndi", ..., 0, 0);
Print("val: ", val); // Output: val: 1.7976931348623158e+308
That probably means:
Buffer 0 was either not calculated yet or
Assigned EMPTY_VALUE.
If the values you're getting from iCustom(...) are unrealistically large, such as
2147483647, 1e+308, or similar outliers, it usually means something went wrong.
Here's a clear list of the most common causes:
🔴 Common Reasons Why iCustom() Returns "Big"/Wrong
Values
1. ❌ Invalid Buffer Index
You're trying to access a buffer (like index 2) that was not set using
SetIndexBuffer(...).
Symptom: iCustom(...) returns garbage values or extreme numbers like
1e+308.
2. ⚠️Incorrect shift (bar index)
Using a shift value that’s out of bounds (e.g., asking for bar 0 when the indicator
hasn't calculated that far yet).
Fix: Ensure OnCalculate() calculates up to rates_total - 1.
3. ⚠️Not Enough History Loaded
On a fresh chart, not enough candles have loaded for the custom indicator to
initialize properly.
Symptom: Values appear as EMPTY_VALUE, NaN, or extreme.
4. ❌ Incorrect Parameters in iCustom() Call
Passing the wrong number or order of parameters (iCustom(NULL, 0,
"MyIndi", ..., buffer_index, shift)).
Symptom: Indicator fails silently or returns junk values.
5. ⚠️Your Indicator Buffers Were Not Filled
You forgot to assign values like buffer[i] = ... inside the loop.
Fix: Ensure your indicator writes to buffer[i] for every i.
6. ❌ Your Indicator Returned EMPTY_VALUE
You explicitly assigned EMPTY_VALUE to a buffer index (e.g., when skipping
invalid candles).
EMPTY_VALUE = DBL_MAX = 1.7976931348623158e+308 → Looks huge!
7. ⚠️Time Mismatch Between Chart and Indicator
If your chart is on one timeframe (e.g., M1), and your indicator logic assumes
another timeframe (e.g., M15), values may appear wrong or delayed.
8. ⚠️Wrong Return Type Usage
Using int buffer values as double, or vice versa. Especially when dealing with
INDICATOR_COLOR_INDEX.
✅ How to Debug Step-by-Step
1. ✅ Check that SetIndexBuffer(...) is correctly assigned for that buffer index.
2. ✅ Verify OnCalculate(...) actually fills that buffer.
3. ✅ Confirm that iCustom(..., buffer_index, shift) uses the correct
buffer_index.
4. ✅ Use Print(...) inside your indicator to log buffer values at runtime (e.g.,
Print("fastMA[0] = ", fastMA[0])).
5. ✅ Log what iCustom(...) is returning in your bot to catch anomalies early.
6. 🧠 What does ? mean in programming?
7. It’s called the ternary operator — a short way of writing an if-else condition.
int start = prev_calculated == 0 ? MathMax(FastMAPeriod, SlowMAPeriod) :
prev_calculated - 1;
8. 🔄 Full Equivalent with if-else
The line above is just a shortcut for:
mql5
int start;
if (prev_calculated == 0)
start = MathMax(FastMAPeriod, SlowMAPeriod);
else
start = prev_calculated - 1;
📣 In Human Language:
“If this is the first time running the indicator, start calculations from where
both the fast and slow moving averages have enough bars. Otherwise,
continue from the last calculated bar (one bar back, just to be safe).”
✅ Example:
mql5
CopiarEditar
int OnCalculate(...) {
if (rates_total < 50) {
Print("Not enough bars");
return 0; // 🚫 Function ends here if condition is true
}
// This part runs only if the above return was NOT triggered
Print("Continue calculating...");
// other logic here
}
🧠 Important Points:
If rates_total < 50, the message is printed and the rest of the code is skipped.
If rates_total >= 50, then the code continues past the return.
💬 Think of return as:
“I’m done with this function — don’t read any further.”
So yes — if it always gets triggered (like inside a loop or a condition that’s always true),
you’ll never reach the lines below.