Back to Blog

Custom Indicator Development for MT4/MT5: From Concept to Code

Off-the-shelf indicators often don't capture your exact trading idea. Here's how to build custom indicators from scratch. .

Viprasol Team
January 18, 2026
17 min read

Custom Indicator Development Mt4 Mt5 | Viprasol Tech

Custom Indicator Development for MT4/MT5

Off-the-shelf indicators often don't capture your exact trading idea. Here's how to build custom indicators from scratch.

Indicator Architecture Basics

MT5 Indicator Structure:

//+------------------------------------------------------------------+
//| Indicator Properties                                               |
//+------------------------------------------------------------------+
#property indicator_chart_window          // Draw on main chart
//#property indicator_separate_window      // Draw in separate window
#property indicator_buffers 2             // Number of data buffers
#property indicator_plots 2               // Number of visual plots

//--- Plot settings
#property indicator_label1 "Signal Line"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrBlue
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2

//--- Input parameters
input int      InpPeriod = 14;           // Period
input double   InpDeviation = 2.0;       // Deviation
input ENUM_APPLIED_PRICE InpPrice = PRICE_CLOSE;

//--- Indicator buffers
double SignalBuffer[];
double HistogramBuffer[];

//+------------------------------------------------------------------+
int OnInit() {
    // Initialize buffers
    SetIndexBuffer(0, SignalBuffer, INDICATOR_DATA);
    SetIndexBuffer(1, HistogramBuffer, INDICATOR_DATA);
    
    // Set draw begin
    PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpPeriod);
    
    // Set indicator name
    IndicatorSetString(INDICATOR_SHORTNAME, "MyIndicator(" + IntegerToString(InpPeriod) + ")");
    
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
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[]) {
    
    int start;
    
    if(prev_calculated == 0) {
        start = InpPeriod;
        ArrayInitialize(SignalBuffer, EMPTY_VALUE);
    } else {
        start = prev_calculated - 1;
    }
    
    for(int i = start; i < rates_total; i++) {
        // Your calculation logic here
        SignalBuffer[i] = CalculateSignal(close, i, InpPeriod);
    }
    
    return(rates_total);
}

Building a Custom RSI Divergence Indicator

Complete Example:

#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots 3

#property indicator_label1 "RSI"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2

#property indicator_label2 "Bullish Divergence"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrGreen

#property indicator_label3 "Bearish Divergence"
#property indicator_type3 DRAW_ARROW
#property indicator_color3 clrRed

//--- Inputs
input int RSI_Period = 14;
input int Divergence_Lookback = 20;
input double RSI_Oversold = 30;
input double RSI_Overbought = 70;

//--- Buffers
double RSI_Buffer[];
double BullDiv_Buffer[];
double BearDiv_Buffer[];
double Price_Highs[];
double Price_Lows[];

int OnInit() {
    SetIndexBuffer(0, RSI_Buffer, INDICATOR_DATA);
    SetIndexBuffer(1, BullDiv_Buffer, INDICATOR_DATA);
    SetIndexBuffer(2, BearDiv_Buffer, INDICATOR_DATA);
    SetIndexBuffer(3, Price_Highs, INDICATOR_CALCULATIONS);
    SetIndexBuffer(4, Price_Lows, INDICATOR_CALCULATIONS);
    
    PlotIndexSetInteger(1, PLOT_ARROW, 233);  // Up arrow
    PlotIndexSetInteger(2, PLOT_ARROW, 234);  // Down arrow
    
    IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, RSI_Overbought);
    IndicatorSetDouble(INDICATOR_LEVELVALUE, 1, RSI_Oversold);
    IndicatorSetInteger(INDICATOR_LEVELS, 2);
    
    return(INIT_SUCCEEDED);
}

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[]) {
    
    // Calculate RSI
    int handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
    CopyBuffer(handle, 0, 0, rates_total, RSI_Buffer);
    
    // Initialize divergence buffers
    if(prev_calculated == 0) {
        ArrayInitialize(BullDiv_Buffer, EMPTY_VALUE);
        ArrayInitialize(BearDiv_Buffer, EMPTY_VALUE);
    }
    
    int start = MathMax(prev_calculated - 1, Divergence_Lookback);
    
    for(int i = start; i < rates_total - 1; i++) {
        // Check for bullish divergence
        if(IsBullishDivergence(close, RSI_Buffer, i, Divergence_Lookback)) {
            BullDiv_Buffer[i] = RSI_Buffer[i];
        } else {
            BullDiv_Buffer[i] = EMPTY_VALUE;
        }
        
        // Check for bearish divergence
        if(IsBearishDivergence(close, RSI_Buffer, i, Divergence_Lookback)) {
            BearDiv_Buffer[i] = RSI_Buffer[i];
        } else {
            BearDiv_Buffer[i] = EMPTY_VALUE;
        }
    }
    
    return(rates_total);
}

bool IsBullishDivergence(const double &price[], const double &rsi[], 
                         int bar, int lookback) {
    // Find previous swing low in price
    int prev_low_bar = FindPreviousSwingLow(price, bar, lookback);
    if(prev_low_bar < 0) return false;
    
    // Price making lower low, RSI making higher low
    if(price[bar] < price[prev_low_bar] && 
       rsi[bar] > rsi[prev_low_bar] &&
       rsi[bar] < RSI_Oversold) {
        return true;
    }
    
    return false;
}

bool IsBearishDivergence(const double &price[], const double &rsi[],
                         int bar, int lookback) {
    // Find previous swing high in price
    int prev_high_bar = FindPreviousSwingHigh(price, bar, lookback);
    if(prev_high_bar < 0) return false;
    
    // Price making higher high, RSI making lower high
    if(price[bar] > price[prev_high_bar] && 
       rsi[bar] < rsi[prev_high_bar] &&
       rsi[bar] > RSI_Overbought) {
        return true;
    }
    
    return false;
}

int FindPreviousSwingLow(const double &price[], int current_bar, int lookback) {
    int min_bar = -1;
    double min_price = DBL_MAX;
    
    for(int i = current_bar - 3; i >= current_bar - lookback && i >= 0; i--) {
        if(price[i] < min_price && IsSwingLow(price, i)) {
            min_price = price[i];
            min_bar = i;
        }
    }
    
    return min_bar;
}

bool IsSwingLow(const double &price[], int bar) {
    if(bar < 2 || bar >= ArraySize(price) - 2) return false;
    
    return (price[bar] < price[bar-1] && 
            price[bar] < price[bar-2] &&
            price[bar] < price[bar+1] && 
            price[bar] < price[bar+2]);
}

🤖 Can This Strategy Be Automated?

In 2026, top traders run custom EAs — not manual charts. We build MT4/MT5 Expert Advisors that execute your exact strategy 24/7, pass prop firm challenges, and eliminate emotional decisions.

  • Runs 24/7 — no screen time, no missed entries
  • Prop-firm compliant (FTMO, MFF, TFT drawdown rules)
  • MyFXBook-verified backtest results included
  • From strategy brief to live EA in 2–4 weeks

Multi-Timeframe Indicators

Access Higher Timeframe Data:

#property indicator_chart_window
#property indicator_buffers 2

input ENUM_TIMEFRAMES HTF = PERIOD_H4;  // Higher timeframe
input int MA_Period = 20;

double HTF_MA_Buffer[];
double Current_MA_Buffer[];

int htf_ma_handle;
int current_ma_handle;

int OnInit() {
    htf_ma_handle = iMA(_Symbol, HTF, MA_Period, 0, MODE_SMA, PRICE_CLOSE);
    current_ma_handle = iMA(_Symbol, PERIOD_CURRENT, MA_Period, 0, MODE_SMA, PRICE_CLOSE);
    
    SetIndexBuffer(0, HTF_MA_Buffer, INDICATOR_DATA);
    SetIndexBuffer(1, Current_MA_Buffer, INDICATOR_DATA);
    
    return(INIT_SUCCEEDED);
}

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[]) {
    
    // Copy current timeframe MA
    CopyBuffer(current_ma_handle, 0, 0, rates_total, Current_MA_Buffer);
    
    // Copy and expand HTF MA
    double htf_temp[];
    int htf_bars = Bars(_Symbol, HTF);
    ArrayResize(htf_temp, htf_bars);
    CopyBuffer(htf_ma_handle, 0, 0, htf_bars, htf_temp);
    
    // Map HTF values to current timeframe
    for(int i = 0; i < rates_total; i++) {
        datetime bar_time = time[i];
        int htf_shift = iBarShift(_Symbol, HTF, bar_time);
        
        if(htf_shift >= 0 && htf_shift < htf_bars) {
            HTF_MA_Buffer[i] = htf_temp[htf_shift];
        }
    }
    
    return(rates_total);
}

Performance Optimization

Efficient Calculation:

// Bad: Recalculates entire history every tick
for(int i = 0; i < rates_total; i++) {
    Buffer[i] = HeavyCalculation(close, i);
}

// Good: Only calculate new bars
int limit;
if(prev_calculated == 0) {
    limit = 0;
} else {
    limit = prev_calculated - 1;
}

for(int i = limit; i < rates_total; i++) {
    Buffer[i] = HeavyCalculation(close, i);
}

Caching Expensive Operations:

static double cached_value = 0;
static datetime last_calc_time = 0;

double GetExpensiveValue() {
    if(TimeCurrent() - last_calc_time < 60) {  // Cache for 60 seconds
        return cached_value;
    }
    
    cached_value = ExpensiveCalculation();
    last_calc_time = TimeCurrent();
    return cached_value;
}
Custom Indicators - Custom Indicator Development for MT4/MT5: From Concept to Code

📈 Stop Trading Manually — Let AI Do It

While you sleep, your EA keeps working. Viprasol builds prop-firm-compliant Expert Advisors with strict risk management, real backtests, and live deployment support.

  • No rule violations — daily drawdown, max drawdown, consistency rules built in
  • Covers MT4, MT5, cTrader, and Python-based algos
  • 5.0★ Upwork record — 100% job success rate
  • Free strategy consultation before we write a single line

Testing Your Indicator

Visual Testing Checklist:

  1. ✅ Correct values compared to manual calculation
  2. ✅ No repaint issues
  3. ✅ Handles missing data gracefully
  4. ✅ Works on different timeframes
  5. ✅ Works on different symbols
  6. ✅ Efficient (doesn't lag)

Debug Output:

#ifdef _DEBUG
    Print("Bar: ", i, " Value: ", SignalBuffer[i]);
#endif

Our Custom Indicator Services

Viprasol develops professional custom indicators:

  • Proprietary algorithms
  • Multi-timeframe analysis
  • Divergence detection
  • Pattern recognition
  • Alert systems

Need a custom indicator? Contact us with your specifications.

Custom IndicatorsMQL4MQL5Technical AnalysisProgrammingDevelopment
Share this article:

About the Author

V

Viprasol Tech Team

Custom Software Development Specialists

The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 1000+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement.

MT4/MT5 EA DevelopmentAI Agent SystemsSaaS DevelopmentAlgorithmic Trading

Ready to Automate Your Trading?

Get a custom Expert Advisor built by professionals with verified MyFXBook results.

Free consultation • No commitment • Response within 24 hours

Viprasol · Trading Software

Need a custom EA or trading bot built?

We specialise in MT4/MT5 Expert Advisor development — prop-firm compliant, forward-tested before live, MyFXBook verifiable. 5.0★ Upwork, 100% Job Success, 1000+ projects shipped.