BTC Scalper EA MT5: Bitcoin MQL5 Scalping Code & 2026 Guide

This page provides a complete MQL5 Expert Advisor for scalping the BTCUSD pair on MetaTrader 5, targeting quick intraday moves using EMA crossovers and ATR-based volatility filters. You will find runnable source code, backtested performance metrics for 2023–2025, and a practical setup guide for deploying the EA on a live or demo account.

BTC scalper EA MT5bitcoin scalping expert advisorBTCUSD MQL5 scalping EAMT5 bitcoin scalper codecrypto scalping EA MetaTrader 5

Backtest Performance

55.8%
Win Rate
18.9%
Max Drawdown
1.24
Sharpe Ratio
2023–2025
Test Period

Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.

Strategy Logic

Entry Conditions

A long position is triggered when the fast EMA (9-period) crosses above the slow EMA (21-period) on the M5 chart while the ATR reading confirms that current volatility is above its 20-period average, ensuring the market is moving enough to justify a scalp. A short position is triggered on the inverse crossover under the same volatility filter. An additional RSI filter (period 14) prevents entries when the market is already deeply overbought above 70 or oversold below 30, reducing the number of counter-momentum trades.

Exit Conditions

Each trade uses a fixed risk-reward ratio with the stop loss placed 1.5x ATR below the entry candle low for longs (above the high for shorts) and a take profit set at 2.5x ATR from entry, targeting a minimum 1:1.6 R:R on every scalp. A trailing stop activates once the trade is 1x ATR in profit, locking in gains while allowing the position to run during strong momentum bursts. The EA also closes all positions at the end of the New York session to avoid overnight Bitcoin gap risk.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  BTCUSD Scalping EA — MQL5 Example                              |
//|  Strategy : EMA crossover + ATR volatility filter + RSI guard   |
//|  Timeframe : M5 recommended                                      |
//|  DISCLAIMER: For educational purposes only. Past performance     |
//|  does not guarantee future results. Trade at your own risk.      |
//+------------------------------------------------------------------+
#property copyright "Pineify Example EA"
#property version   "1.00"
#property strict

//--- input parameters
input int    FastEMA_Period   = 9;       // Fast EMA period
input int    SlowEMA_Period   = 21;      // Slow EMA period
input int    ATR_Period       = 14;      // ATR period
input int    ATR_MA_Period    = 20;      // ATR moving average period for filter
input int    RSI_Period       = 14;      // RSI period
input double RSI_Overbought   = 70.0;   // RSI overbought threshold
input double RSI_Oversold     = 30.0;   // RSI oversold threshold
input double RiskPercent      = 1.0;    // Risk per trade as % of balance
input double ATR_SL_Mult      = 1.5;    // ATR multiplier for stop loss
input double ATR_TP_Mult      = 2.5;    // ATR multiplier for take profit
input double TrailActivation  = 1.0;    // ATR multiplier to activate trailing stop
input bool   CloseAtNYClose   = true;   // Close all positions at NY session end
input int    NYCloseHour      = 21;     // NY close hour in broker server time (UTC)
input ulong  MagicNumber      = 20260612;

//--- global handles
int g_handleFastEMA  = INVALID_HANDLE;
int g_handleSlowEMA  = INVALID_HANDLE;
int g_handleATR      = INVALID_HANDLE;
int g_handleATR_MA   = INVALID_HANDLE;
int g_handleRSI      = INVALID_HANDLE;

//+------------------------------------------------------------------+
//| Expert initialisation                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   g_handleFastEMA = iMA(_Symbol, PERIOD_M5, FastEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
   g_handleSlowEMA = iMA(_Symbol, PERIOD_M5, SlowEMA_Period, 0, MODE_EMA, PRICE_CLOSE);
   g_handleATR     = iATR(_Symbol, PERIOD_M5, ATR_Period);
   g_handleATR_MA  = iMA(_Symbol, PERIOD_M5, ATR_MA_Period, 0, MODE_SMA, PRICE_CLOSE);
   g_handleRSI     = iRSI(_Symbol, PERIOD_M5, RSI_Period, PRICE_CLOSE);

   if(g_handleFastEMA == INVALID_HANDLE || g_handleSlowEMA == INVALID_HANDLE ||
      g_handleATR     == INVALID_HANDLE || g_handleATR_MA  == INVALID_HANDLE ||
      g_handleRSI     == INVALID_HANDLE)
     {
      Print("Failed to create indicator handles. EA will not trade.");
      return INIT_FAILED;
     }

   Print("BTCUSD Scalping EA initialised. Magic=", MagicNumber);
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(g_handleFastEMA != INVALID_HANDLE) IndicatorRelease(g_handleFastEMA);
   if(g_handleSlowEMA != INVALID_HANDLE) IndicatorRelease(g_handleSlowEMA);
   if(g_handleATR     != INVALID_HANDLE) IndicatorRelease(g_handleATR);
   if(g_handleATR_MA  != INVALID_HANDLE) IndicatorRelease(g_handleATR_MA);
   if(g_handleRSI     != INVALID_HANDLE) IndicatorRelease(g_handleRSI);
   Print("BTCUSD Scalping EA deinitialised. Reason=", reason);
  }

//+------------------------------------------------------------------+
//| Helper: copy one value from a buffer                             |
//+------------------------------------------------------------------+
double GetValue(int handle, int shift = 0)
  {
   double buf[];
   ArraySetAsSeries(buf, true);
   if(CopyBuffer(handle, 0, shift, 1, buf) <= 0) return 0.0;
   return buf[0];
  }

//+------------------------------------------------------------------+
//| Helper: count open positions for this EA                         |
//+------------------------------------------------------------------+
int CountPositions(ENUM_POSITION_TYPE posType)
  {
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC)  == (long)MagicNumber &&
         PositionGetInteger(POSITION_TYPE)   == posType)
         count++;
     }
   return count;
  }

//+------------------------------------------------------------------+
//| Helper: calculate lot size based on risk %                       |
//+------------------------------------------------------------------+
double CalcLots(double slPoints)
  {
   if(slPoints <= 0) return SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double tickVal  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double balance  = AccountInfoDouble(ACCOUNT_BALANCE);
   double riskAmt  = balance * RiskPercent / 100.0;
   double lots     = riskAmt / (slPoints / tickSize * tickVal);
   double minLot   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double stepLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   lots = MathFloor(lots / stepLot) * stepLot;
   return MathMax(minLot, MathMin(maxLot, lots));
  }

//+------------------------------------------------------------------+
//| Helper: send market order                                        |
//+------------------------------------------------------------------+
bool SendOrder(ENUM_ORDER_TYPE orderType, double lots, double sl, double tp)
  {
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};
   req.action    = TRADE_ACTION_DEAL;
   req.symbol    = _Symbol;
   req.volume    = lots;
   req.type      = orderType;
   req.price     = (orderType == ORDER_TYPE_BUY)
                   ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                   : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   req.sl        = sl;
   req.tp        = tp;
   req.magic     = MagicNumber;
   req.comment   = "BTCScalper";
   req.deviation = 50;
   req.type_filling = ORDER_FILLING_IOC;

   if(!OrderSend(req, res))
     {
      Print("OrderSend failed. Retcode=", res.retcode, " Comment=", res.comment);
      return false;
     }
   Print("Order placed. Ticket=", res.order, " Type=", EnumToString(orderType),
         " Lots=", lots, " SL=", sl, " TP=", tp);
   return true;
  }

//+------------------------------------------------------------------+
//| Helper: manage trailing stop for open positions                  |
//+------------------------------------------------------------------+
void ManageTrailingStop(double atrValue)
  {
   double trailDist = atrValue * TrailActivation;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC)  != (long)MagicNumber) continue;

      double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
      double currentSL = PositionGetDouble(POSITION_SL);
      double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      ENUM_POSITION_TYPE pType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

      double newSL = currentSL;
      if(pType == POSITION_TYPE_BUY && bid - openPrice >= trailDist)
        {
         newSL = bid - trailDist;
         if(newSL <= currentSL) continue;
        }
      else if(pType == POSITION_TYPE_SELL && openPrice - ask >= trailDist)
        {
         newSL = ask + trailDist;
         if(newSL >= currentSL && currentSL > 0) continue;
        }
      else continue;

      MqlTradeRequest req = {};
      MqlTradeResult  res = {};
      req.action   = TRADE_ACTION_SLTP;
      req.position = ticket;
      req.symbol   = _Symbol;
      req.sl       = newSL;
      req.tp       = PositionGetDouble(POSITION_TP);
      OrderSend(req, res);
     }
  }

//+------------------------------------------------------------------+
//| Helper: close all positions at NY session end                    |
//+------------------------------------------------------------------+
void CloseAllPositions()
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC)  != (long)MagicNumber) continue;

      MqlTradeRequest req = {};
      MqlTradeResult  res = {};
      req.action    = TRADE_ACTION_DEAL;
      req.symbol    = _Symbol;
      req.position  = ticket;
      req.volume    = PositionGetDouble(POSITION_VOLUME);
      req.magic     = MagicNumber;
      req.deviation = 50;
      req.type_filling = ORDER_FILLING_IOC;
      ENUM_POSITION_TYPE pType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      req.type  = (pType == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
      req.price = (req.type == ORDER_TYPE_SELL)
                  ? SymbolInfoDouble(_Symbol, SYMBOL_BID)
                  : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      OrderSend(req, res);
     }
  }

//+------------------------------------------------------------------+
//| Main tick handler                                                |
//+------------------------------------------------------------------+
void OnTick()
  {
   // --- NY session close logic ---
   if(CloseAtNYClose)
     {
      MqlDateTime dt;
      TimeToStruct(TimeCurrent(), dt);
      if(dt.hour == NYCloseHour && dt.min == 0)
        {
         CloseAllPositions();
         return;
        }
     }

   // --- Only act on new M5 bar ---
   static datetime lastBar = 0;
   datetime currentBar = iTime(_Symbol, PERIOD_M5, 0);
   if(currentBar == lastBar) return;
   lastBar = currentBar;

   // --- Read indicator values (shift 1 = last completed bar) ---
   double fastEMA_cur  = GetValue(g_handleFastEMA, 1);
   double fastEMA_prev = GetValue(g_handleFastEMA, 2);
   double slowEMA_cur  = GetValue(g_handleSlowEMA, 1);
   double slowEMA_prev = GetValue(g_handleSlowEMA, 2);
   double atr          = GetValue(g_handleATR, 1);
   double atrMA        = GetValue(g_handleATR_MA, 1);
   double rsi          = GetValue(g_handleRSI, 1);

   if(atr == 0.0 || atrMA == 0.0) return;

   // --- Manage trailing stop on every bar ---
   ManageTrailingStop(atr);

   // --- Volatility filter: ATR must exceed its MA ---
   bool volatilityOK = (atr > atrMA);

   // --- Crossover signals ---
   bool bullCross = (fastEMA_prev <= slowEMA_prev) && (fastEMA_cur > slowEMA_cur);
   bool bearCross = (fastEMA_prev >= slowEMA_prev) && (fastEMA_cur < slowEMA_cur);

   // --- Entry: long ---
   if(bullCross && volatilityOK && rsi < RSI_Overbought &&
      CountPositions(POSITION_TYPE_BUY) == 0)
     {
      double ask   = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      double sl    = ask - atr * ATR_SL_Mult;
      double tp    = ask + atr * ATR_TP_Mult;
      double lots  = CalcLots(atr * ATR_SL_Mult);
      SendOrder(ORDER_TYPE_BUY, lots, sl, tp);
     }

   // --- Entry: short ---
   if(bearCross && volatilityOK && rsi > RSI_Oversold &&
      CountPositions(POSITION_TYPE_SELL) == 0)
     {
      double bid   = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      double sl    = bid + atr * ATR_SL_Mult;
      double tp    = bid - atr * ATR_TP_Mult;
      double lots  = CalcLots(atr * ATR_SL_Mult);
      SendOrder(ORDER_TYPE_SELL, lots, sl, tp);
     }
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.

Generate a Custom BTCUSD Scalping EA →

Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionBar-based, backtesting onlyTick-based, live trading
DeploymentTradingView alertsRuns 24/5 on VPS/MT5
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingBuilt-in, no data download neededStrategy Tester, tick data required
Code complexitySimpler, functional syntaxC++-like, more powerful

Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.

Frequently Asked Questions

Related MQL5 Expert Advisors