Advertisement IC Markets

The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Post by FTtrader »

MQL4 Version (For MetaTrader 4)

Create a new Expert Advisor in MetaEditor 4. MQL4 handles timeseries arrays (Close[]) and order execution (OrderSend) differently, so the math indexing and execution logic are adjusted.

Code: Select all

//+------------------------------------------------------------------+
//|                                              ALMA_Pro_MT4.mq4    |
//+------------------------------------------------------------------+
#property strict

// --- Inputs ---
input int    InpAlmaLength = 9;       // ALMA Window Size
input double InpAlmaOffset = 0.85;    // ALMA Offset
input double InpAlmaSigma  = 6.0;     // ALMA Sigma
input int    InpMacdFast   = 12;      // MACD Fast
input int    InpMacdSlow   = 26;      // MACD Slow
input int    InpMacdSignal = 9;       // MACD Signal
input int    InpAtrPeriod  = 14;      // ATR Period
input double InpSlAtrMult  = 1.5;     // SL ATR Multiplier
input double InpRrRatio    = 2.0;     // Risk/Reward Ratio
input double InpLotSize    = 0.1;     // Fixed Lot Size
input int    InpMagic      = 123456;  // Magic Number

void OnTick() {
    static datetime lastBar = 0;
    if(Time[0] == lastBar) return; // Execute strictly on bar close
    
    if(OrdersTotal() > 0) return; // Prevent overlapping trades
    
    // Calculate ALMA for the last two closed bars
    double alma1 = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    double alma2 = CalculateALMA(2, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    
    double close1 = Close[1];
    double close2 = Close[2];
    
    // Fetch MACD and ATR data
    double macdMain = iMACD(Symbol(), 0, InpMacdFast, InpMacdSlow, InpMacdSignal, PRICE_CLOSE, MODE_MAIN, 1);
    double macdSig  = iMACD(Symbol(), 0, InpMacdFast, InpMacdSlow, InpMacdSignal, PRICE_CLOSE, MODE_SIGNAL, 1);
    double hist1    = macdMain - macdSig; // MACD Histogram
    
    double atr = iATR(Symbol(), 0, InpAtrPeriod, 1);
    
    // Entry Conditions
    bool longCross  = (close1 > alma1 && close2 <= alma2);
    bool shortCross = (close1 < alma1 && close2 >= alma2);
    
    if(longCross && hist1 > 0) {
        double sl = NormalizeDouble(close1 - (atr * InpSlAtrMult), Digits);
        double tp = NormalizeDouble(close1 + ((close1 - sl) * InpRrRatio), Digits);
        int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, 3, sl, tp, "ALMA Long", InpMagic, 0, clrGreen);
        if(ticket > 0) lastBar = Time[0];
    }
    else if(shortCross && hist1 < 0) {
        double sl = NormalizeDouble(close1 + (atr * InpSlAtrMult), Digits);
        double tp = NormalizeDouble(close1 - ((sl - close1) * InpRrRatio), Digits);
        int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, 3, sl, tp, "ALMA Short", InpMagic, 0, clrRed);
        if(ticket > 0) lastBar = Time[0];
    }
}

// Custom Gaussian Distribution ALMA Calculation
double CalculateALMA(int shift, int length, double offset, double sigma) {
    double m = offset * (length - 1);
    double s = length / sigma;
    double wSum = 0.0, res = 0.0;
    
    for(int i = 0; i < length; i++) {
        double w = MathExp(-(MathPow(i - m, 2)) / (2 * s * s));
        // Map i=0 to the oldest bar, and i=(length-1) to the newest bar in the window
        res += Close[shift + (length - 1 - i)] * w; 
        wSum += w;
    }
    return wSum > 0 ? res / wSum : 0.0;
}
Recommended broker for automated trading & scalping IC Markets
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Post by FTtrader »

Trailing the stop loss behind a dynamic moving average like ALMA is an excellent way to capture extended momentum phases while still dynamically protecting unrealized profits.

To implement this, we need to make three structural changes to the logic:

Remove the Hard Take Profit: When placing the initial trade, the Take Profit (tp) is set to 0.

Add a Trailing Buffer: A point-based buffer is added so the stop loss isn't placed exactly on the ALMA line, preventing premature stop-outs from minor liquidity sweeps.

Continuous SL Modification: A trailing function continuously evaluates the last closed bar's ALMA (ALMA[1]) and pulls the Stop Loss forward if the line moves favorably.

Here are the complete, updated scripts for both platforms.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Post by FTtrader »

MQL5 Version (MT5)

Code: Select all

//+------------------------------------------------------------------+
//|                                     ALMA_Pro_Trailing_MT5.mq5    |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade trade;

// --- Inputs ---
input int    InpAlmaLength    = 9;       // ALMA Window Size
input double InpAlmaOffset    = 0.85;    // ALMA Offset
input double InpAlmaSigma     = 6.0;     // ALMA Sigma
input int    InpMacdFast      = 12;      // MACD Fast
input int    InpMacdSlow      = 26;      // MACD Slow
input int    InpMacdSignal    = 9;       // MACD Signal
input int    InpAtrPeriod     = 14;      // ATR Period
input double InpSlAtrMult     = 1.5;     // Initial SL ATR Multiplier
input int    InpTrailBuffer   = 10;      // Trailing Buffer behind ALMA (in Points)
input double InpLotSize       = 0.1;     // Fixed Lot Size
input ulong  InpMagic         = 123456;  // Magic Number

int macdHandle, atrHandle;

int OnInit() {
    trade.SetExpertMagicNumber(InpMagic);
    macdHandle = iMACD(_Symbol, _Period, InpMacdFast, InpMacdSlow, InpMacdSignal, PRICE_CLOSE);
    atrHandle = iATR(_Symbol, _Period, InpAtrPeriod);
    
    if(macdHandle == INVALID_HANDLE || atrHandle == INVALID_HANDLE) return(INIT_FAILED);
    return(INIT_SUCCEEDED);
}

void OnTick() {
    // 1. Manage Trailing Stop on every tick using ALMA[1]
    double alma1 = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    if(alma1 != 0 && PositionsTotal() > 0) {
        ManageTrailingStop(alma1);
    }

    // 2. Entry Logic executes only on bar close
    if(!IsNewBar()) return; 
    if(PositionsTotal() > 0) return; 
    
    double alma2 = CalculateALMA(2, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    if(alma2 == 0) return;
    
    double close1 = iClose(_Symbol, _Period, 1);
    double close2 = iClose(_Symbol, _Period, 2);
    
    double macdMain[1], macdSignal[1], atr[1];
    if(CopyBuffer(macdHandle, 0, 1, 1, macdMain) <= 0) return;
    if(CopyBuffer(macdHandle, 1, 1, 1, macdSignal) <= 0) return;
    if(CopyBuffer(atrHandle, 0, 1, 1, atr) <= 0) return;
    
    double hist1 = macdMain[0] - macdSignal[0]; 
    
    bool longCross  = (close1 > alma1 && close2 <= alma2);
    bool shortCross = (close1 < alma1 && close2 >= alma2);
    
    // Note: Take Profit is set to 0.0 to allow ALMA trailing
    if(longCross && hist1 > 0) {
        double sl = NormalizeDouble(close1 - (atr[0] * InpSlAtrMult), _Digits);
        trade.Buy(InpLotSize, _Symbol, 0, sl, 0.0, "ALMA Long");
    }
    else if(shortCross && hist1 < 0) {
        double sl = NormalizeDouble(close1 + (atr[0] * InpSlAtrMult), _Digits);
        trade.Sell(InpLotSize, _Symbol, 0, sl, 0.0, "ALMA Short");
    }
}

// Handles dynamic moving of the Stop Loss based on ALMA
void ManageTrailingStop(double currentAlma) {
    for(int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagic) {
            
            double currentSL = PositionGetDouble(POSITION_SL);
            ulong  type      = PositionGetInteger(POSITION_TYPE);
            
            if(type == POSITION_TYPE_BUY) {
                double newSL = NormalizeDouble(currentAlma - (InpTrailBuffer * _Point), _Digits);
                // Move SL up if the new ALMA calculation provides a tighter stop, ensuring it stays below current Bid
                if(newSL > currentSL && newSL < SymbolInfoDouble(_Symbol, SYMBOL_BID)) {
                    trade.PositionModify(ticket, newSL, 0.0);
                }
            } 
            else if(type == POSITION_TYPE_SELL) {
                double newSL = NormalizeDouble(currentAlma + (InpTrailBuffer * _Point), _Digits);
                // Move SL down if the new ALMA calculation provides a tighter stop, ensuring it stays above current Ask
                if((newSL < currentSL || currentSL == 0.0) && newSL > SymbolInfoDouble(_Symbol, SYMBOL_ASK)) {
                    trade.PositionModify(ticket, newSL, 0.0);
                }
            }
        }
    }
}

double CalculateALMA(int shift, int length, double offset, double sigma) {
    double m = offset * (length - 1);
    double s = length / sigma;
    double wSum = 0.0, res = 0.0;
    
    double prices[];
    if(CopyClose(_Symbol, _Period, shift, length, prices) < length) return 0.0;
    
    for(int i = 0; i < length; i++) {
        double w = MathExp(-(MathPow(i - m, 2)) / (2 * s * s));
        res += prices[i] * w; 
        wSum += w;
    }
    return wSum > 0 ? res / wSum : 0.0;
}

bool IsNewBar() {
    static datetime lastBar = 0;
    datetime currBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
    if(lastBar != currBar) {
        lastBar = currBar;
        return true;
    }
    return false;
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Post by FTtrader »

MQL4 Version (MT4)

Code: Select all

//+------------------------------------------------------------------+
//|                                     ALMA_Pro_Trailing_MT4.mq4    |
//+------------------------------------------------------------------+
#property strict

// --- Inputs ---
input int    InpAlmaLength    = 9;       // ALMA Window Size
input double InpAlmaOffset    = 0.85;    // ALMA Offset
input double InpAlmaSigma     = 6.0;     // ALMA Sigma
input int    InpMacdFast      = 12;      // MACD Fast
input int    InpMacdSlow      = 26;      // MACD Slow
input int    InpMacdSignal    = 9;       // MACD Signal
input int    InpAtrPeriod     = 14;      // ATR Period
input double InpSlAtrMult     = 1.5;     // Initial SL ATR Multiplier
input int    InpTrailBuffer   = 10;      // Trailing Buffer behind ALMA (in Points)
input double InpLotSize       = 0.1;     // Fixed Lot Size
input int    InpMagic         = 123456;  // Magic Number

void OnTick() {
    // 1. Manage Trailing Stop on every tick
    double alma1 = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    if(alma1 != 0 && OrdersTotal() > 0) {
        ManageTrailingStop(alma1);
    }

    // 2. Entry Logic executes only on bar close
    static datetime lastBar = 0;
    if(Time[0] == lastBar) return; 
    if(OrdersTotal() > 0) return; 
    
    double alma2 = CalculateALMA(2, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    
    double close1 = Close[1];
    double close2 = Close[2];
    
    double macdMain = iMACD(Symbol(), 0, InpMacdFast, InpMacdSlow, InpMacdSignal, PRICE_CLOSE, MODE_MAIN, 1);
    double macdSig  = iMACD(Symbol(), 0, InpMacdFast, InpMacdSlow, InpMacdSignal, PRICE_CLOSE, MODE_SIGNAL, 1);
    double hist1    = macdMain - macdSig; 
    
    double atr = iATR(Symbol(), 0, InpAtrPeriod, 1);
    
    bool longCross  = (close1 > alma1 && close2 <= alma2);
    bool shortCross = (close1 < alma1 && close2 >= alma2);
    
    // Note: Take Profit is set to 0.0 to allow ALMA trailing
    if(longCross && hist1 > 0) {
        double sl = NormalizeDouble(close1 - (atr * InpSlAtrMult), Digits);
        int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, 3, sl, 0.0, "ALMA Long", InpMagic, 0, clrGreen);
        if(ticket > 0) lastBar = Time[0];
    }
    else if(shortCross && hist1 < 0) {
        double sl = NormalizeDouble(close1 + (atr * InpSlAtrMult), Digits);
        int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, 3, sl, 0.0, "ALMA Short", InpMagic, 0, clrRed);
        if(ticket > 0) lastBar = Time[0];
    }
}

// Handles dynamic moving of the Stop Loss based on ALMA
void ManageTrailingStop(double currentAlma) {
    for(int i = OrdersTotal() - 1; i >= 0; i--) {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagic) {
                
                double currentSL = OrderStopLoss();
                
                if(OrderType() == OP_BUY) {
                    double newSL = NormalizeDouble(currentAlma - (InpTrailBuffer * Point), Digits);
                    if(newSL > currentSL && newSL < Bid) {
                        OrderModify(OrderTicket(), OrderOpenPrice(), newSL, 0.0, 0, clrBlue);
                    }
                } 
                else if(OrderType() == OP_SELL) {
                    double newSL = NormalizeDouble(currentAlma + (InpTrailBuffer * Point), Digits);
                    if((newSL < currentSL || currentSL == 0.0) && newSL > Ask) {
                        OrderModify(OrderTicket(), OrderOpenPrice(), newSL, 0.0, 0, clrRed);
                    }
                }
            }
        }
    }
}

double CalculateALMA(int shift, int length, double offset, double sigma) {
    double m = offset * (length - 1);
    double s = length / sigma;
    double wSum = 0.0, res = 0.0;
    
    for(int i = 0; i < length; i++) {
        double w = MathExp(-(MathPow(i - m, 2)) / (2 * s * s));
        res += Close[shift + (length - 1 - i)] * w; 
        wSum += w;
    }
    return wSum > 0 ? res / wSum : 0.0;
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Post by FTtrader »

To implement partial profit taking, the EA must calculate the initial risk distance ($1.0R$) at entry, project the $1.5R$ target price, close 50% of the position volume once price touches that target, and leave the remaining volume open for the ALMA trailing stop.

Core Architectural Considerations

Lot Step & Min Lot Normalization: Brokers reject orders if closed volume does not align with SYMBOL_VOLUME_STEP or falls below SYMBOL_VOLUME_MIN. Initial volume must be at least $2 \times \text{Min Lot}$ (e.g., $0.02$ if min lot is $0.01$).

Breakeven Migration: When securing the first 50% profit, the EA optionally shifts the Stop Loss to the original entry price (OpenPrice) to guarantee a risk-free runner.

State Persistence: Checking whether the current active volume is less than InpLotSize ensures the EA recognizes a partial close has already happened, even across chart reloads or terminal restarts.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Post by FTtrader »

MQL5 Implementation (MT5)

In MT5, partial closures are handled via trade.PositionClosePartial(ticket, volume) [cite: 1.1.1].

Code: Select all

//+------------------------------------------------------------------+
//|                                  ALMA_Pro_PartialClose_MT5.mq5   |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
CTrade trade;

// --- Inputs ---
input int    InpAlmaLength    = 9;       // ALMA Window Size
input double InpAlmaOffset    = 0.85;    // ALMA Offset
input double InpAlmaSigma     = 6.0;     // ALMA Sigma
input int    InpMacdFast      = 12;      // MACD Fast
input int    InpMacdSlow      = 26;      // MACD Slow
input int    InpMacdSignal    = 9;       // MACD Signal
input int    InpAtrPeriod     = 14;      // ATR Period
input double InpSlAtrMult     = 1.5;     // Initial SL ATR Multiplier
input int    InpTrailBuffer   = 10;      // Trailing Buffer behind ALMA (Points)

// --- Partial Close Inputs ---
input double InpLotSize       = 0.10;    // Initial Lot Size (Min 2x broker min lot)
input double InpPartialRR     = 1.5;     // Partial Profit Target (in R)
input double InpPartialPct    = 0.5;     // Percentage of position to close (0.5 = 50%)
input bool   InpMoveToBE      = true;    // Move SL to Breakeven after partial close?
input ulong  InpMagic         = 123456;  // Magic Number

int    macdHandle, atrHandle;
double g_target15R       = 0.0;
bool   g_isPartialClosed = false;

int OnInit() {
    trade.SetExpertMagicNumber(InpMagic);
    macdHandle = iMACD(_Symbol, _Period, InpMacdFast, InpMacdSlow, InpMacdSignal, PRICE_CLOSE);
    atrHandle  = iATR(_Symbol, _Period, InpAtrPeriod);
    
    if(macdHandle == INVALID_HANDLE || atrHandle == INVALID_HANDLE) return(INIT_FAILED);
    return(INIT_SUCCEEDED);
}

void OnTick() {
    // 1. Position Management & Trailing Engine
    if(PositionsTotal() == 0) {
        g_target15R       = 0.0;
        g_isPartialClosed = false;
    } else {
        CheckPartialClose();
        double alma1 = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
        if(alma1 != 0) ManageTrailingStop(alma1);
    }

    // 2. Bar Close Entry Checks
    if(!IsNewBar()) return; 
    if(PositionsTotal() > 0) return; 
    
    double alma1  = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    double alma2  = CalculateALMA(2, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    if(alma1 == 0 || alma2 == 0) return;
    
    double close1 = iClose(_Symbol, _Period, 1);
    double close2 = iClose(_Symbol, _Period, 2);
    
    double macdMain[1], macdSignal[1], atr[1];
    if(CopyBuffer(macdHandle, 0, 1, 1, macdMain) <= 0) return;
    if(CopyBuffer(macdHandle, 1, 1, 1, macdSignal) <= 0) return;
    if(CopyBuffer(atrHandle, 0, 1, 1, atr) <= 0) return;
    
    double hist1 = macdMain[0] - macdSignal[0]; 
    
    bool longCross  = (close1 > alma1 && close2 <= alma2);
    bool shortCross = (close1 < alma1 && close2 >= alma2);
    
    if(longCross && hist1 > 0) {
        double sl   = NormalizeDouble(close1 - (atr[0] * InpSlAtrMult), _Digits);
        double risk = close1 - sl;
        g_target15R = NormalizeDouble(close1 + (risk * InpPartialRR), _Digits);
        g_isPartialClosed = false;
        trade.Buy(InpLotSize, _Symbol, 0, sl, 0.0, "ALMA Long");
    }
    else if(shortCross && hist1 < 0) {
        double sl   = NormalizeDouble(close1 + (atr[0] * InpSlAtrMult), _Digits);
        double risk = sl - close1;
        g_target15R = NormalizeDouble(close1 - (risk * InpPartialRR), _Digits);
        g_isPartialClosed = false;
        trade.Sell(InpLotSize, _Symbol, 0, sl, 0.0, "ALMA Short");
    }
}

// Executes Partial Close and shifts SL to Breakeven
void CheckPartialClose() {
    if(g_isPartialClosed || g_target15R <= 0.0) return;
    
    for(int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagic) {
            
            double currentVolume = PositionGetDouble(POSITION_VOLUME);
            ulong  type          = PositionGetInteger(POSITION_TYPE);
            double openPrice     = PositionGetDouble(POSITION_PRICE_OPEN);
            
            // If volume has already decreased, state is already partially closed
            if(currentVolume < InpLotSize) {
                g_isPartialClosed = true;
                return;
            }
            
            double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
            double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
            
            bool targetHit = (type == POSITION_TYPE_BUY && bid >= g_target15R) ||
                             (type == POSITION_TYPE_SELL && ask <= g_target15R);
                             
            if(targetHit) {
                double step     = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
                double minLot   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
                double closeVol = MathFloor((currentVolume * InpPartialPct) / step) * step;
                closeVol        = NormalizeDouble(closeVol, 2);
                
                if(closeVol >= minLot && trade.PositionClosePartial(ticket, closeVol)) {
                    g_isPartialClosed = true;
                    if(InpMoveToBE) {
                        trade.PositionModify(ticket, openPrice, 0.0);
                    }
                }
            }
        }
    }
}

void ManageTrailingStop(double currentAlma) {
    for(int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagic) {
            double currentSL = PositionGetDouble(POSITION_SL);
            ulong  type      = PositionGetInteger(POSITION_TYPE);
            
            if(type == POSITION_TYPE_BUY) {
                double newSL = NormalizeDouble(currentAlma - (InpTrailBuffer * _Point), _Digits);
                if(newSL > currentSL && newSL < SymbolInfoDouble(_Symbol, SYMBOL_BID)) {
                    trade.PositionModify(ticket, newSL, 0.0);
                }
            } 
            else if(type == POSITION_TYPE_SELL) {
                double newSL = NormalizeDouble(currentAlma + (InpTrailBuffer * _Point), _Digits);
                if((newSL < currentSL || currentSL == 0.0) && newSL > SymbolInfoDouble(_Symbol, SYMBOL_ASK)) {
                    trade.PositionModify(ticket, newSL, 0.0);
                }
            }
        }
    }
}

double CalculateALMA(int shift, int length, double offset, double sigma) {
    double m = offset * (length - 1);
    double s = length / sigma;
    double wSum = 0.0, res = 0.0;
    double prices[];
    if(CopyClose(_Symbol, _Period, shift, length, prices) < length) return 0.0;
    
    for(int i = 0; i < length; i++) {
        double w = MathExp(-(MathPow(i - m, 2)) / (2 * s * s));
        res += prices[i] * w; 
        wSum += w;
    }
    return wSum > 0 ? res / wSum : 0.0;
}

bool IsNewBar() {
    static datetime lastBar = 0;
    datetime currBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
    if(lastBar != currBar) {
        lastBar = currBar;
        return true;
    }
    return false;
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Post by FTtrader »

MQL4 Implementation (MT4)

In MT4, passing a fractional volume to OrderClose() executes a partial close and leaves the remaining volume open under the same magic number.

Code: Select all

//+------------------------------------------------------------------+
//|                                  ALMA_Pro_PartialClose_MT4.mq4   |
//+------------------------------------------------------------------+
#property strict

// --- Inputs ---
input int    InpAlmaLength    = 9;       // ALMA Window Size
input double InpAlmaOffset    = 0.85;    // ALMA Offset
input double InpAlmaSigma     = 6.0;     // ALMA Sigma
input int    InpMacdFast      = 12;      // MACD Fast
input int    InpMacdSlow      = 26;      // MACD Slow
input int    InpMacdSignal    = 9;       // MACD Signal
input int    InpAtrPeriod     = 14;      // ATR Period
input double InpSlAtrMult     = 1.5;     // Initial SL ATR Multiplier
input int    InpTrailBuffer   = 10;      // Trailing Buffer behind ALMA (Points)

// --- Partial Close Inputs ---
input double InpLotSize       = 0.10;    // Initial Lot Size (Min 2x broker min lot)
input double InpPartialRR     = 1.5;     // Partial Profit Target (in R)
input double InpPartialPct    = 0.5;     // Percentage of position to close (0.5 = 50%)
input bool   InpMoveToBE      = true;    // Move SL to Breakeven after partial close?
input int    InpMagic         = 123456;  // Magic Number

double g_target15R       = 0.0;
bool   g_isPartialClosed = false;

void OnTick() {
    // 1. Position Management & Trailing Engine
    if(OrdersTotal() == 0) {
        g_target15R       = 0.0;
        g_isPartialClosed = false;
    } else {
        CheckPartialClose();
        double alma1 = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
        if(alma1 != 0) ManageTrailingStop(alma1);
    }

    // 2. Bar Close Entry Checks
    static datetime lastBar = 0;
    if(Time[0] == lastBar) return; 
    if(OrdersTotal() > 0) return; 
    
    double alma1 = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    double alma2 = CalculateALMA(2, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    
    double close1 = Close[1];
    double close2 = Close[2];
    
    double macdMain = iMACD(Symbol(), 0, InpMacdFast, InpMacdSlow, InpMacdSignal, PRICE_CLOSE, MODE_MAIN, 1);
    double macdSig  = iMACD(Symbol(), 0, InpMacdFast, InpMacdSlow, InpMacdSignal, PRICE_CLOSE, MODE_SIGNAL, 1);
    double hist1    = macdMain - macdSig; 
    double atr      = iATR(Symbol(), 0, InpAtrPeriod, 1);
    
    bool longCross  = (close1 > alma1 && close2 <= alma2);
    bool shortCross = (close1 < alma1 && close2 >= alma2);
    
    if(longCross && hist1 > 0) {
        double sl   = NormalizeDouble(close1 - (atr * InpSlAtrMult), Digits);
        double risk = close1 - sl;
        g_target15R = NormalizeDouble(close1 + (risk * InpPartialRR), Digits);
        g_isPartialClosed = false;
        
        int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, 3, sl, 0.0, "ALMA Long", InpMagic, 0, clrGreen);
        if(ticket > 0) lastBar = Time[0];
    }
    else if(shortCross && hist1 < 0) {
        double sl   = NormalizeDouble(close1 + (atr * InpSlAtrMult), Digits);
        double risk = sl - close1;
        g_target15R = NormalizeDouble(close1 - (risk * InpPartialRR), Digits);
        g_isPartialClosed = false;
        
        int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, 3, sl, 0.0, "ALMA Short", InpMagic, 0, clrRed);
        if(ticket > 0) lastBar = Time[0];
    }
}

// Executes Partial Close and shifts SL to Breakeven
void CheckPartialClose() {
    if(g_isPartialClosed || g_target15R <= 0.0) return;
    
    for(int i = OrdersTotal() - 1; i >= 0; i--) {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagic) {
                
                double currentLots = OrderLots();
                int    type        = OrderType();
                double openPrice   = OrderOpenPrice();
                
                if(currentLots < InpLotSize) {
                    g_isPartialClosed = true;
                    return;
                }
                
                bool targetHit = (type == OP_BUY && Bid >= g_target15R) ||
                                 (type == OP_SELL && Ask <= g_target15R);
                                 
                if(targetHit) {
                    double step     = MarketInfo(Symbol(), MODE_LOTSTEP);
                    double minLot   = MarketInfo(Symbol(), MODE_MINLOT);
                    double closeVol = MathFloor((currentLots * InpPartialPct) / step) * step;
                    closeVol        = NormalizeDouble(closeVol, 2);
                    
                    if(closeVol >= minLot) {
                        double closePrice = (type == OP_BUY) ? Bid : Ask;
                        if(OrderClose(OrderTicket(), closeVol, closePrice, 3, clrYellow)) {
                            g_isPartialClosed = true;
                            if(InpMoveToBE) {
                                // Select active order ticket again to apply BE to the runner
                                OrderModify(OrderTicket(), openPrice, openPrice, 0.0, 0, clrBlue);
                            }
                        }
                    }
                }
            }
        }
    }
}

void ManageTrailingStop(double currentAlma) {
    for(int i = OrdersTotal() - 1; i >= 0; i--) {
        if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
            if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagic) {
                double currentSL = OrderStopLoss();
                
                if(OrderType() == OP_BUY) {
                    double newSL = NormalizeDouble(currentAlma - (InpTrailBuffer * Point), Digits);
                    if(newSL > currentSL && newSL < Bid) {
                        OrderModify(OrderTicket(), OrderOpenPrice(), newSL, 0.0, 0, clrBlue);
                    }
                } 
                else if(OrderType() == OP_SELL) {
                    double newSL = NormalizeDouble(currentAlma + (InpTrailBuffer * Point), Digits);
                    if((newSL < currentSL || currentSL == 0.0) && newSL > Ask) {
                        OrderModify(OrderTicket(), OrderOpenPrice(), newSL, 0.0, 0, clrRed);
                    }
                }
            }
        }
    }
}

double CalculateALMA(int shift, int length, double offset, double sigma) {
    double m = offset * (length - 1);
    double s = length / sigma;
    double wSum = 0.0, res = 0.0;
    
    for(int i = 0; i < length; i++) {
        double w = MathExp(-(MathPow(i - m, 2)) / (2 * s * s));
        res += Close[shift + (length - 1 - i)] * w; 
        wSum += w;
    }
    return wSum > 0 ? res / wSum : 0.0;
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Post by FTtrader »

Adding a maximum spread filter is absolutely critical for 1-minute scalping. During the daily rollover (midnight server time) or around major macroeconomic news releases, liquidity drops and spreads can widen by 500% to 1000%. On an M1 timeframe, taking a trade during these spikes will instantly trigger your stop loss or completely ruin your risk-to-reward mathematics.

In both MQL4 and MQL5, the most efficient way to handle this is by querying the broker's current dynamic spread in real-time using SymbolInfoInteger with the SYMBOL_SPREAD property [cite: 1.1.1].

Here is how to modify your existing scripts to block trades when the spread is too high, while also logging a print statement to your journal so you know why a valid ALMA cross was ignored.

1. Add the Input Parameter
First, add this new input variable at the top of your script with your other inputs.

Note: This is measured in Points (pipettes). If you are on a standard 5-digit broker and want a max spread of 1.5 pips, you must enter 15 points.

Code: Select all

input int InpMaxSpreadPoints = 15; // Maximum Allowed Spread (in Points)
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Post by FTtrader »

2. Update the MQL5 OnTick() Function

Find your OnTick() function in the MQL5 code and insert the spread check directly into the bar close entry logic.

Code: Select all

void OnTick() {
    // 1. Position Management & Trailing Engine (Leave this exactly as it is)
    if(PositionsTotal() == 0) {
        g_target15R       = 0.0;
        g_isPartialClosed = false;
    } else {
        CheckPartialClose();
        double alma1 = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
        if(alma1 != 0) ManageTrailingStop(alma1);
    }

    // 2. Bar Close Entry Checks
    if(!IsNewBar()) return; 
    if(PositionsTotal() > 0) return; 
    
    double alma1  = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    double alma2  = CalculateALMA(2, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    if(alma1 == 0 || alma2 == 0) return;
    
    // ---> FETCH CURRENT SPREAD IN POINTS <---
    int currentSpread = (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
    bool isSpreadOk   = (currentSpread <= InpMaxSpreadPoints);
    
    double close1 = iClose(_Symbol, _Period, 1);
    double close2 = iClose(_Symbol, _Period, 2);
    
    double macdMain[1], macdSignal[1], atr[1];
    if(CopyBuffer(macdHandle, 0, 1, 1, macdMain) <= 0) return;
    if(CopyBuffer(macdHandle, 1, 1, 1, macdSignal) <= 0) return;
    if(CopyBuffer(atrHandle, 0, 1, 1, atr) <= 0) return;
    
    double hist1 = macdMain[0] - macdSignal[0]; 
    
    bool longCross  = (close1 > alma1 && close2 <= alma2);
    bool shortCross = (close1 < alma1 && close2 >= alma2);
    
    // ---> APPLY SPREAD CHECK TO EXECUTION <---
    if(longCross && hist1 > 0) {
        if(isSpreadOk) {
            double sl   = NormalizeDouble(close1 - (atr[0] * InpSlAtrMult), _Digits);
            double risk = close1 - sl;
            g_target15R = NormalizeDouble(close1 + (risk * InpPartialRR), _Digits);
            g_isPartialClosed = false;
            trade.Buy(InpLotSize, _Symbol, 0, sl, 0.0, "ALMA Long");
        } else {
            Print("Long ALMA Cross ignored: Spread (", currentSpread, ") exceeds max limit (", InpMaxSpreadPoints, ").");
        }
    }
    else if(shortCross && hist1 < 0) {
        if(isSpreadOk) {
            double sl   = NormalizeDouble(close1 + (atr[0] * InpSlAtrMult), _Digits);
            double risk = sl - close1;
            g_target15R = NormalizeDouble(close1 - (risk * InpPartialRR), _Digits);
            g_isPartialClosed = false;
            trade.Sell(InpLotSize, _Symbol, 0, sl, 0.0, "ALMA Short");
        } else {
            Print("Short ALMA Cross ignored: Spread (", currentSpread, ") exceeds max limit (", InpMaxSpreadPoints, ").");
        }
    }
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: The Ultimate 1 Minute Forex Scalping Strategy: ALMA Indicator Explained

Post by FTtrader »

3. Update the MQL4 OnTick() Function

Modern MQL4 supports the same SymbolInfoInteger syntax as MQL5 [cite: 1.1.6], making it seamless to drop in. Replace your MQL4 bar close logic with this updated check:

Code: Select all

void OnTick() {
    // 1. Position Management & Trailing Engine (Leave this exactly as it is)
    if(OrdersTotal() == 0) {
        g_target15R       = 0.0;
        g_isPartialClosed = false;
    } else {
        CheckPartialClose();
        double alma1 = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
        if(alma1 != 0) ManageTrailingStop(alma1);
    }

    // 2. Bar Close Entry Checks
    static datetime lastBar = 0;
    if(Time[0] == lastBar) return; 
    if(OrdersTotal() > 0) return; 
    
    double alma1 = CalculateALMA(1, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    double alma2 = CalculateALMA(2, InpAlmaLength, InpAlmaOffset, InpAlmaSigma);
    
    // ---> FETCH CURRENT SPREAD IN POINTS <---
    int currentSpread = (int)SymbolInfoInteger(Symbol(), SYMBOL_SPREAD);
    bool isSpreadOk   = (currentSpread <= InpMaxSpreadPoints);
    
    double close1 = Close[1];
    double close2 = Close[2];
    
    double macdMain = iMACD(Symbol(), 0, InpMacdFast, InpMacdSlow, InpMacdSignal, PRICE_CLOSE, MODE_MAIN, 1);
    double macdSig  = iMACD(Symbol(), 0, InpMacdFast, InpMacdSlow, InpMacdSignal, PRICE_CLOSE, MODE_SIGNAL, 1);
    double hist1    = macdMain - macdSig; 
    double atr      = iATR(Symbol(), 0, InpAtrPeriod, 1);
    
    bool longCross  = (close1 > alma1 && close2 <= alma2);
    bool shortCross = (close1 < alma1 && close2 >= alma2);
    
    // ---> APPLY SPREAD CHECK TO EXECUTION <---
    if(longCross && hist1 > 0) {
        if(isSpreadOk) {
            double sl   = NormalizeDouble(close1 - (atr * InpSlAtrMult), Digits);
            double risk = close1 - sl;
            g_target15R = NormalizeDouble(close1 + (risk * InpPartialRR), Digits);
            g_isPartialClosed = false;
            
            int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, 3, sl, 0.0, "ALMA Long", InpMagic, 0, clrGreen);
            if(ticket > 0) lastBar = Time[0];
        } else {
            Print("Long ALMA Cross ignored: Spread (", currentSpread, ") exceeds max limit (", InpMaxSpreadPoints, ").");
        }
    }
    else if(shortCross && hist1 < 0) {
        if(isSpreadOk) {
            double sl   = NormalizeDouble(close1 + (atr * InpSlAtrMult), Digits);
            double risk = sl - close1;
            g_target15R = NormalizeDouble(close1 - (risk * InpPartialRR), Digits);
            g_isPartialClosed = false;
            
            int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, 3, sl, 0.0, "ALMA Short", InpMagic, 0, clrRed);
            if(ticket > 0) lastBar = Time[0];
        } else {
            Print("Short ALMA Cross ignored: Spread (", currentSpread, ") exceeds max limit (", InpMaxSpreadPoints, ").");
        }
    }
}
Post Reply