Zenith-FX/MQL5/Experts/ForexTrader_EA Base Version.mq5
copilot-swe-agent[bot] b12cf1c4ee Reorganize repository to MT5 file tree standard
Co-authored-by: simonokwundue-ops <243668919+simonokwundue-ops@users.noreply.github.com>
2025-11-14 15:53:21 +00:00

473 lines
15 KiB
MQL5

//+------------------------------------------------------------------+
//| ForexTrader_EA.mq5 |
//| Based on Forex Documents |
//| Advanced Trading Course Principles |
//+------------------------------------------------------------------+
#property copyright "ForexTrader EA"
#property link ""
#property version "1.00"
#property description "Expert Advisor based on Moving Average trading strategy"
#property description "Implements trend following with proper risk management"
//--- Input Parameters
input group "=== Trading Strategy Parameters ==="
input int FastMA_Period = 10; // Fast MA Period
input int SlowMA_Period = 70; // Slow MA Period
input ENUM_MA_METHOD MA_Method = MODE_SMA; // Moving Average Method
input ENUM_APPLIED_PRICE MA_Price = PRICE_CLOSE; // Applied Price
input group "=== Risk Management ==="
input double RiskPercent = 2.0; // Risk per trade (% of balance)
input double StopLossPips = 50.0; // Stop Loss in Pips
input double TakeProfitPips = 100.0; // Take Profit in Pips
input bool UseTrailingStop = true; // Use Trailing Stop
input double TrailingStopPips = 30.0; // Trailing Stop Distance (Pips)
input double TrailingStepPips = 10.0; // Trailing Step (Pips)
input group "=== Money Management ==="
input double MaxLotSize = 10.0; // Maximum Lot Size
input double MinLotSize = 0.01; // Minimum Lot Size
input bool UseFixedLot = false; // Use Fixed Lot Size
input double FixedLotSize = 0.1; // Fixed Lot Size
input group "=== Trading Hours ==="
input bool UseTradingHours = false; // Limit Trading Hours
input int StartHour = 8; // Start Trading Hour
input int EndHour = 20; // End Trading Hour
input group "=== Advanced Settings ==="
input int MagicNumber = 123456; // Magic Number
input string TradeComment = "ForexTrader"; // Trade Comment
input int Slippage = 10; // Slippage in Points
//--- Global Variables
int handleFastMA;
int handleSlowMA;
double fastMA[], slowMA[];
double point;
double tickSize;
double tickValue;
double lotStep;
double minLot;
double maxLot;
#include <Trade\Trade.mqh>
CTrade trade;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Initialize trade object
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
trade.SetTypeFilling(ORDER_FILLING_FOK);
//--- Get symbol properties
point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
//--- Validate inputs
if(FastMA_Period <= 0 || SlowMA_Period <= 0)
{
Print("Error: MA periods must be positive");
return(INIT_PARAMETERS_INCORRECT);
}
if(FastMA_Period >= SlowMA_Period)
{
Print("Error: Fast MA period must be less than Slow MA period");
return(INIT_PARAMETERS_INCORRECT);
}
if(RiskPercent <= 0 || RiskPercent > 100)
{
Print("Error: Risk percent must be between 0 and 100");
return(INIT_PARAMETERS_INCORRECT);
}
//--- Create Moving Average indicators
handleFastMA = iMA(_Symbol, PERIOD_CURRENT, FastMA_Period, 0, MA_Method, MA_Price);
handleSlowMA = iMA(_Symbol, PERIOD_CURRENT, SlowMA_Period, 0, MA_Method, MA_Price);
if(handleFastMA == INVALID_HANDLE || handleSlowMA == INVALID_HANDLE)
{
Print("Error creating MA indicators");
return(INIT_FAILED);
}
//--- Set array as series
ArraySetAsSeries(fastMA, true);
ArraySetAsSeries(slowMA, true);
Print("ForexTrader EA initialized successfully");
Print("Fast MA Period: ", FastMA_Period);
Print("Slow MA Period: ", SlowMA_Period);
Print("Risk per trade: ", RiskPercent, "%");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- Release indicator handles
if(handleFastMA != INVALID_HANDLE)
IndicatorRelease(handleFastMA);
if(handleSlowMA != INVALID_HANDLE)
IndicatorRelease(handleSlowMA);
Print("ForexTrader EA deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Check if it's a new bar
static datetime lastBar = 0;
datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBar == lastBar)
return;
lastBar = currentBar;
//--- Update indicator buffers
if(!UpdateIndicators())
return;
//--- Check trading hours
if(UseTradingHours && !IsTradingTime())
return;
//--- Manage existing positions
ManagePositions();
//--- Check for entry signals
if(CountPositions() == 0)
{
CheckEntrySignals();
}
}
//+------------------------------------------------------------------+
//| Update indicator values |
//+------------------------------------------------------------------+
bool UpdateIndicators()
{
if(CopyBuffer(handleFastMA, 0, 0, 3, fastMA) < 3)
{
Print("Failed to copy Fast MA data");
return false;
}
if(CopyBuffer(handleSlowMA, 0, 0, 3, slowMA) < 3)
{
Print("Failed to copy Slow MA data");
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Check if current time is within trading hours |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
MqlDateTime tm;
TimeToStruct(TimeCurrent(), tm);
int currentHour = tm.hour;
if(StartHour <= EndHour)
{
return (currentHour >= StartHour && currentHour < EndHour);
}
else
{
return (currentHour >= StartHour || currentHour < EndHour);
}
}
//+------------------------------------------------------------------+
//| Check for entry signals |
//+------------------------------------------------------------------+
void CheckEntrySignals()
{
//--- Get current price
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
//--- Check for buy signal (Fast MA crosses above Slow MA)
if(fastMA[1] > slowMA[1] && fastMA[2] <= slowMA[2])
{
if(fastMA[0] > slowMA[0]) // Confirm trend
{
OpenBuyPosition(ask);
}
}
//--- Check for sell signal (Fast MA crosses below Slow MA)
if(fastMA[1] < slowMA[1] && fastMA[2] >= slowMA[2])
{
if(fastMA[0] < slowMA[0]) // Confirm trend
{
OpenSellPosition(bid);
}
}
}
//+------------------------------------------------------------------+
//| Open Buy Position |
//+------------------------------------------------------------------+
void OpenBuyPosition(double price)
{
double sl = CalculateStopLoss(ORDER_TYPE_BUY, price);
double tp = CalculateTakeProfit(ORDER_TYPE_BUY, price);
double lots = CalculateLotSize(price, sl);
//--- Normalize values
sl = NormalizeDouble(sl, _Digits);
tp = NormalizeDouble(tp, _Digits);
lots = NormalizeLot(lots);
//--- Validate lot size
if(lots < minLot || lots > maxLot)
{
Print("Invalid lot size: ", lots);
return;
}
//--- Send order
if(trade.Buy(lots, _Symbol, price, sl, tp, TradeComment))
{
Print("Buy order opened successfully. Ticket: ", trade.ResultOrder());
Print("Price: ", price, " SL: ", sl, " TP: ", tp, " Lots: ", lots);
}
else
{
Print("Failed to open buy order. Error: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Open Sell Position |
//+------------------------------------------------------------------+
void OpenSellPosition(double price)
{
double sl = CalculateStopLoss(ORDER_TYPE_SELL, price);
double tp = CalculateTakeProfit(ORDER_TYPE_SELL, price);
double lots = CalculateLotSize(price, sl);
//--- Normalize values
sl = NormalizeDouble(sl, _Digits);
tp = NormalizeDouble(tp, _Digits);
lots = NormalizeLot(lots);
//--- Validate lot size
if(lots < minLot || lots > maxLot)
{
Print("Invalid lot size: ", lots);
return;
}
//--- Send order
if(trade.Sell(lots, _Symbol, price, sl, tp, TradeComment))
{
Print("Sell order opened successfully. Ticket: ", trade.ResultOrder());
Print("Price: ", price, " SL: ", sl, " TP: ", tp, " Lots: ", lots);
}
else
{
Print("Failed to open sell order. Error: ", GetLastError());
}
}
//+------------------------------------------------------------------+
//| Calculate Stop Loss |
//+------------------------------------------------------------------+
double CalculateStopLoss(ENUM_ORDER_TYPE orderType, double price)
{
double sl = 0;
double slDistance = StopLossPips * point * 10; // Convert pips to price
if(orderType == ORDER_TYPE_BUY)
{
sl = price - slDistance;
}
else if(orderType == ORDER_TYPE_SELL)
{
sl = price + slDistance;
}
return sl;
}
//+------------------------------------------------------------------+
//| Calculate Take Profit |
//+------------------------------------------------------------------+
double CalculateTakeProfit(ENUM_ORDER_TYPE orderType, double price)
{
double tp = 0;
double tpDistance = TakeProfitPips * point * 10; // Convert pips to price
if(orderType == ORDER_TYPE_BUY)
{
tp = price + tpDistance;
}
else if(orderType == ORDER_TYPE_SELL)
{
tp = price - tpDistance;
}
return tp;
}
//+------------------------------------------------------------------+
//| Calculate Lot Size based on risk |
//+------------------------------------------------------------------+
double CalculateLotSize(double entryPrice, double stopLoss)
{
if(UseFixedLot)
return FixedLotSize;
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = accountBalance * (RiskPercent / 100.0);
double slDistance = MathAbs(entryPrice - stopLoss);
if(slDistance == 0)
return MinLotSize;
double ticksDistance = slDistance / tickSize;
double riskPerLot = ticksDistance * tickValue;
if(riskPerLot == 0)
return MinLotSize;
double lots = riskAmount / riskPerLot;
//--- Apply limits
if(lots > MaxLotSize)
lots = MaxLotSize;
if(lots < MinLotSize)
lots = MinLotSize;
return lots;
}
//+------------------------------------------------------------------+
//| Normalize lot size |
//+------------------------------------------------------------------+
double NormalizeLot(double lots)
{
lots = MathFloor(lots / lotStep) * lotStep;
if(lots < minLot)
lots = minLot;
if(lots > maxLot)
lots = maxLot;
return NormalizeDouble(lots, 2);
}
//+------------------------------------------------------------------+
//| Count positions |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket <= 0)
continue;
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
count++;
}
}
return count;
}
//+------------------------------------------------------------------+
//| Manage existing positions (Trailing Stop) |
//+------------------------------------------------------------------+
void ManagePositions()
{
if(!UseTrailingStop)
return;
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket <= 0)
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol ||
PositionGetInteger(POSITION_MAGIC) != MagicNumber)
continue;
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double newSL = currentSL;
double trailDistance = TrailingStopPips * point * 10;
double trailStep = TrailingStepPips * point * 10;
if(posType == POSITION_TYPE_BUY)
{
double trailPrice = bid - trailDistance;
if(trailPrice > currentSL + trailStep || currentSL == 0)
{
newSL = NormalizeDouble(trailPrice, _Digits);
if(newSL != currentSL && newSL > positionOpenPrice)
{
if(trade.PositionModify(ticket, newSL, currentTP))
{
Print("Trailing stop updated for Buy position. Ticket: ", ticket, " New SL: ", newSL);
}
}
}
}
else if(posType == POSITION_TYPE_SELL)
{
double trailPrice = ask + trailDistance;
if(trailPrice < currentSL - trailStep || currentSL == 0)
{
newSL = NormalizeDouble(trailPrice, _Digits);
if(newSL != currentSL && newSL < positionOpenPrice)
{
if(trade.PositionModify(ticket, newSL, currentTP))
{
Print("Trailing stop updated for Sell position. Ticket: ", ticket, " New SL: ", newSL);
}
}
}
}
}
}
//+------------------------------------------------------------------+