Self-Programmed Expert Advisor “Moving Average Crossover” Introduction This Expert Advisor (EA) “MA_Cross” for MetaTrader 5 automates detection and trading of a trend-following setup based on two Exponential Moving Averages (EMA) and the Average True Range (ATR). It demonstrates how to use MQL5 to: Create and read indicators Detect signals (crossover and volatility filter) Implement risk management via stop-loss, take-profit, and lot sizing Place and monitor orders Dynamically adjust a trailing stop The goal is to show how to work cleanly with indicator handles in MQL5 and build a complete, runnable EA. Code Overview Libraries and Trading Object #include <Trade\Trade.mqh> // include CTrade class for order functions CTrade trade; // trading object to send and manage orders Trade.mqh provides the CTrade class, which makes it easy to place market orders (buy/sell) and modify existing positions. Input Parameters input int FastEMAPeriod = 10; // period for fast EMA input int SlowEMAPeriod = 50; // period for slow EMA input int ATRPeriod = 14; // period for ATR input double RiskPercent = 1.0; // max risk per trade in % of account input double RewardRiskRatio = 2.0; // TP to SL ratio (e.g. 1:2) input bool UseTrailingStop = true; // enable trailing stop? input string CommentText = "EMA_Cross_ATR"; // comment for all orders These inputs let you adjust key parameters (EMA periods, ATR, risk, R:R ratio, trailing stop) directly in the tester or EA properties. Global Handles for Indicators int fastHandle; // handle for fast EMA (FastEMAPeriod) int slowHandle; // handle for slow EMA (SlowEMAPeriod) int atrHandle; // handle for ATR (ATRPeriod) Indicators in MQL5 are created via handles. These integer variables store references and are later used with CopyBuffer() to read values. OnInit() int OnInit() { // create EMA handles fastHandle = iMA(_Symbol, PERIOD_CURRENT, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE); slowHandle = iMA(_Symbol, PERIOD_CURRENT, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE); // create ATR handle atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod); // check if all handles are valid if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE || atrHandle == INVALID_HANDLE) { Print("Error creating indicator handles"); return(INIT_FAILED); } return(INIT_SUCCEEDED); } iMA(...) and iATR(...) return the handles. If a handle is INVALID_HANDLE, initialization fails. CalculateLot() double CalculateLot(bool isBuy, double stopLossPrice) { // entry price (Ask for buy, Bid for sell) double entryPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); // distance entry ↔ stop-loss in price units double distance = MathAbs(entryPrice - stopLossPrice); // maximum amount we are allowed to risk double riskAmount = AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100.0; // tick value and tick size for the symbol double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double pointValue = tickValue / tickSize; // raw lot size: risk / (distance × point value) double lots = riskAmount / (distance * pointValue); // round to allowed volume steps double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); lots = MathFloor(lots / step) * step; if(lots < minLot) lots = minLot; if(lots > maxLot) lots = maxLot; return(lots); } This function calculates the lot size so that, if the stop-loss is hit, the maximum risk amount will not be exceeded. OnTick() void OnTick() { // 1) current Bid/Ask double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); // 2) buffers for the last 2 values of each indicator double fastBuf[2], slowBuf[2], atrBuf[2]; // 3) copy indicator values (current = index 0, previous = index 1) if(CopyBuffer(fastHandle, 0, 0, 2, fastBuf) <= 0 || CopyBuffer(slowHandle, 0, 0, 2, slowBuf) <= 0 || CopyBuffer(atrHandle, 0, 0, 2, atrBuf) <= 0) { Print("Error copying indicator data"); return; } // 4) assign buffer data double fastEMA = fastBuf[0], fastEMAold = fastBuf[1]; double slowEMA = slowBuf[0], slowEMAold = slowBuf[1]; double atrNow = atrBuf[0], atrOld = atrBuf[1]; // 5) check if a position is already open bool positionExists = PositionSelect(_Symbol); // — entries only if no position is open — if(!positionExists) { // long signal: EMA10 crosses EMA50 upward + ATR rising bool longSignal = (fastEMAold < slowEMAold) && (fastEMA > slowEMA) && (atrNow > atrOld); // short signal: EMA10 crosses EMA50 downward bool shortSignal = (fastEMAold > slowEMAold) && (fastEMA < slowEMA); // long entry if(longSignal) { double sl = ask - 1.5 * atrNow; double tp = ask + RewardRiskRatio * (ask - sl); double lot = CalculateLot(true, sl); if(trade.Buy(lot, NULL, ask, sl, tp, CommentText)) Print("Long opened at ", ask, " SL=", sl, " TP=", tp); } // short entry else if(shortSignal) { double sl = bid + 1.5 * atrNow; double tp = bid - RewardRiskRatio * (sl - bid); double lot = CalculateLot(false, sl); if(trade.Sell(lot, NULL, bid, sl, tp, CommentText)) Print("Short opened at ", bid, " SL=", sl, " TP=", tp); } } // — trailing stop if a position is open and enabled — else if(UseTrailingStop) { ulong ticket = PositionGetInteger(POSITION_TICKET); ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double newSL = (type == POSITION_TYPE_BUY) ? bid - atrNow : ask + atrNow; trade.PositionModify(ticket, newSL, 0); } } In OnTick(), indicators are evaluated, signals are detected, orders are sent, and the stop-loss is trailed dynamically if needed. OnDeinit() void OnDeinit(const int reason) { if(fastHandle != INVALID_HANDLE) IndicatorRelease(fastHandle); if(slowHandle != INVALID_HANDLE) IndicatorRelease(slowHandle); if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle); } When removing the EA, all indicator handles are released to avoid memory leaks and other issues. Explanation of the Key Steps Handle creation and value access iMA() / iATR() each return a handle in OnInit(), a numeric reference to the respective indicator. In OnTick(), CopyBuffer(handle, 0, shift, count, buffer) fetches current and previous values into a local array. Benefit: more efficient than repeated direct calls and enables access to historical bars. Crossover detection bool longSignal = (fastEMAold < slowEMAold) && (fastEMA > slowEMA) && (atrNow > atrOld); bool shortSignal = (fastEMAold > slowEMAold) && (fastEMA < slowEMA); fastEMAold and slowEMAold are the previous bar’s EMA values; fastEMA and slowEMA are the current ones. A long signal occurs when the fast EMA crosses above the slow EMA and ATR indicates rising volatility. A short signal occurs when crossing downward, without the ATR filter. Risk management & lot sizing Stop-loss is set to 1.5 × ATR: Long: SL = Ask – 1.5 × ATR Short: SL = Bid + 1.5 × ATR Take-profit targets a 1:2 risk-reward: TP distance = 2 × SL distance CalculateLot() computes how many lots to place so that, if the SL is hit, at most RiskPercent% of the account is lost. It uses SymbolInfoDouble(..., SYMBOL_TRADE_TICK_VALUE) and SYMBOL_TRADE_TICK_SIZE to correctly account for the money value per point. Order placement With trade.Buy(lot, NULL, price, sl, tp, CommentText) or trade.Sell(...) a market order is sent with defined SL and TP. CTrade handles internal error processing, slippage, and returns the order ticket. Trailing stop (optional) Once a position is open and UseTrailingStop == true, the EA shifts the SL on every tick by 1 × ATR behind the current market price: Long: new SL = Bid – ATR Short: new SL = Ask + ATR This provides a dynamic lock-in of accrued profits. Cleanup in OnDeinit() All created indicator handles (fastHandle, slowHandle, atrHandle) are released with IndicatorRelease(handle) to free resources properly. With these clearly structured steps you learn how to: Create and read indicators efficiently Detect signals precisely Implement responsible risk management Manage market orders professionally Apply dynamic stop-loss strategies Summary This EA demonstrates a complete workflow in MQL5: Clean separation between initialization (OnInit), main logic (OnTick), and cleanup (OnDeinit). Handle-based indicator usage with CopyBuffer(). Simple trend-following strategy using two EMAs plus a volatility filter. Careful risk management using stop-loss, take-profit, and dynamic lot sizing. Optional automation of a trailing stop. With this commented example you have a solid foundation to develop your own strategies in MQL5 and understand how to implement professional automated trading systems. Good luck with your next steps in MQL5 programming! Source Code MA_Cross.mq5 //+------------------------------------------------------------------+ //| MA_Cross.mq5 | //| EMA Crossover + ATR filter for MetaTrader 5 (MQL5) | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> // include CTrade class for order functions CTrade trade; // trading object //—— Input parameters ————————————————————————————————————————————— input int FastEMAPeriod = 10; // fast EMA period input int SlowEMAPeriod = 50; // slow EMA period input int ATRPeriod = 14; // ATR period input double RiskPercent = 1.0; // max risk per trade in % (e.g. 1 = 1%) input double RewardRiskRatio = 2.0; // risk-reward ratio (e.g. 1:2) input bool UseTrailingStop = true; // enable trailing stop? input string CommentText = "EMA_Cross_ATR"; // order comment //—— Global handles for indicators ———————————————————————————————— int fastHandle; // handle for fast EMA int slowHandle; // handle for slow EMA int atrHandle; // handle for ATR //+------------------------------------------------------------------+ //| OnInit: initialization when the EA is loaded | //+------------------------------------------------------------------+ int OnInit() { // create EMA handles with symbol, timeframe, period, shift, method, price source fastHandle = iMA(_Symbol, PERIOD_CURRENT, FastEMAPeriod, 0, MODE_EMA, PRICE_CLOSE); slowHandle = iMA(_Symbol, PERIOD_CURRENT, SlowEMAPeriod, 0, MODE_EMA, PRICE_CLOSE); // create ATR handle with symbol, timeframe, period atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod); // validate handles if(fastHandle==INVALID_HANDLE || slowHandle==INVALID_HANDLE || atrHandle==INVALID_HANDLE) { Print("Error creating indicator handles"); return(INIT_FAILED); // abort init on error } return(INIT_SUCCEEDED); // ok } //+------------------------------------------------------------------+ //| CalculateLot: lot size based on SL distance | //+------------------------------------------------------------------+ double CalculateLot(bool isBuy, double stopLossPrice) { // 1) entry price depending on buy/sell double entryPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); // 2) distance SL ↔ entry double distance = MathAbs(entryPrice - stopLossPrice); // 3) risk amount (balance × risk%) double riskAmount = AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100.0; // 4) point value for symbol (TickValue / TickSize) double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double pointValue = tickValue / tickSize; // 5) raw lots = risk / (distance × point value) double lots = riskAmount / (distance * pointValue); // 6) round to minimum lot steps double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); lots = MathFloor(lots / step) * step; if(lots < minLot) lots = minLot; if(lots > maxLot) lots = maxLot; return(lots); // return final lot size } //+------------------------------------------------------------------+ //| OnTick: main logic each tick | //+------------------------------------------------------------------+ void OnTick() { // 1) current Bid/Ask double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); // 2) buffers for current and previous indicator values double fastBuf[2], slowBuf[2], atrBuf[2]; // 3) copy data from handles if(CopyBuffer(fastHandle, 0, 0, 2, fastBuf) <= 0 || CopyBuffer(slowHandle, 0, 0, 2, slowBuf) <= 0 || CopyBuffer(atrHandle, 0, 0, 2, atrBuf) <= 0) { Print("Error copying indicator data"); return; // abort on error } // 4) assign values: [0] = current, [1] = previous double fastEMA = fastBuf[0]; double fastEMAold = fastBuf[1]; double slowEMA = slowBuf[0]; double slowEMAold = slowBuf[1]; double atrNow = atrBuf[0]; double atrOld = atrBuf[1]; // 5) check if a position is already open bool positionExists = PositionSelect(_Symbol); // —— entries when no position is open ———————————————————————————— if(!positionExists) { // long when EMA cross up + ATR rising bool longSignal = (fastEMAold < slowEMAold) && (fastEMA > slowEMA) && (atrNow > atrOld); // short when EMA cross down bool shortSignal = (fastEMAold > slowEMAold) && (fastEMA < slowEMA); if(longSignal) // long entry { double sl = ask - 1.5 * atrNow; // SL = entry – 1.5×ATR double tp = ask + RewardRiskRatio * (ask - sl); // TP = entry + 2×risk double lot = CalculateLot(true, sl); // lot sizing if(trade.Buy(lot, NULL, ask, sl, tp, CommentText)) // send buy order Print("Long opened at ", ask, " SL=", sl, " TP=", tp); } else if(shortSignal) // short entry { double sl = bid + 1.5 * atrNow; // SL = entry + 1.5×ATR double tp = bid - RewardRiskRatio * (sl - bid); // TP = entry – 2×risk double lot = CalculateLot(false, sl); // lot sizing if(trade.Sell(lot, NULL, bid, sl, tp, CommentText)) // send sell order Print("Short opened at ", bid, " SL=", sl, " TP=", tp); } } // —— trailing stop if a position is open —————————————————————————— else if(UseTrailingStop) { ulong ticket = PositionGetInteger(POSITION_TICKET); // position ticket ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double newSL; if(type == POSITION_TYPE_BUY) // for long newSL = bid - atrNow; // SL = Bid – 1×ATR else // for short newSL = ask + atrNow; // SL = Ask + 1×ATR trade.PositionModify(ticket, newSL, 0); // modify SL } } //+------------------------------------------------------------------+ //| OnDeinit: release handles | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(fastHandle != INVALID_HANDLE) IndicatorRelease(fastHandle); // release fast EMA if(slowHandle != INVALID_HANDLE) IndicatorRelease(slowHandle); // release slow EMA if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle); // release ATR } //+------------------------------------------------------------------+