473 lines
15 KiB
MQL5
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|