Initial upload of ForexTrendBot.mq5, an EMA 20/50 trend-following Expert Advisor intended for demo testing only. Not yet compiled or backtested. No live trading.
166 lines
5.3 KiB
MQL5
166 lines
5.3 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| ForexTrendBot.mq5 |
| |
//| Simple EMA 20/50 crossover EA - DEMO ACCOUNTS ONLY |
| |
//| Educational example; not a guarantee of profit. |
| |
//+------------------------------------------------------------------+
| |
#property strict
| |
#property version "1.00"
| |
#property description "EMA 20/50 trend-following example. Demo accounts only."
| |
| |
#include <Trade/Trade.mqh>
| |
| |
input double InpLots = 0.01; // Fixed lot size
| |
input int InpFastEMA = 20; // Fast EMA period
| |
input int InpSlowEMA = 50; // Slow EMA period
| |
input int InpStopLossPoints = 300; // Stop loss in points
| |
input int InpTakeProfitPoints = 600; // Take profit in points
| |
input int InpMaxSpreadPoints = 25; // Maximum spread in points
| |
input ulong InpMagicNumber = 205020; // EA identifier
| |
| |
CTrade trade;
| |
int fastHandle = INVALID_HANDLE;
| |
int slowHandle = INVALID_HANDLE;
| |
datetime lastBarTime = 0;
| |
| |
//+------------------------------------------------------------------+
| |
int OnInit()
| |
{
| |
// This EA intentionally refuses to run on non-demo accounts.
| |
if((ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE)
| |
!= ACCOUNT_TRADE_MODE_DEMO)
| |
{
| |
Print("ForexTrendBot is DEMO-ONLY. Initialization blocked.");
| |
return(INIT_FAILED);
| |
}
| |
| |
if(InpLots <= 0 || InpFastEMA <= 0 || InpSlowEMA <= 0 ||
| |
InpFastEMA >= InpSlowEMA || InpStopLossPoints <= 0 ||
| |
InpTakeProfitPoints <= 0 || InpMaxSpreadPoints < 0)
| |
{
| |
Print("Invalid input settings.");
| |
return(INIT_PARAMETERS_INCORRECT);
| |
}
| |
| |
fastHandle = iMA(_Symbol, PERIOD_M15, InpFastEMA, 0, MODE_EMA, PRICE_CLOSE);
| |
slowHandle = iMA(_Symbol, PERIOD_M15, InpSlowEMA, 0, MODE_EMA, PRICE_CLOSE);
| |
| |
if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE)
| |
{
| |
Print("Could not create EMA indicator handles. Error: ", GetLastError());
| |
return(INIT_FAILED);
| |
}
| |
| |
trade.SetExpertMagicNumber(InpMagicNumber);
| |
trade.SetTypeFillingBySymbol(_Symbol);
| |
Print("ForexTrendBot initialized on demo account. Signal timeframe: M15.");
| |
return(INIT_SUCCEEDED);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
void OnDeinit(const int reason)
| |
{
| |
if(fastHandle != INVALID_HANDLE)
| |
IndicatorRelease(fastHandle);
| |
if(slowHandle != INVALID_HANDLE)
| |
IndicatorRelease(slowHandle);
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
void OnTick()
| |
{
| |
if((ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE)
| |
!= ACCOUNT_TRADE_MODE_DEMO)
| |
return;
| |
| |
if(!IsNewBar())
| |
return;
| |
| |
if(HasOurPosition())
| |
return;
| |
| |
MqlTick tick;
| |
if(!SymbolInfoTick(_Symbol, tick))
| |
return;
| |
| |
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
| |
if(point <= 0)
| |
return;
| |
| |
double spreadPoints = (tick.ask - tick.bid) / point;
| |
if(spreadPoints > InpMaxSpreadPoints)
| |
{
| |
Print("Spread too high; skipping this bar. Spread points: ", spreadPoints);
| |
return;
| |
}
| |
| |
// Dynamic series arrays: index 0=current bar, 1=last closed, 2=previous.
| |
double fast[], slow[];
| |
ArrayResize(fast, 3);
| |
ArrayResize(slow, 3);
| |
ArraySetAsSeries(fast, true);
| |
ArraySetAsSeries(slow, true);
| |
| |
if(CopyBuffer(fastHandle, 0, 0, 3, fast) < 3 ||
| |
CopyBuffer(slowHandle, 0, 0, 3, slow) < 3)
| |
{
| |
Print("Not enough EMA data yet.");
| |
return;
| |
}
| |
| |
bool bullishCross = (fast[2] <= slow[2] && fast[1] > slow[1]);
| |
bool bearishCross = (fast[2] >= slow[2] && fast[1] < slow[1]);
| |
| |
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
| |
double sl = 0.0, tp = 0.0;
| |
| |
if(bullishCross)
| |
{
| |
sl = NormalizeDouble(tick.ask - InpStopLossPoints * point, digits);
| |
tp = NormalizeDouble(tick.ask + InpTakeProfitPoints * point, digits);
| |
| |
if(!trade.Buy(InpLots, _Symbol, 0.0, sl, tp, "EMA20/50 demo buy"))
| |
Print("Buy failed. Retcode: ", trade.ResultRetcode(), " - ",
| |
trade.ResultRetcodeDescription());
| |
else
| |
Print("Demo BUY submitted. SL=", sl, " TP=", tp);
| |
}
| |
else if(bearishCross)
| |
{
| |
sl = NormalizeDouble(tick.bid + InpStopLossPoints * point, digits);
| |
tp = NormalizeDouble(tick.bid - InpTakeProfitPoints * point, digits);
| |
| |
if(!trade.Sell(InpLots, _Symbol, 0.0, sl, tp, "EMA20/50 demo sell"))
| |
Print("Sell failed. Retcode: ", trade.ResultRetcode(), " - ",
| |
trade.ResultRetcodeDescription());
| |
else
| |
Print("Demo SELL submitted. SL=", sl, " TP=", tp);
| |
}
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool IsNewBar()
| |
{
| |
datetime currentBar = iTime(_Symbol, PERIOD_M15, 0);
| |
if(currentBar == 0 || currentBar == lastBarTime)
| |
return false;
| |
| |
lastBarTime = currentBar;
| |
return true;
| |
}
| |
| |
//+------------------------------------------------------------------+
| |
bool HasOurPosition()
| |
{
| |
for(int i = PositionsTotal() - 1; i >= 0; i--)
| |
{
| |
ulong ticket = PositionGetTicket(i);
| |
if(ticket == 0)
| |
continue;
| |
| |
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
| |
(ulong)PositionGetInteger(POSITION_MAGIC) == InpMagicNumber)
| |
return true;
| |
}
| |
return false;
| |
}
| |
//+------------------------------------------------------------------+
|