Complete .mq5 source code

Crypto Expert Advisor MT5: Bitcoin, Ethereum & Altcoin EA Guide

Review an educational H1 EMA-crossover EA with an iMACD main-line filter, ATR stops and illustrative risk sizing. The source runs on the attached broker symbol, not a multi-asset portfolio. No verified win rate or optimized crypto settings are supplied.

Strategy logic

Entry conditions

The EA compares completed H1 bars 2 and 1 for an EMA crossover, then requires the iMACD main line (buffer 0) to be above or below zero. It does not require that MACD itself crosses zero, nor subtract the signal line as an OsMA-style histogram would. Any existing position on this symbol blocks a new entry; the EA does not scan Bitcoin, Ethereum and altcoins simultaneously.

Exit conditions

Initial SL/TP use the selected ATR multiples. At each new H1 bar, the trailing routine checks positions belonging to this example and may tighten the stop using the latest completed ATR. With the default 1 ATR activation and 1.5 ATR trailing distance, the first adjustment can remain below the entry price for a long: activation does not guarantee locked-in profit. There is no opposite-signal exit. Tick-size, stop/freeze levels, IOC filling, netting ownership and execution responses still require broker-specific testing.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  CryptoTrendEA.mq5                                               |
//|  Trend-following Expert Advisor for BTC, ETH & Altcoin pairs     |
//|  Uses EMA crossover + MACD confirmation with ATR-based stops      |
//|  DISCLAIMER: For educational purposes only. Past performance      |
//|  does not guarantee future results. Trade at your own risk.       |
//+------------------------------------------------------------------+
#property copyright "Pineify"
#property version   "1.00"
#property strict

//--- Input parameters
input int    FastEMAPeriod   = 10;       // Fast EMA period
input int    SlowEMAPeriod   = 50;       // Slow EMA period
input int    MACDFastPeriod  = 12;       // MACD fast EMA
input int    MACDSlowPeriod  = 26;       // MACD slow EMA
input int    MACDSignalPeriod = 9;       // MACD signal period
input int    ATRPeriod       = 14;       // ATR period for stops
input double ATRStopMult     = 1.5;      // ATR multiplier for stop-loss
input double ATRTPMult       = 2.5;      // ATR multiplier for take-profit
input double ATRTrailMult    = 1.0;      // ATR multiplier to activate trail
input double RiskPercent     = 1.5;      // Risk per trade as % of balance
input int    MagicNumber     = 202601;   // Unique EA magic number
input string TradeComment    = "CryptoTrendEA";

//--- Indicator handles
int handleFastEMA=INVALID_HANDLE;
int handleSlowEMA=INVALID_HANDLE;
int handleMACD=INVALID_HANDLE;
int handleATR=INVALID_HANDLE;

//--- Global state
double fastEMABuffer[];
double slowEMABuffer[];
double macdMainBuffer[];
double macdSignalBuffer[];
double atrBuffer[];

datetime lastBarTime = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(FastEMAPeriod<=0 || SlowEMAPeriod<=0 || MACDFastPeriod<=0 || MACDSlowPeriod<=0 || MACDSignalPeriod<=0 || ATRPeriod<=0 || ATRStopMult<=0 || ATRTPMult<=0 || ATRTrailMult<0 || RiskPercent<=0) return INIT_PARAMETERS_INCORRECT;
   //--- Create indicator handles
   handleFastEMA = iMA(_Symbol, PERIOD_H1, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   handleSlowEMA = iMA(_Symbol, PERIOD_H1, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   handleMACD    = iMACD(_Symbol, PERIOD_H1, MACDFastPeriod, MACDSlowPeriod, MACDSignalPeriod, PRICE_CLOSE);
   handleATR     = iATR(_Symbol, PERIOD_H1, ATRPeriod);

   if(handleFastEMA == INVALID_HANDLE || handleSlowEMA == INVALID_HANDLE ||
      handleMACD == INVALID_HANDLE || handleATR == INVALID_HANDLE)
     {
      Print("Error creating indicator handles: ", GetLastError());
      return INIT_FAILED;
     }

   //--- Set buffer as series
   ArraySetAsSeries(fastEMABuffer,   true);
   ArraySetAsSeries(slowEMABuffer,   true);
   ArraySetAsSeries(macdMainBuffer,  true);
   ArraySetAsSeries(macdSignalBuffer,true);
   ArraySetAsSeries(atrBuffer,       true);

   Print("CryptoTrendEA initialized on ", _Symbol);
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(handleFastEMA!=INVALID_HANDLE) IndicatorRelease(handleFastEMA);
   if(handleSlowEMA!=INVALID_HANDLE) IndicatorRelease(handleSlowEMA);
   if(handleMACD!=INVALID_HANDLE) IndicatorRelease(handleMACD);
   if(handleATR!=INVALID_HANDLE) IndicatorRelease(handleATR);
   Print("CryptoTrendEA deinitialized. Reason: ", reason);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- Only act on new H1 bar close
   datetime currentBarTime = iTime(_Symbol, PERIOD_H1, 0);
   if(currentBarTime == 0 || currentBarTime == lastBarTime)
      return;
   lastBarTime = currentBarTime;

   //--- Copy indicator data (need 3 bars: [0]=current, [1]=prev, [2]=prev-prev)
   if(CopyBuffer(handleFastEMA,    0, 0, 3, fastEMABuffer)    < 3) return;
   if(CopyBuffer(handleSlowEMA,    0, 0, 3, slowEMABuffer)    < 3) return;
   if(CopyBuffer(handleMACD,       0, 0, 3, macdMainBuffer)   < 3) return;
   if(CopyBuffer(handleMACD,       1, 0, 3, macdSignalBuffer) < 3) return;
   if(CopyBuffer(handleATR,        0, 0, 3, atrBuffer)        < 3) return;

   double atr      = atrBuffer[1];
   if(atr<=0) return;
   double fastPrev = fastEMABuffer[2];
   double slowPrev = slowEMABuffer[2];
   double fastCurr = fastEMABuffer[1];
   double slowCurr = slowEMABuffer[1];
   double macdCurr = macdMainBuffer[1];

   //--- Update trailing stops on open positions
   UpdateTrailingStops(atr);

   //--- Check for existing positions
   if(PositionSelect(_Symbol))
      return; // One position at a time

   //--- Bullish crossover: fast crosses above slow + MACD main line positive
   bool bullCross = (fastPrev <= slowPrev) && (fastCurr > slowCurr) && (macdCurr > 0);
   //--- Bearish crossover: fast crosses below slow + MACD main line negative
   bool bearCross = (fastPrev >= slowPrev) && (fastCurr < slowCurr) && (macdCurr < 0);

   if(bullCross)
      OpenTrade(ORDER_TYPE_BUY, atr);
   else if(bearCross)
      OpenTrade(ORDER_TYPE_SELL, atr);
  }

//+------------------------------------------------------------------+
//| Open a trade with ATR-based SL and TP                           |
//+------------------------------------------------------------------+
void OpenTrade(ENUM_ORDER_TYPE orderType, double atr)
  {
   double ask   = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid   = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double price = (orderType == ORDER_TYPE_BUY) ? ask : bid;

   double slDistance = atr * ATRStopMult;
   double tpDistance = atr * ATRTPMult;

   double sl, tp;
   if(orderType == ORDER_TYPE_BUY)
     {
      sl = price - slDistance;
      tp = price + tpDistance;
     }
   else
     {
      sl = price + slDistance;
      tp = price - tpDistance;
     }

   //--- Normalize prices
   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   sl = NormalizeDouble(sl, digits);
   tp = NormalizeDouble(tp, digits);

   //--- Calculate lot size based on risk
   double lotSize = CalculateLotSize(slDistance);
   if(lotSize <= 0)
     {
      Print("Invalid lot size calculated.");
      return;
     }

   MqlTradeRequest request = {};
   MqlTradeResult  result  = {};

   request.action       = TRADE_ACTION_DEAL;
   request.symbol       = _Symbol;
   request.volume       = lotSize;
   request.type         = orderType;
   request.price        = price;
   request.sl           = sl;
   request.tp           = tp;
   request.deviation    = 20;
   request.magic        = MagicNumber;
   request.comment      = TradeComment;
   request.type_filling = ORDER_FILLING_IOC;

   if(!OrderSend(request, result) || result.retcode!=TRADE_RETCODE_DONE)
      Print("OrderSend failed: ", result.retcode, " ", result.comment);
   else
      Print("Trade opened: ", EnumToString(orderType), " Lot=", lotSize,
            " SL=", sl, " TP=", tp);
  }

//+------------------------------------------------------------------+
//| Update trailing stops for open positions                         |
//+------------------------------------------------------------------+
void UpdateTrailingStops(double atr)
  {
   if(!PositionSelect(_Symbol))
      return;

   if(PositionGetInteger(POSITION_MAGIC)!=MagicNumber) return;
   long posType = PositionGetInteger(POSITION_TYPE);
   double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double currentSL = PositionGetDouble(POSITION_SL);
   double currentTP = PositionGetDouble(POSITION_TP);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

   double trailActivation = atr * ATRTrailMult;
   double newSL = currentSL;

   if(posType == POSITION_TYPE_BUY)
     {
      if((bid - openPrice) >= trailActivation)
        {
         double proposedSL = NormalizeDouble(bid - atr * ATRStopMult, digits);
         if(proposedSL > currentSL)
            newSL = proposedSL;
        }
     }
   else if(posType == POSITION_TYPE_SELL)
     {
      if((openPrice - ask) >= trailActivation)
        {
         double proposedSL = NormalizeDouble(ask + atr * ATRStopMult, digits);
         if(proposedSL < currentSL || currentSL == 0)
            newSL = proposedSL;
        }
     }

   if(newSL != currentSL)
     {
      MqlTradeRequest req = {};
      MqlTradeResult  res = {};
      req.action   = TRADE_ACTION_SLTP;
      req.symbol   = _Symbol;
      req.sl       = newSL;
      req.tp       = currentTP;
      req.position = PositionGetInteger(POSITION_TICKET);
      if(!OrderSend(req, res) || res.retcode!=TRADE_RETCODE_DONE)
         Print("Trailing stop update failed: ", res.retcode);
     }
  }

//+------------------------------------------------------------------+
//| Calculate lot size based on account risk percentage             |
//+------------------------------------------------------------------+
double CalculateLotSize(double slDistance)
  {
   double balance      = AccountInfoDouble(ACCOUNT_BALANCE);
   double riskAmount   = balance * RiskPercent / 100.0;
   double tickValue    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize     = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double minLot       = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot       = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double lotStep      = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   if(tickSize <= 0 || tickValue <= 0 || slDistance <= 0 || lotStep<=0 || riskAmount<=0)
      return 0;

   double lotSize = riskAmount / (slDistance / tickSize * tickValue);
   lotSize = MathFloor(lotSize / lotStep) * lotStep;
   lotSize = MathFloor(MathMin(maxLot, lotSize)/lotStep)*lotStep;
   if(lotSize<minLot) return 0;
   return NormalizeDouble(lotSize, 8);
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor, save it in the MQL5/Experts folder, and compile with F7.

Generate a custom Crypto trend-following 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

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionSeries evaluated on chart updatesEvent handlers with explicit buffers
DeploymentRuns in TradingView with alertsRuns in the MT5 terminal or on a VPS
Broker accessStrategy orders are simulated; live automation needs a separate integrationDirect broker connectivity
BacktestingStrategy Tester for strategy scriptsStrategy Tester for Expert Advisors
Code complexitySimpler, functional syntaxC++-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.