Summary: Bitcoin Trend Armor EA is a stable trend-following MQL4 expert advisor for BTCUSD. It combines dual EMA trend detection, ADX strength filter and ATR-based dynamic stops. Suitable for H4 timeframe.




Bitcoin Trend Armor EA is designed for stable, trend-following automated trading on Bitcoin (BTCUSD). Unlike high-frequency or grid strategies, this EA focuses on capturing sustained medium-term trends while avoiding false breakouts and high-volatility whipsaws. The system uses dual EMA crossover for trend direction, ADX to filter out weak/non-trending markets, and ATR for dynamic stop loss and take profit. A unique volatility-based position sizing reduces exposure when Bitcoin volatility spikes. The EA includes maximum daily drawdown protection, spread control, and weekend gap avoidance.

Recommended Timeframe: H4
Trading Logic:
  • Trend Detection: EMA(20) and EMA(50) crossover confirms trend direction.

  • Trend Strength: ADX(14) must be above 25 to ensure a trending market.

  • Entry Signal: After crossover confirmation, wait for pullback to EMA20 (price touches or approaches) with momentum (MACD histogram positive for longs, negative for shorts).

  • Risk Management: Dynamic stop loss at 2x ATR from entry, take profit at 4x ATR. Trailing stop activates after 1.5x ATR profit. Position size reduces when ATR > average ATR × 1.5.


  • ``mql4
    //+------------------------------------------------------------------+
    //| BitcoinTrendArmorEA.mq4 |
    //+------------------------------------------------------------------+
    #property copyright ""
    #property link ""
    #property version "1.00"
    #property strict

    //--- input parameters with comments
    input double BaseLotSize = 0.01; // Base lot size (0.01 BTC)
    input int FastEMAPeriod = 20; // Fast EMA period for trend detection
    input int SlowEMAPeriod = 50; // Slow EMA period for trend detection
    input int ADXPeriod = 14; // ADX period for trend strength filter
    input int ADXThreshold = 25; // Minimum ADX value to allow trading
    input int MACDFast = 12; // MACD fast EMA period
    input int MACDSlow = 26; // MACD slow EMA period
    input int MACDSignal = 9; // MACD signal line period
    input int ATRPeriod = 14; // ATR period for dynamic stops
    input double ATRStopMultiplier = 2.0; // Stop loss as multiple of ATR
    input double ATRTakeMultiplier = 4.0; // Take profit as multiple of ATR
    input double TrailingStartMulti = 1.5; // Trailing starts after profit reaches this ATR multiple
    input double TrailingStepMulti = 0.8; // Trailing step in ATR multiples
    input double VolatilityLotReduce = 1.5; // Reduce lot size if ATR > avgATR this value
    input int MagicNumber = 202417; // Unique EA identifier
    input int MaxSpread = 120; // Maximum allowed spread in points (BTC spread is wider)
    input double MaxDailyDrawdown = 6.0; // Maximum daily drawdown percentage
    input bool UseSaturdayClose = true; // Close trades before Saturday 22:00 GMT

    //--- global variables
    double dailyStartEquity = 0;
    datetime lastBarTime = 0;
    bool saturdayCloseExecuted = false;
    double avgATR = 0;
    double baseATR = 0;

    //+------------------------------------------------------------------+
    //| Expert initialization function |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    dailyStartEquity = AccountEquity();
    lastBarTime = 0;
    saturdayCloseExecuted = false;
    avgATR = iATR(Symbol(), PERIOD_H4, ATRPeriod, 1);
    if(avgATR <= 0) avgATR = 800
    Point;
    baseATR = avgATR;
    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| Expert deinitialization function |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    Comment("");
    }

    //+------------------------------------------------------------------+
    //| Get dynamic lot size based on current volatility |
    //+------------------------------------------------------------------+
    double GetDynamicLotSize(double currentATR)
    {
    double lot = BaseLotSize;
    if(currentATR > avgATR VolatilityLotReduce && avgATR > 0)
    {
    lot = BaseLotSize
    (avgATR / currentATR);
    }
    if(lot < 0.005) lot = 0.005;
    if(lot > 0.5) lot = 0.5;
    lot = NormalizeDouble(lot, 2);
    return lot;
    }

    //+------------------------------------------------------------------+
    //| Check trend direction using EMA crossover |
    //+------------------------------------------------------------------+
    int GetTrendDirection()
    {
    double fastEMA = iMA(Symbol(), PERIOD_H4, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
    double slowEMA = iMA(Symbol(), PERIOD_H4, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
    double fastEMAPrev = iMA(Symbol(), PERIOD_H4, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 2);
    double slowEMAPrev = iMA(Symbol(), PERIOD_H4, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 2);

    // Uptrend: fastEMA above slowEMA and recently crossed or already above
    if(fastEMA > slowEMA && fastEMAPrev <= slowEMAPrev)
    return 1; // fresh bullish crossover
    if(fastEMA > slowEMA)
    return 2; // established uptrend
    // Downtrend: fastEMA below slowEMA
    if(fastEMA < slowEMA && fastEMAPrev >= slowEMAPrev)
    return -1; // fresh bearish crossover
    if(fastEMA < slowEMA)
    return -2; // established downtrend

    return 0; // no clear trend
    }

    //+------------------------------------------------------------------+
    //| Check MACD momentum confirmation |
    //+------------------------------------------------------------------+
    bool IsMACDBullish()
    {
    double macdMain = iMACD(Symbol(), PERIOD_H4, MACDFast, MACDSlow, MACDSignal, PRICE_CLOSE, MODE_MAIN, 1);
    double macdSignal = iMACD(Symbol(), PERIOD_H4, MACDFast, MACDSlow, MACDSignal, PRICE_CLOSE, MODE_SIGNAL, 1);
    double macdHist = macdMain - macdSignal;
    double macdHistPrev = iMACD(Symbol(), PERIOD_H4, MACDFast, MACDSlow, MACDSignal, PRICE_CLOSE, MODE_MAIN, 2) -
    iMACD(Symbol(), PERIOD_H4, MACDFast, MACDSlow, MACDSignal, PRICE_CLOSE, MODE_SIGNAL, 2);

    return (macdHist > 0 && macdHist > macdHistPrev);
    }

    bool IsMACDBearish()
    {
    double macdMain = iMACD(Symbol(), PERIOD_H4, MACDFast, MACDSlow, MACDSignal, PRICE_CLOSE, MODE_MAIN, 1);
    double macdSignal = iMACD(Symbol(), PERIOD_H4, MACDFast, MACDSlow, MACDSignal, PRICE_CLOSE, MODE_SIGNAL, 1);
    double macdHist = macdMain - macdSignal;
    double macdHistPrev = iMACD(Symbol(), PERIOD_H4, MACDFast, MACDSlow, MACDSignal, PRICE_CLOSE, MODE_MAIN, 2) -
    iMACD(Symbol(), PERIOD_H4, MACDFast, MACDSlow, MACDSignal, PRICE_CLOSE, MODE_SIGNAL, 2);

    return (macdHist < 0 && macdHist < macdHistPrev);
    }

    //+------------------------------------------------------------------+
    //| Check pullback to fast EMA |
    //+------------------------------------------------------------------+
    bool IsPullbackToEMA(int trendDir)
    {
    double fastEMA = iMA(Symbol(), PERIOD_H4, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1);
    double close1 = iClose(Symbol(), PERIOD_H4, 1);
    double low1 = iLow(Symbol(), PERIOD_H4, 1);
    double high1 = iHigh(Symbol(), PERIOD_H4, 1);
    double emaRange = fastEMA 0.002; // 0.2% tolerance for Bitcoin

    if(trendDir > 0) // uptrend, price should pull back near EMA
    {
    return (low1 <= fastEMA + emaRange && close1 > fastEMA);
    }
    else if(trendDir < 0) // downtrend
    {
    return (high1 >= fastEMA - emaRange && close1 < fastEMA);
    }
    return false;
    }

    //+------------------------------------------------------------------+
    //| Expert tick function |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    // Daily drawdown protection
    double currentEquity = AccountEquity();
    double drawdownPercent = (dailyStartEquity - currentEquity) / dailyStartEquity
    100;
    if(drawdownPercent >= MaxDailyDrawdown)
    {
    Comment("Max daily drawdown reached. No new trades.");
    if(CountPositions() > 0 && drawdownPercent >= MaxDailyDrawdown + 2)
    CloseAllOrders();
    return;
    }

    // Saturday close before weekend gap
    if(UseSaturdayClose && !saturdayCloseExecuted)
    {
    datetime currentTime = TimeCurrent();
    if(TimeDayOfWeek(currentTime) == 6 && TimeHour(currentTime) >= 22)
    {
    CloseAllOrders();
    saturdayCloseExecuted = true;
    return;
    }
    if(TimeDayOfWeek(currentTime) != 6)
    saturdayCloseExecuted = false;
    }

    // Spread filter for Bitcoin
    if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread)
    {
    Comment("Spread too high for BTC: ", MarketInfo(Symbol(), MODE_SPREAD));
    return;
    }

    // New bar logic (H4)
    if(Time[0] == lastBarTime)
    return;
    lastBarTime = Time[0];

    // Update average ATR for volatility reference
    double currentATR = iATR(Symbol(), PERIOD_H4, ATRPeriod, 1);
    if(currentATR > 0) avgATR = (avgATR 0.9) + (currentATR 0.1);

    // Manage existing position with trailing stop
    if(CountPositions() > 0)
    {
    ManageTrailingStop();
    return;
    }

    // ADX trend strength filter
    double adx = iADX(Symbol(), PERIOD_H4, ADXPeriod, PRICE_CLOSE, MODE_MAIN, 1);
    if(adx < ADXThreshold)
    {
    Comment("Weak trend, ADX: ", adx);
    return;
    }

    // Get trend direction
    int trendDir = GetTrendDirection();
    if(trendDir == 0)
    {
    Comment("No clear trend direction");
    return;
    }

    // Check pullback condition
    if(!IsPullbackToEMA(trendDir))
    {
    Comment("No pullback to EMA");
    return;
    }

    int cmd = -1;
    double sl = 0, tp = 0;
    double ask = Ask;
    double bid = Bid;
    double dynamicLot = GetDynamicLotSize(currentATR);

    // Long signal: uptrend, pullback confirmed, MACD bullish momentum
    if((trendDir == 1 || trendDir == 2) && IsMACDBullish())
    {
    cmd = OP_BUY;
    sl = bid - (currentATR ATRStopMultiplier);
    tp = bid + (currentATR
    ATRTakeMultiplier);
    }
    // Short signal: downtrend, pullback confirmed, MACD bearish momentum
    else if((trendDir == -1 || trendDir == -2) && IsMACDBearish())
    {
    cmd = OP_SELL;
    sl = ask + (currentATR ATRStopMultiplier);
    tp = ask - (currentATR
    ATRTakeMultiplier);
    }

    if(cmd != -1)
    {
    int ticket = OrderSend(Symbol(), cmd, dynamicLot, (cmd==OP_BUY?ask:bid), 5, sl, tp, "BTC Trend Armor", MagicNumber, 0, clrNONE);
    if(ticket < 0)
    Print("OrderSend failed: ", GetLastError());
    }
    }

    //+------------------------------------------------------------------+
    //| Manage trailing stop for open position |
    //+------------------------------------------------------------------+
    void ManageTrailingStop()
    {
    for(int i = OrdersTotal()-1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    {
    double currentATR = iATR(Symbol(), PERIOD_H4, ATRPeriod, 1);
    if(currentATR <= 0) currentATR = avgATR;

    double trailStartPrice = currentATR TrailingStartMulti;
    double trailStep = currentATR
    TrailingStepMulti;
    double newSL = 0;

    if(OrderType() == OP_BUY)
    {
    double profitPoints = (Bid - OrderOpenPrice());
    if(profitPoints >= trailStartPrice)
    {
    newSL = Bid - trailStep;
    if(newSL > OrderStopLoss())
    {
    if(OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE))
    Print("Trailing stop updated for BUY #", OrderTicket());
    }
    }
    }
    else if(OrderType() == OP_SELL)
    {
    double profitPoints = (OrderOpenPrice() - Ask);
    if(profitPoints >= trailStartPrice)
    {
    newSL = Ask + trailStep;
    if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
    {
    if(OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE))
    Print("Trailing stop updated for SELL #", OrderTicket());
    }
    }
    }
    break;
    }
    }
    }
    }

    //+------------------------------------------------------------------+
    //| Count open positions with this MagicNumber |
    //+------------------------------------------------------------------+
    int CountPositions()
    {
    int count = 0;
    for(int i = OrdersTotal()-1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    count++;
    }
    }
    return count;
    }

    //+------------------------------------------------------------------+
    //| Close all orders for this symbol and magic |
    //+------------------------------------------------------------------+
    void CloseAllOrders()
    {
    for(int i = OrdersTotal()-1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    {
    if(OrderType() == OP_BUY)
    OrderClose(OrderTicket(), OrderLots(), Bid, 5, clrNONE);
    else if(OrderType() == OP_SELL)
    OrderClose(OrderTicket(), OrderLots(), Ask, 5, clrNONE);
    }
    }
    }
    }
    //+------------------------------------------------------------------+
    ``
    Reference: Original MQL4 code for educational purposes.
    Disclaimer: Bitcoin trading carries extreme risk due to high volatility. This EA is provided as-is without any guarantee of profitability. Always test on a demo account for a minimum of 3 months before live deployment. Past performance does not guarantee future results.