forextrendbot/ForexTrendBot.mq5

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