BTC Scalper EA MT5: Bitcoin MQL5 Scalping Code & 2026 Guide
Inspect an educational BTCUSD M5 EA combining EMA crossovers, an ATR-versus-ATR-average filter and RSI conditions. The source illustrates signal timing and order requests; no verified backtest, best timeframe or minimum account balance is established. Broker contracts and costs must be checked before testing.
Strategy logic
Entry conditions
The sample compares two completed M5 bars for a 9/21 EMA crossover, requires ATR above its own simple average, and applies an RSI ceiling for buys or floor for sells. These are research rules, not validated protection from false entries. It limits matching positions by direction; opposite positions and netting account behavior require explicit evaluation.
Exit conditions
Initial SL and TP are measured from the requested ask or bid using ATR multiples, not from candle extremes. Trailing is evaluated on each new M5 bar after data readiness checks. The optional close-hour filter uses broker-server time and blocks new entries after that hour until the next server day; it does not automatically convert New York time or account for daylight saving.
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; // Illustrative cutoff in broker server time
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, g_handleATR);
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 || RiskPercent<=0) return 0;
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;
if(tickSize<=0 || tickVal<=0) return 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);
if(stepLot<=0 || minLot<=0 || maxLot<minLot) return 0;
lots = MathFloor(MathMin(lots,maxLot) / stepLot) * stepLot;
return lots<minLot ? 0 : lots;
}
//+------------------------------------------------------------------+
//| Helper: send market order |
//+------------------------------------------------------------------+
bool SendOrder(ENUM_ORDER_TYPE orderType, double lots, double sl, double tp)
{
if(lots<=0) return false;
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) ||
(res.retcode!=TRADE_RETCODE_DONE && res.retcode!=TRADE_RETCODE_DONE_PARTIAL))
{
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);
if(!OrderSend(req,res) || res.retcode!=TRADE_RETCODE_DONE)
Print("Trade update result: ",res.retcode," ",res.comment);
}
}
//+------------------------------------------------------------------+
//| 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);
if(!OrderSend(req,res) || res.retcode!=TRADE_RETCODE_DONE)
Print("Trade update result: ",res.retcode," ",res.comment);
}
}
//+------------------------------------------------------------------+
//| Main tick handler |
//+------------------------------------------------------------------+
void OnTick()
{
// --- NY session close logic ---
if(CloseAtNYClose)
{
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
if(dt.hour >= NYCloseHour)
{
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 || atrMA<=0 || fastEMA_cur<=0 || fastEMA_prev<=0 ||
slowEMA_cur<=0 || slowEMA_prev<=0 || rsi<=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, save it in the MQL5/Experts folder, and compile with F7.
Generate a custom BTCUSD scalping EA
Describe the entry, exit, position, order, and risk rules you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.
Pine Script vs MQL5: Same Strategy, Different Platforms
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Series evaluated on chart updates | Event handlers with explicit buffers |
| Deployment | Runs in TradingView with alerts | Runs in the MT5 terminal or on a VPS |
| Broker access | Strategy orders are simulated; live automation needs a separate integration | Direct broker connectivity |
| Backtesting | Strategy Tester for strategy scripts | Strategy Tester for Expert Advisors |
| Code complexity | Simpler, functional syntax | C++-like, more powerful |
The calculation can be implemented on either platform, but the runtime model and buffer APIs differ.
Frequently Asked Questions
Related MQL5 pages
Technical references
Risk and testing note
These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.