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;
| |||
}
| |||
//+------------------------------------------------------------------+
|