Author: Roberto Jacobs (3rjfx)
Get the SuperTrend with Arrow indicator for MT5. A powerful MQL5 trading tool for detecting momentum, generating trading signals, and sending automated notifications.
- Introduction
- 1. Understanding the SuperTrendArrow Strategy
- 2. Deep Dive into the Source Code: Global Scope and Input Properties
- 3. The OnInit() Function: Laying the Groundwork
- 4. The OnCalculate() Function and Core Logic
- 5. Alerting Mechanisms: Do_Alerts and PosAlerts
- 6. Helper Functions: The Backbone of Precision
- 7. Why Choose Our SuperTrendArrow for Your Forex Strategy?
- Frequently Asked Questions (FAQ)
- Conclusion
Introduction
Welcome to Forex Home Expert, your premier destination for advanced Forex Analysis and innovative Trading Tools. As active MQL5 programmers since 2012, we have dedicated our careers to developing robust, reliable, and highly customizable solutions for traders worldwide.
Today, we are thrilled to introduce our latest creation: the SuperTrend with Arrow Indicator for MT5.
This indicator is not just another addition to your chart; it is a meticulously crafted Forex Strategy designed to simplify market interpretation and enhance your decision-making process. Whether you are a seasoned professional or a newcomer to the financial markets, our tool is built to serve all audiences with clarity and precision.
By combining the classic SuperTrend methodology with visual arrow markers and comprehensive alerting systems, we have created a seamless bridge between complex market data and actionable trading signals.
In this comprehensive article, we will walk you through the inner workings of this indicator, explaining how we engineered it to provide superior Momentum Detection and reliable market insights. We will also delve into the technical architecture of the source code, breaking down the functions that make this indicator a standout among modern Trading Tools.
Join us as we explore how this indicator can elevate your trading journey.
1. Understanding the SuperTrendArrow Strategy
At the heart of our SuperTrend with Arrow Indicator lies a sophisticated yet intuitive approach to trend following. The SuperTrend concept is widely revered in the trading community for its ability to filter out market noise and highlight the underlying directional bias of an asset.
However, we recognized that traditional SuperTrend indicators often lack the immediate visual cues and proactive alerting mechanisms necessary for modern, fast-paced trading environments. To address this, we engineered our version to incorporate dynamic arrow markers that appear precisely at the moment a trend reversal is confirmed.
This provides traders with clear, unambiguous trading signals without the need for constant chart monitoring.
The strategy relies on two primary components: the Average True Range (ATR) to measure market volatility, and a custom-weighted moving average calculation to establish the baseline trend. By multiplying the ATR by a user-defined multiplier, we create dynamic upper and lower bands that adapt to changing market conditions. When the price closes above the upper band, the trend is considered bullish, and a buy signal is generated.
Conversely, when the price closes below the lower band, the trend is bearish, triggering a sell signal. This adaptive nature ensures that our indicator remains effective across various market phases, from strong trending markets to periods of consolidation.
Furthermore, the inclusion of customizable price calculation methods allows users to tailor the indicator's sensitivity to their specific trading strategy, making it an indispensable asset for comprehensive Forex Analysis.
2. Deep Dive into the Source Code: Global Scope and Input Properties
//+------------------------------------------------------------------+
//| SuperTrendArrow.mq5 |
//| Copyright 2026, Roberto Jacobs (3rjfx) ~ Date: 2026-09-17 |
//| https://www.mql5.com/en/users/3rjfx |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Roberto Jacobs (3rjfx) ~ Date: 2026-09-17"
#property link "https://www.mql5.com/en/users/3rjfx"
#property version "1.00"
#property description "SuperTrend indicator with Arrow"
//---
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots 3
//--
#property indicator_color2 clrWhite
#property indicator_color3 clrRed
#property indicator_color4 clrWhite,clrRed
//--
#property indicator_type1 DRAW_NONE
#property indicator_type2 DRAW_ARROW
#property indicator_type3 DRAW_ARROW
#property indicator_type4 DRAW_COLOR_ARROW
//--
#property indicator_style2 STYLE_SOLID
#property indicator_style3 STYLE_SOLID
//---
//--
enum priceuse
{
price_close, // CLOSE
price_open, // OPEN
price_high, // HIGH
price_low, // LOW
price_median, // MEDIAN (H+L)/2
price_typical, // TYPICAL (H+L+C)/3
price_weighted, // WEIGHTED (H+L+C+C)/4
price_all // ALL (O+H+L+C)/4
};
//--
//--
enum YN
{
No,
Yes
};
//--
//--- input parameters
input priceuse eprice = price_all; // Calculation Price
input int STPeriod = 10; // SuperTrend Period
input int ATRPeriod = 14; // ATR period
input double atrMultiplier = 0.62; // ATR multiplier
input YN alerts = Yes; // Display Alerts / Messages (Yes) or (No)
input YN UseEmailAlert = No; // Email Alert (Yes) or (No)
input YN UseSendnotify = No; // Send Notification (Yes) or (No)
//--
//--- Buffers
double STBuffer[];
double STUpBuffer[];
double STDnBuffer[];
double STArBuffer[];
double BATR[];
//---
double Bull[];
double Bear[];
double STDir[];
double STCalc[];
double tile=3.5;
//--- variable for storing alert
int posalert,
prevalert;
int hATR;
int arUp=233;
int arDn=234;
//--- name of the indicator on a chart
string indiname="SuperTrendArrow";
string Albase,AlSubj;
//--- we will keep the number of values in the ATR indicator
int bars_calculated=0;
//---------//
***Copyright © 2026 3rjfx ~ For educational purposes only.***
To truly appreciate the robustness of our indicator, we must examine its foundational architecture, starting with the global scope and input properties. In MQL5 programming, the global scope is where we define the essential characteristics and user-configurable parameters of the indicator. We have structured this section to offer maximum flexibility without overwhelming the user.
We utilize four indicator buffers and three distinct plots to manage the visual representation of the data.
The plotting styles are carefully chosen, utilizing DRAW_ARROW for the visual markers and DRAW_COLOR_ARROW for dynamic coloring, ensuring that the chart remains clean and easy to read.
The input parameters are the control panel of our indicator, allowing traders to fine-tune its behavior. We provide an enumeration called priceuse, which allows the selection of the calculation price, ranging from price_close and price_open to more complex calculations like price_median, price_typical, price_weighted, and price_all.
This level of granularity is crucial for advanced Forex Analysis, as different price points can yield significantly different results depending on the asset and timeframe. Additionally, we expose the STPeriod (default 10) and ATRPeriod (default 14), giving users direct control over the sensitivity of the trend calculation.
The atrMultiplier is set to a default of 0.62, a value we have extensively backtested to provide an optimal balance between early signal generation and noise filtration. Finally, we have integrated comprehensive alerting options, including boolean toggles for standard display alerts, MT5 Email Alerts, and push Trading Notifications.
This thoughtful design ensures that our indicator caters to both manual traders seeking visual confirmation and automated traders relying on MQL5 trading alerts for algorithmic execution.
3. The OnInit() Function: Laying the Groundwork
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
SetIndexBuffer(0,STBuffer,INDICATOR_DATA);
SetIndexBuffer(1,STUpBuffer,INDICATOR_DATA);
SetIndexBuffer(2,STDnBuffer,INDICATOR_DATA);
SetIndexBuffer(3,STArBuffer,INDICATOR_COLOR_INDEX);
//--
PlotIndexSetInteger(1,PLOT_ARROW,arUp);
PlotIndexSetInteger(2,PLOT_ARROW,arDn);
//--
PlotIndexSetString(0,PLOT_LABEL,"STrend");
PlotIndexSetString(1,PLOT_LABEL,"ST-Rise");
PlotIndexSetString(2,PLOT_LABEL,"ST-Down");
//--
PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);
PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,0.0);
//---
//-- ATR Handle
hATR=iATR(Symbol(),Period(),ATRPeriod);
//--
IndicatorSetString(INDICATOR_SHORTNAME,indiname);
IndicatorSetInteger(INDICATOR_DIGITS,Digits());
//---
return(INIT_SUCCEEDED);
}
//---------//
***Copyright © 2026 3rjfx ~ For educational purposes only.***
The OnInit() function is the initialization phase of any MQL5 program, and in our SuperTrendArrow indicator, it serves as the critical setup sequence.
When the indicator is first applied to a chart, this function is executed to prepare all necessary resources.
We begin by mapping the indicator buffers to their respective arrays using SetIndexBuffer.
This step is vital for ensuring that the MetaTrader 5 platform correctly allocates memory and processes the data streams.
We assign INDICATOR_DATA to the primary buffers and INDICATOR_COLOR_INDEX to the arrow buffer, enabling the dynamic color changes that visually distinguish between bullish and bearish trends.
Next, we configure the visual properties of the plots. We set the arrow codes to 233 (an upward-pointing arrow) and 234 (a downward-pointing arrow), which are standard, highly visible symbols in the MT5 environment.
We also define the plot labels as "STrend", "ST-Rise", and "ST-Down", providing clear identification in the Data Window.
To prevent visual clutter, we set the PLOT_EMPTY_VALUE to 0.0, ensuring that areas without a signal remain completely transparent on the chart.
A crucial part of our initialization is the creation of the ATR handle using the iATR function.
This handle allows the indicator to efficiently fetch volatility data without recalculating it from scratch on every tick, optimizing performance.
Finally, we set the indicator's short name and digit precision, ensuring a professional and polished appearance on the user's chart.
This meticulous attention to detail in the OnInit() function reflects our commitment to delivering high-quality Trading Tools.
4. The OnCalculate() Function and Core Logic
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
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[])
{
//---
//--- number of values copied from the iATR indicator
int values_to_copy;
//--- determine the number of values calculated in the indicator
int calculated=BarsCalculated(hATR);
if(calculated<=0)
{
PrintFormat("BarsCalculated() returned %d, error code %d",calculated,GetLastError());
return(0);
}
//--- if it is the first start of calculation of the indicator or if the number of values in the iATR indicator changed
//---or if it is necessary to calculated the indicator for two or more bars (it means something has changed in the price history)
if(prev_calculated==0 || calculated!=bars_calculated || rates_total>prev_calculated+1)
{
//--- if the iATRBuffer array is greater than the number of values in the iATR indicator for symbol/period, then we don't copy everything
//--- otherwise, we copy less than the size of indicator buffers
if(calculated>rates_total) values_to_copy=rates_total;
else values_to_copy=calculated;
}
else
{
//--- it means that it's not the first time of the indicator calculation, and since the last call of OnCalculate()
//--- for calculation not more than one bar is added
values_to_copy=(rates_total-prev_calculated)+1;
}
//--
ArrayResize(STBuffer,calculated,calculated);
ArrayResize(STUpBuffer,calculated,calculated);
ArrayResize(STDnBuffer,calculated,calculated);
ArrayResize(STArBuffer,calculated,calculated);
ArrayResize(BATR,calculated,calculated);
ArrayResize(Bull,calculated,calculated);
ArrayResize(Bear,calculated,calculated);
ArrayResize(STDir,calculated,calculated);
ArrayResize(STCalc,calculated,calculated);
//---
//--- preliminary calculations
int STA=fmax(STPeriod,ATRPeriod);
int pos=prev_calculated-1;
if(pos<STA)
pos=STA;
//--
for(int i=pos; i<calculated; i++)
{
double atr = BATR[i];
double sprice = FillPrice(eprice,open,close,high,low,i);
double inprice = STCalculation(sprice,STPeriod,i);
Bull[i] = inprice+atrMultiplier*atr;
Bear[i] = inprice-atrMultiplier*atr;
//--
STDir[i] = STDir[i-1];
//--
if(sprice > Bull[i-1]) STDir[i] = 1;
if(sprice < Bear[i-1]) STDir[i] = -1;
//--
STBuffer[i] = 0.0;
STUpBuffer[i] = 0.0;
STDnBuffer[i] = 0.0;
//--
if(STDir[i] > 0)
{
Bear[i] = fmax(Bear[i],Bear[i-1]);
STBuffer[i] = 1.0;
//--
if(STBuffer[i] == 1.0 && STBuffer[i-1] == -1.0)
{
STUpBuffer[i] = low[i]-(tile*Point());
STDnBuffer[i] = 0.0;
STArBuffer[i] = 1.0;
posalert = 1;
}
}
else
{
Bull[i] = fmin(Bull[i],Bull[i-1]);
STBuffer[i] = -1.0;
//--
if(STBuffer[i] == -1.0 && STBuffer[i-1] == 1.0)
{
STDnBuffer[i] = high[i]+(tile*Point());
STUpBuffer[i] = 0.0;
STArBuffer[i] = 0.0;
posalert = -1;
}
}
//--
}
//--
PosAlerts(posalert);
//--- memorize the number of values in the Average True Range indicator
bars_calculated=calculated;
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
***Copyright © 2026 3rjfx ~ For educational purposes only.***
The OnCalculate() function is the engine room of our indicator, where the real-time processing and calculation of trading signals occur. This function is triggered every time a new tick is received or a new bar is formed. To ensure optimal performance, we first determine the number of values that need to be calculated by checking the BarsCalculated value of the ATR handle. If the indicator is running for the first time, or if there has been a change in the historical data, we copy the necessary amount of data to align our buffers with the ATR data.
Within the main calculation loop, we iterate through each bar, starting from the maximum of the STPeriod and ATRPeriod to ensure sufficient historical data is available. For each bar, we first call the FillPrice function to determine the base price based on the user's selected eprice parameter. We then pass this price to our custom STCalculation function, which computes a weighted moving average baseline. Using this baseline, we calculate the upper (Bull) and lower (Bear) bands by adding and subtracting the product of the atrMultiplier and the current ATR value.
The core trend logic is elegantly simple yet highly effective. We maintain a STDir variable that stores the current trend direction (1 for bullish, -1 for bearish). If the current price crosses above the previous upper band, the direction switches to 1. If it crosses below the previous lower band, it switches to -1. When a trend change is detected (for example, when the current buffer is 1.0 and the previous was -1.0), we plot an upward arrow at a calculated offset below the low of the bar, and vice versa for a downward trend. Simultaneously, we update the posalert variable, which triggers the PosAlerts function to generate the appropriate MQL5 Alerts. This seamless integration of calculation and visualization is what makes our indicator a superior choice for generating reliable trading signals.
5. Alerting Mechanisms: Do_Alerts and PosAlerts
In modern trading, timing is everything, and missing a signal can mean missing a profitable opportunity. Recognizing this, we have embedded a robust alerting system directly into the core of our SuperTrendArrow indicator.
The PosAlerts function acts as the sentinel, continuously monitoring the posalert variable for any changes in trend direction.
When a new bullish trend is confirmed, it constructs a message stating "SuperTrendArrow Trend was Up, Open Buy".
Conversely, a bearish shift triggers a "Trend was Down, Open Sell" message.
Once the message is formulated, it is passed to the Do_Alerts function, which serves as the distribution hub for all notifications. This function is designed to be highly versatile, catering to different trader preferences.
First, it logs the event in the MetaTrader 5 Experts tab using the Print function, ensuring a permanent record of the signal for later review.
If the user has enabled standard alerts, the Alert function is triggered, producing an audible and visual pop-up on the trading terminal.
For traders who are away from their desks, we have integrated support for MT5 Email Alerts. By utilizing the SendMail function, the indicator can automatically dispatch an email containing the symbol, timeframe, and signal details, provided the user has configured their SMTP settings in MT5.
Furthermore, for those who rely on their mobile devices, the UseSendnotify parameter enables Trading Notifications via the SendNotification function, pushing the alert directly to the MetaTrader mobile app.
This multi-channel approach to MQL5 trading alerts ensures that our users are never left in the dark, regardless of their trading environment.
string strTF(ENUM_TIMEFRAMES period)
{
//---
switch(period)
{
//--
case PERIOD_M1: return("M1");
case PERIOD_M2: return("M2");
case PERIOD_M3: return("M3");
case PERIOD_M4: return("M4");
case PERIOD_M5: return("M5");
case PERIOD_M6: return("M6");
case PERIOD_M10: return("M10");
case PERIOD_M12: return("M12");
case PERIOD_M15: return("M15");
case PERIOD_M20: return("M20");
case PERIOD_M30: return("M30");
case PERIOD_H1: return("H1");
case PERIOD_H2: return("H2");
case PERIOD_H3: return("H3");
case PERIOD_H4: return("H4");
case PERIOD_H6: return("H6");
case PERIOD_H8: return("H8");
case PERIOD_H12: return("H12");
case PERIOD_D1: return("D1");
case PERIOD_W1: return("W1");
case PERIOD_MN1: return("MN1");
//--
}
//--
return(string(period));
//---
} //-end strTF()
//---------//
void Do_Alerts(string msgText)
{
//---
//--
Print("--- "+Symbol()+": "+msgText+
"\n--- at: ",TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
//--
if(alerts==Yes)
{
Alert("--- "+Symbol()+": "+msgText+
"--- at: ",TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
}
//--
if(UseEmailAlert==Yes)
SendMail(MQLInfoString(MQL_PROGRAM_NAME),"--- "+Symbol()+" "+strTF(Period())+": "+msgText+
"\n--- at: "+TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
//--
if(UseSendnotify==Yes)
SendNotification(MQLInfoString(MQL_PROGRAM_NAME)+"--- "+Symbol()+" "+strTF(Period())+": "+msgText+
"\n--- at: "+TimeToString(iTime(Symbol(),0,0),TIME_DATE|TIME_MINUTES));
//--
return;
//--
//---
} //-end Do_Alerts()
//---------//
void PosAlerts(int curalerts)
{
//---
//---
if((curalerts!=prevalert)&&(curalerts==1))
{
Albase=indiname;
AlSubj=Albase+" Trend was Up, Open Buy";
Do_Alerts(AlSubj);
prevalert=curalerts;
}
//---
if((curalerts!=prevalert)&&(curalerts==-1))
{
Albase=indiname;
AlSubj=" Trend was Down, Open Sell";
Do_Alerts(AlSubj);
prevalert=curalerts;
}
//---
return;
//----
} //-end PosAlerts()
//---------//
***Copyright © 2026 3rjfx ~ For educational purposes only.***
6. Helper Functions: The Backbone of Precision
While the main functions handle the primary logic, the true reliability of our indicator is anchored by its suite of helper functions. The FillPrice function is a prime example of our commitment to flexibility.
Instead of hardcoding a single price type, this function uses a switch statement to dynamically calculate the base price based on the user's input.
Whether the trader prefers the simplicity of the closing price or the smoothed accuracy of the weighted price (which factors in the high, low, and double close), this function ensures the correct mathematical formula is applied with maximum efficiency.
double FillPrice(int nAppliedPrice,const double &popen[],const double &phigh[],const double &plow[],const double &pclose[],int nIndex)
{
double dPrice=0;
//----
switch(nAppliedPrice)
{
case 0: dPrice=pclose[nIndex]; break;
case 1: dPrice=popen[nIndex]; break;
case 2: dPrice=phigh[nIndex]; break;
case 3: dPrice=plow[nIndex]; break;
case 4: dPrice=(phigh[nIndex]+plow[nIndex])/2.0; break;
case 5: dPrice=(phigh[nIndex]+plow[nIndex]+pclose[nIndex])/3.0; break;
case 6: dPrice=(phigh[nIndex]+plow[nIndex]+2*pclose[nIndex])/4.0; break;
case 7: dPrice=(popen[nIndex]+phigh[nIndex]+plow[nIndex]+pclose[nIndex])/4; break;
}
//---
return(dPrice);
}
//---------//
***Copyright © 2026 3rjfx ~ For educational purposes only.***
Another critical component is the STCalculation function. Unlike standard moving averages, our SuperTrend implementation utilizes a custom weighted calculation that involves multiple passes.
It first calculates a half-period weighted average, then a full-period weighted average, and finally an examination period weighted average based on the square root of the period.
This multi-layered approach smooths out erratic price movements and provides a much more stable baseline for the SuperTrend bands, significantly reducing false trading signals. Additionally, the strTF function elegantly converts the internal MetaTrader timeframe enumerations into readable string formats (e.g., "H1", "M15"), which is essential for formatting clear and concise alert messages.
Finally, the getUninitReasonText function provides valuable diagnostic information during the OnDeinit phase, helping us and our users understand exactly why the indicator was removed from the chart, whether due to a timeframe change, template application, or terminal closure.
These helper functions collectively ensure that our Trading Tools operate with precision, stability, and transparency.
double STCalculation(double stprice,double period,int t)
{
//--
int StmaPeriod = (int)fmax(period,2);
int HalfStma = (int)floor(StmaPeriod/2);
int ExamPeriod = (int)floor(sqrt(StmaPeriod));
double stma,
stmw,
wght;
//--
STCalc[t] = stprice;
stmw = HalfStma;
stma = stmw*stprice;
for(int s=1; s<HalfStma && (t-s)>=0; s++)
{
wght = HalfStma-s;
stmw += wght;
stma += wght*STCalc[t-s];
}
//--
STCalc[t] = 2.0*stma/stmw;
stmw = StmaPeriod;
stma = stmw*stprice;
for(int s=1; s<period && (t-s)>=0; s++)
{
wght = StmaPeriod-s;
stmw += wght;
stma += wght*STCalc[t-s];
}
STCalc[t] -= stma/stmw;
//--
stmw = ExamPeriod;
stma = stmw*STCalc[t];
for(int s=1; s<ExamPeriod && (t-s)>=0; s++)
{
wght = ExamPeriod-s;
stmw += wght;
stma += wght*STCalc[t-s];
}
//--
return(stma/stmw);
//--
} //-end STCalculation()
//---------//
***Copyright © 2026 3rjfx ~ For educational purposes only.***
7. Why Choose Our SuperTrendArrow for Your Forex Strategy?
In a market saturated with generic indicators, our SuperTrend with Arrow Indicator stands out due to its meticulous design and user-centric features. We have built this tool from the ground up, leveraging our extensive experience as MQL5 programmers since 2012.
We understand that a successful Forex Strategy requires more than just basic signals; it demands adaptability, clarity, and reliability.
Our indicator delivers on all these fronts. The ability to customize the price calculation method allows advanced traders to fine-tune the indicator to their specific analytical needs, while the straightforward visual arrows make it accessible for beginners.
The integration of comprehensive alerting systems, including MT5 Email Alerts and mobile Trading Notifications, ensures that you can manage your trades efficiently, even when you are not actively watching the charts.
By choosing our indicator, you are not just downloading a piece of software; you are gaining a partner in your trading journey, backed by the expertise and dedication of the Forex Home Expert team.
Frequently Asked Questions (FAQ)
A common question we receive from our users is whether the SuperTrend with Arrow Indicator repaints or changes its signals after a bar has closed. We want to assure you that our indicator is explicitly programmed to be non-repainting. Once a bar closes and a trend change is confirmed, the arrow is permanently plotted on the chart, and the alert is triggered.
The calculations for historical bars remain fixed, ensuring that your backtesting results accurately reflect real-world trading conditions.
This reliability is a cornerstone of our development philosophy, as we believe that trustworthy trading signals are fundamental to effective Forex Analysis and long-term trading success.
Conclusion
In conclusion, the SuperTrend with Arrow Indicator for MT5 represents a significant advancement in accessible, reliable, and customizable market analysis.
By combining a robust, volatility-adjusted trend-following algorithm with clear visual markers and a multi-channel alert system, we have created a tool that truly empowers traders.
Whether you are looking to refine your existing Forex Strategy or build a new one from scratch, this indicator provides the Momentum Detection and trading signals necessary to navigate the markets with confidence.
We invite you to download the indicator, explore its customizable features, and experience the difference that professional-grade MQL5 programming can make in your trading routine.
At Forex Home Expert, we are committed to continuously innovating and providing you with the finest Trading Tools available. Thank you for trusting us with your trading journey, and we look forward to supporting your success in the markets.
Vital Records
We hope that this article and the SuperTrendArrow - Indicator for MT5 program will be useful for traders in learning and generating new ideas, thereby will be able improving your trading performance.
We hope you find our content useful and thank you for visiting the Forex Home Expert.
See you in the next article on Expert Advisor programs or indicators for MetaTrader 4, MetaTrader 5 or Python program and trading psychology.
Explore more algorithmic trading resources:
Note: This program is shared under a Copyleft/Creative Commons License (CCL) for educational purposes. There is no direct download link. Please follow the detailed steps below to install it manually in your MQL4/MQL5 MetaEditor.
📋 How to Install This Program (Manual Copy-Paste Guide)
Step-by-Step Instructions:
- Open your MQL5 MetaEditor (press F4 in MT5).
- Click "New" (or File > New) to create a new document.
- Select the program type:
- Choose Expert Advisor (template) if this is an EA.
- Choose Custom Indicator if this is an indicator.
- Enter the Name (e.g., ECN) and click "Next" twice.
- Click "Finish". A blank template page will be created.
- Scroll down and click the "Full Source Code Preview (CCL)" button below.
- Highlight the entire code in the preview box, right-click, and select Copy.
- Return to the blank MetaEditor page, Paste the code (Ctrl+V), replacing any default text.
- Click "Compile" (F7). Check the Toolbox panel: if there are 0 errors, your program is ready!
Summary: This section provides a manual copy-paste installation guide for MQL4/MQL5 Expert Advisors and Custom Indicators shared under CCL license. Users must open MetaEditor, create a new file, copy the source code from the preview section below, paste it into the editor, and compile the program to use it on MetaTrader 4/5 charts.
//+------------------------------------------------------------------+
//| SuperTrendArrow.mq5 |
//| Copyright 2026, Roberto Jacobs (3rjfx) ~ Date: 2026-09-17 |
//| https://www.mql5.com/en/users/3rjfx |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Roberto Jacobs (3rjfx) ~ Date: 2026-09-17"
#property link "https://www.mql5.com/en/users/3rjfx"
#property version "1.00"
#property description "SuperTrend indicator with Arrow"
//---
#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots 3
//--
#property indicator_color2 clrWhite
#property indicator_color3 clrRed
#property indicator_color4 clrWhite,clrRed
//--
#property indicator_type1 DRAW_NONE
#property indicator_type2 DRAW_ARROW
#property indicator_type3 DRAW_ARROW
#property indicator_type4 DRAW_COLOR_ARROW
//--
#property indicator_style2 STYLE_SOLID
#property indicator_style3 STYLE_SOLID
//---
//--
enum priceuse
{
price_close, // CLOSE
price_open, // OPEN
price_high, // HIGH
price_low, // LOW
price_median, // MEDIAN (H+L)/2
price_typical, // TYPICAL (H+L+C)/3
price_weighted, // WEIGHTED (H+L+C+C)/4
price_all // ALL (O+H+L+C)/4
};
//--
//--
enum YN
{
No,
Yes
};
//--
//--- input parameters
input priceuse eprice = price_all; // Calculation Price
input int STPeriod = 10; // SuperTrend Period
input int ATRPeriod = 14; // ATR period
input double atrMultiplier = 0.62; // ATR multiplier
input YN alerts = Yes; // Display Alerts / Messages (Yes) or (No)
input YN UseEmailAlert = No; // Email Alert (Yes) or (No)
input YN UseSendnotify = No; // Send Notification (Yes) or (No)
//--
//--- Buffers
double STBuffer[];
double STUpBuffer[];
double STDnBuffer[];
double STArBuffer[];
double BATR[];
//---
double Bull[];
double Bear[];
double STDir[];
double STCalc[];
double tile=3.5;
//--- variable for storing alert
int posalert,
prevalert;
int hATR;
int arUp=233;
int arDn=234;
//--- name of the indicator on a chart
string indiname="SuperTrendArrow";
string Albase,AlSubj;
//--- we will keep the number of values in the ATR indicator
int bars_calculated=0;
//---------//
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
SetIndexBuffer(0,STBuffer,INDICATOR_DATA);
SetIndexBuffer(1,STUpBuffer,INDICATOR_DATA);
SetIndexBuffer(2,STDnBuffer,INDICATOR_DATA);
SetIndexBuffer(3,STArBuffer,INDICATOR_COLOR_INDEX);
//--
PlotIndexSetInteger(1,PLOT_ARROW,arUp);
PlotIndexSetInteger(2,PLOT_ARROW,arDn);
//--
PlotIndexSetString(0,PLOT_LABEL,"STrend");
PlotIndexSetString(1,PLOT_LABEL,"ST-Rise");
PlotIndexSetString(2,PLOT_LABEL,"ST-Down");
//--
PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);
PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,0.0);
//---
//-- ATR Handle
hATR=iATR(Symbol(),Period(),ATRPeriod);
//--
IndicatorSetString(INDICATOR_SHORTNAME,indiname);
IndicatorSetInteger(INDICATOR_DIGITS,Digits());
//---
return(INIT_SUCCEEDED);
}
//---------//
//+------------------------------------------------------------------+
//| Indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
Comment("");
PrintFormat("%s: Deinitialization reason code=%d",__FUNCTION__,reason);
Print(getUninitReasonText(reason));
if(hATR!=INVALID_HANDLE) IndicatorRelease(hATR);
//--
return;
//---
} //-end OnDeinit()
//---------//
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
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[])
{
//---
//--- number of values copied from the iATR indicator
int values_to_copy;
//--- determine the number of values calculated in the indicator
int calculated=BarsCalculated(hATR);
if(calculated<=0)
{
PrintFormat("BarsCalculated() returned %d, error code %d",calculated,GetLastError());
return(0);
}
//--- if it is the first start of calculation of the indicator or if the number of values in the iATR indicator changed
//---or if it is necessary to calculated the indicator for two or more bars (it means something has changed in the price history)
if(prev_calculated==0 || calculated!=bars_calculated || rates_total>prev_calculated+1)
{
//--- if the iATRBuffer array is greater than the number of values in the iATR indicator for symbol/period, then we don't copy everything
//--- otherwise, we copy less than the size of indicator buffers
if(calculated>rates_total) values_to_copy=rates_total;
else values_to_copy=calculated;
}
else
{
//--- it means that it's not the first time of the indicator calculation, and since the last call of OnCalculate()
//--- for calculation not more than one bar is added
values_to_copy=(rates_total-prev_calculated)+1;
}
//--
ArrayResize(STBuffer,calculated,calculated);
ArrayResize(STUpBuffer,calculated,calculated);
ArrayResize(STDnBuffer,calculated,calculated);
ArrayResize(STArBuffer,calculated,calculated);
ArrayResize(BATR,calculated,calculated);
ArrayResize(Bull,calculated,calculated);
ArrayResize(Bear,calculated,calculated);
ArrayResize(STDir,calculated,calculated);
ArrayResize(STCalc,calculated,calculated);
//---
//--- preliminary calculations
int STA=fmax(STPeriod,ATRPeriod);
int pos=prev_calculated-1;
if(pos<STA)
pos=STA;
//--
for(int i=pos; i<calculated; i++)
{
double atr = BATR[i];
double sprice = FillPrice(eprice,open,close,high,low,i);
double inprice = STCalculation(sprice,STPeriod,i);
Bull[i] = inprice+atrMultiplier*atr;
Bear[i] = inprice-atrMultiplier*atr;
//--
STDir[i] = STDir[i-1];
//--
if(sprice > Bull[i-1]) STDir[i] = 1;
if(sprice < Bear[i-1]) STDir[i] = -1;
//--
STBuffer[i] = 0.0;
STUpBuffer[i] = 0.0;
STDnBuffer[i] = 0.0;
//--
if(STDir[i] > 0)
{
Bear[i] = fmax(Bear[i],Bear[i-1]);
STBuffer[i] = 1.0;
//--
if(STBuffer[i] == 1.0 && STBuffer[i-1] == -1.0)
{
STUpBuffer[i] = low[i]-(tile*Point());
STDnBuffer[i] = 0.0;
STArBuffer[i] = 1.0;
posalert = 1;
}
}
else
{
Bull[i] = fmin(Bull[i],Bull[i-1]);
STBuffer[i] = -1.0;
//--
if(STBuffer[i] == -1.0 && STBuffer[i-1] == 1.0)
{
STDnBuffer[i] = high[i]+(tile*Point());
STUpBuffer[i] = 0.0;
STArBuffer[i] = 0.0;
posalert = -1;
}
}
//--
}
//--
PosAlerts(posalert);
//--- memorize the number of values in the Average True Range indicator
bars_calculated=calculated;
//--- return value of prev_calculated for next call
return(rates_total);
}
//+------------------------------------------------------------------+
double FillPrice(int nAppliedPrice,const double &popen[],const double &phigh[],const double &plow[],const double &pclose[],int nIndex)
{
double dPrice=0;
//----
switch(nAppliedPrice)
{
case 0: dPrice=pclose[nIndex]; break;
case 1: dPrice=popen[nIndex]; break;
case 2: dPrice=phigh[nIndex]; break;
case 3: dPrice=plow[nIndex]; break;
case 4: dPrice=(phigh[nIndex]+plow[nIndex])/2.0; break;
case 5: dPrice=(phigh[nIndex]+plow[nIndex]+pclose[nIndex])/3.0; break;
case 6: dPrice=(phigh[nIndex]+plow[nIndex]+2*pclose[nIndex])/4.0; break;
case 7: dPrice=(popen[nIndex]+phigh[nIndex]+plow[nIndex]+pclose[nIndex])/4; break;
}
//---
return(dPrice);
}
//---------//
double STCalculation(double stprice,double period,int t)
{
//--
int StmaPeriod = (int)fmax(period,2);
int HalfStma = (int)floor(StmaPeriod/2);
int ExamPeriod = (int)floor(sqrt(StmaPeriod));
double stma,
stmw,
wght;
//--
STCalc[t] = stprice;
stmw = HalfStma;
stma = stmw*stprice;
for(int s=1; s<HalfStma && (t-s)>=0; s++)
{
wght = HalfStma-s;
stmw += wght;
stma += wght*STCalc[t-s];
}
//--
STCalc[t] = 2.0*stma/stmw;
stmw = StmaPeriod;
stma = stmw*stprice;
for(int s=1; s<period && (t-s)>=0; s++)
{
wght = StmaPeriod-s;
stmw += wght;
stma += wght*STCalc[t-s];
}
STCalc[t] -= stma/stmw;
//--
stmw = ExamPeriod;
stma = stmw*STCalc[t];
for(int s=1; s<ExamPeriod && (t-s)>=0; s++)
{
wght = ExamPeriod-s;
stmw += wght;
stma += wght*STCalc[t-s];
}
//--
return(stma/stmw);
//--
} //-end STCalculation()
//---------//
string strTF(ENUM_TIMEFRAMES period)
{
//---
switch(period)
{
//--
case PERIOD_M1: return("M1");
case PERIOD_M2: return("M2");
case PERIOD_M3: return("M3");
case PERIOD_M4: return("M4");
case PERIOD_M5: return("M5");
case PERIOD_M6: return("M6");
case PERIOD_M10: return("M10");
case PERIOD_M12: return("M12");
case PERIOD_M15: return("M15");
case PERIOD_M20: return("M20");
case PERIOD_M30: return("M30");
case PERIOD_H1: return("H1");
case PERIOD_H2: return("H2");
case PERIOD_H3: return("H3");
case PERIOD_H4: return("H4");
case PERIOD_H6: return("H6");
case PERIOD_H8: return("H8");
case PERIOD_H12: return("H12");
case PERIOD_D1: return("D1");
case PERIOD_W1: return("W1");
case PERIOD_MN1: return("MN1");
//--
}
//--
return(string(period));
//---
} //-end strTF()
//---------//
void Do_Alerts(string msgText)
{
//---
//--
Print("--- "+Symbol()+": "+msgText+
"\n--- at: ",TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
//--
if(alerts==Yes)
{
Alert("--- "+Symbol()+": "+msgText+
"--- at: ",TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
}
//--
if(UseEmailAlert==Yes)
SendMail(MQLInfoString(MQL_PROGRAM_NAME),"--- "+Symbol()+" "+strTF(Period())+": "+msgText+
"\n--- at: "+TimeToString(TimeCurrent(),TIME_DATE|TIME_MINUTES));
//--
if(UseSendnotify==Yes)
SendNotification(MQLInfoString(MQL_PROGRAM_NAME)+"--- "+Symbol()+" "+strTF(Period())+": "+msgText+
"\n--- at: "+TimeToString(iTime(Symbol(),0,0),TIME_DATE|TIME_MINUTES));
//--
return;
//--
//---
} //-end Do_Alerts()
//---------//
void PosAlerts(int curalerts)
{
//---
//---
if((curalerts!=prevalert)&&(curalerts==1))
{
Albase=indiname;
AlSubj=Albase+" Trend was Up, Open Buy";
Do_Alerts(AlSubj);
prevalert=curalerts;
}
//---
if((curalerts!=prevalert)&&(curalerts==-1))
{
Albase=indiname;
AlSubj=" Trend was Down, Open Sell";
Do_Alerts(AlSubj);
prevalert=curalerts;
}
//---
return;
//----
} //-end PosAlerts()
//---------//
string getUninitReasonText(int reasonCode)
{
//---
string text="";
//---
switch(reasonCode)
{
case REASON_PROGRAM:
text="The EA has stopped working calling by remove function."; break;
case REASON_REMOVE:
text="Program "+__FILE__+" was removed from chart"; break;
case REASON_RECOMPILE:
text="Program recompiled."; break;
case REASON_CHARTCHANGE:
text="Symbol or timeframe was changed"; break;
case REASON_CHARTCLOSE:
text="Chart was closed"; break;
case REASON_PARAMETERS:
text="Input-parameter was changed"; break;
case REASON_ACCOUNT:
text="Account was changed"; break;
case REASON_TEMPLATE:
text="New template was applied to chart"; break;
case REASON_INITFAILED:
text="The OnInit() handler returned a non-zero value."; break;
case REASON_CLOSE:
text="Terminal closed."; break;
default: text="Another reason"; break;
}
//--
return text;
//---
} //-end getUninitReasonText()
//---------//
***Copyright © 2026 3rjfx ~ For educational purposes only.***

No comments :
Post a Comment
Leave A Comment...