- Remove MQL5-specific indicator properties - Replace iMA handles with direct iMA function calls - Replace PlotIndexSetString with SetIndexLabel - Replace IndicatorRelease with MQL4 equivalent - Replace CopyBuffer with direct iMA calls - Replace MqlDateTime with TimeHour and TimeDayOfWeek - Fix all MQL4/MQL5 compatibility issues
698 lines
22 KiB
Plaintext
698 lines
22 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| MAEA.mq4 |
|
|
//| Moving Average Expert Advisor |
|
|
//| Advanced Trend-Following Strategy |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "MAEA"
|
|
#property link "https://git.moreminimore.com/kunthawat/MAEA"
|
|
#property version "1.00"
|
|
#property strict
|
|
#property indicator_chart_window
|
|
#property indicator_buffers 5
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Input Parameters |
|
|
//+------------------------------------------------------------------+
|
|
input string Section1 = "=== EMA Settings ===";
|
|
input int EMAPeriod = 30; // EMA Period for all lines
|
|
|
|
input string Section2 = "=== Lot Settings ===";
|
|
input double LotSizeNormal = 0.01; // Lot size without pullback
|
|
input double LotSizePullback = 0.02; // Lot size with pullback
|
|
|
|
input string Section3 = "=== Risk Management ===";
|
|
input double TakeProfitUSD = 5.0; // Take Profit target in USD
|
|
input int BreakevenPoints = 100; // Points to trigger breakeven
|
|
input int TrailingStopPoints = 100; // Trailing stop distance
|
|
input double MaxDrawdownPercent = 10.0;// Max drawdown percentage
|
|
|
|
input string Section4 = "=== Filters ===";
|
|
input int MaxSpread = 30; // Maximum allowed spread in points
|
|
input int VolumePeriod = 20; // Period for volume average
|
|
input bool UseMTFFilter = true; // Enable MTF filter
|
|
input bool UseNewsFilter = true; // Enable news filter
|
|
input string NewsAvoidHours = "14,15,20,21"; // Hours to avoid (Thailand time)
|
|
input string NewsAvoidDays = "1,5"; // Days to avoid (Monday=1, Friday=5)
|
|
|
|
input string Section5 = "=== General ===";
|
|
input int MagicNumber = 12345; // EA unique identifier
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Global Variables |
|
|
//+------------------------------------------------------------------+
|
|
// Indicator buffers
|
|
double EMAHighBuffer[];
|
|
double EMAMediumBuffer[];
|
|
double EMALowBuffer[];
|
|
double HighBorderBuffer[];
|
|
double LowBorderBuffer[];
|
|
|
|
// State tracking
|
|
bool PullbackBuy = false;
|
|
bool PullbackSell = false;
|
|
bool BreakevenTriggered = false;
|
|
double OrderOpenPrice = 0;
|
|
double OrderStopLoss = 0;
|
|
double OrderTakeProfit = 0;
|
|
int OrderTicket = 0;
|
|
|
|
// Drawdown tracking
|
|
double AccountEquityPeak = 0;
|
|
|
|
// Previous bar values for signal detection
|
|
double PrevClose = 0;
|
|
double PrevEMAHigh = 0;
|
|
double PrevEMAMedium = 0;
|
|
double PrevEMALow = 0;
|
|
|
|
// News filter arrays
|
|
int AvoidHoursArray[];
|
|
int AvoidDaysArray[];
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// Set indicator buffers
|
|
SetIndexBuffer(0, EMAHighBuffer);
|
|
SetIndexBuffer(1, EMAMediumBuffer);
|
|
SetIndexBuffer(2, EMALowBuffer);
|
|
SetIndexBuffer(3, HighBorderBuffer);
|
|
SetIndexBuffer(4, LowBorderBuffer);
|
|
|
|
// Set indicator labels
|
|
SetIndexLabel(0, "EMA High");
|
|
SetIndexLabel(1, "EMA Medium");
|
|
SetIndexLabel(2, "EMA Low");
|
|
SetIndexLabel(3, "High Border");
|
|
SetIndexLabel(4, "Low Border");
|
|
|
|
// Set indicator styles
|
|
SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 2, clrLightBlue);
|
|
SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 2, clrYellow);
|
|
SetIndexStyle(2, DRAW_LINE, STYLE_SOLID, 2, clrOrange);
|
|
SetIndexStyle(3, DRAW_LINE, STYLE_DASH, 1, clrPurple);
|
|
SetIndexStyle(4, DRAW_LINE, STYLE_DASH, 1, clrPurple);
|
|
|
|
// Parse news filter arrays
|
|
ParseNewsFilterArrays();
|
|
|
|
// Initialize account equity peak
|
|
AccountEquityPeak = AccountEquity();
|
|
|
|
Print("MAEA initialized successfully");
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
Print("MAEA deinitialized");
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom indicator iteration function |
|
|
//+------------------------------------------------------------------+
|
|
int OnCalculate(const int rates_total,
|
|
const int prev_calculated,
|
|
const datetime &time[],
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[],
|
|
const long &tick_volume[],
|
|
const long &volume[],
|
|
const int &spread[])
|
|
{
|
|
// Check for minimum bars
|
|
if(rates_total < EMAPeriod + 1) return(0);
|
|
|
|
// Set arrays as series
|
|
ArraySetAsSeries(EMAHighBuffer, false);
|
|
ArraySetAsSeries(EMAMediumBuffer, false);
|
|
ArraySetAsSeries(EMALowBuffer, false);
|
|
ArraySetAsSeries(HighBorderBuffer, false);
|
|
ArraySetAsSeries(LowBorderBuffer, false);
|
|
|
|
// Calculate start position
|
|
int start;
|
|
if(prev_calculated == 0)
|
|
{
|
|
start = EMAPeriod;
|
|
// Initialize buffers
|
|
for(int i = 0; i < start; i++)
|
|
{
|
|
EMAHighBuffer[i] = 0;
|
|
EMAMediumBuffer[i] = 0;
|
|
EMALowBuffer[i] = 0;
|
|
HighBorderBuffer[i] = 0;
|
|
LowBorderBuffer[i] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
start = prev_calculated - 1;
|
|
}
|
|
|
|
// Calculate EMA values and border lines
|
|
for(int i = start; i < rates_total; i++)
|
|
{
|
|
// Get EMA values using iMA function (MQL4 style)
|
|
double emaHigh = iMA(Symbol(), PERIOD_CURRENT, EMAPeriod, 0, MODE_EMA, PRICE_HIGH, i);
|
|
double emaMedium = iMA(Symbol(), PERIOD_CURRENT, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE, i);
|
|
double emaLow = iMA(Symbol(), PERIOD_CURRENT, EMAPeriod, 0, MODE_EMA, PRICE_LOW, i);
|
|
|
|
// Calculate range
|
|
double range = emaMedium - emaLow;
|
|
|
|
// Calculate border lines
|
|
double highBorder = emaHigh + range;
|
|
double lowBorder = emaLow - range;
|
|
|
|
// Store values in buffers
|
|
EMAHighBuffer[i] = emaHigh;
|
|
EMAMediumBuffer[i] = emaMedium;
|
|
EMALowBuffer[i] = emaLow;
|
|
HighBorderBuffer[i] = highBorder;
|
|
LowBorderBuffer[i] = lowBorder;
|
|
}
|
|
|
|
// Only process trading logic on new bar
|
|
static datetime lastBarTime = 0;
|
|
if(time[rates_total - 1] != lastBarTime)
|
|
{
|
|
lastBarTime = time[rates_total - 1];
|
|
|
|
// Update previous bar values
|
|
int lastBar = rates_total - 1;
|
|
int prevBar = rates_total - 2;
|
|
|
|
if(prevBar >= EMAPeriod)
|
|
{
|
|
PrevClose = close[prevBar];
|
|
PrevEMAHigh = EMAHighBuffer[prevBar];
|
|
PrevEMAMedium = EMAMediumBuffer[prevBar];
|
|
PrevEMALow = EMALowBuffer[prevBar];
|
|
|
|
// Process trading logic
|
|
ProcessTradingLogic(rates_total, time, open, high, low, close, tick_volume, spread);
|
|
}
|
|
}
|
|
|
|
// Manage open orders on every tick
|
|
ManageOpenOrders();
|
|
|
|
return(rates_total);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Get EMA value from handle |
|
|
//+------------------------------------------------------------------+
|
|
double GetEMAValue(int timeframe, int applied_price, int shift)
|
|
{
|
|
return iMA(Symbol(), timeframe, EMAPeriod, 0, MODE_EMA, applied_price, shift);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Parse news filter arrays |
|
|
//+------------------------------------------------------------------+
|
|
void ParseNewsFilterArrays()
|
|
{
|
|
// Parse hours
|
|
string hoursStr = NewsAvoidHours;
|
|
ArrayResize(AvoidHoursArray, 0);
|
|
while(hoursStr != "")
|
|
{
|
|
int pos = StringFind(hoursStr, ",");
|
|
string hourStr;
|
|
if(pos >= 0)
|
|
{
|
|
hourStr = StringSubstr(hoursStr, 0, pos);
|
|
hoursStr = StringSubstr(hoursStr, pos + 1);
|
|
}
|
|
else
|
|
{
|
|
hourStr = hoursStr;
|
|
hoursStr = "";
|
|
}
|
|
StringTrimLeft(hourStr);
|
|
StringTrimRight(hourStr);
|
|
if(hourStr != "")
|
|
{
|
|
int hour = (int)StringToInteger(hourStr);
|
|
int size = ArraySize(AvoidHoursArray);
|
|
ArrayResize(AvoidHoursArray, size + 1);
|
|
AvoidHoursArray[size] = hour;
|
|
}
|
|
}
|
|
|
|
// Parse days
|
|
string daysStr = NewsAvoidDays;
|
|
ArrayResize(AvoidDaysArray, 0);
|
|
while(daysStr != "")
|
|
{
|
|
int pos = StringFind(daysStr, ",");
|
|
string dayStr;
|
|
if(pos >= 0)
|
|
{
|
|
dayStr = StringSubstr(daysStr, 0, pos);
|
|
daysStr = StringSubstr(daysStr, pos + 1);
|
|
}
|
|
else
|
|
{
|
|
dayStr = daysStr;
|
|
daysStr = "";
|
|
}
|
|
StringTrimLeft(dayStr);
|
|
StringTrimRight(dayStr);
|
|
if(dayStr != "")
|
|
{
|
|
int day = (int)StringToInteger(dayStr);
|
|
int size = ArraySize(AvoidDaysArray);
|
|
ArrayResize(AvoidDaysArray, size + 1);
|
|
AvoidDaysArray[size] = day;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Process trading logic |
|
|
//+------------------------------------------------------------------+
|
|
void ProcessTradingLogic(const int rates_total,
|
|
const datetime &time[],
|
|
const double &open[],
|
|
const double &high[],
|
|
const double &low[],
|
|
const double &close[],
|
|
const long &tick_volume[],
|
|
const int &spread[])
|
|
{
|
|
// Check if we have an open order
|
|
if(HasOpenOrder()) return;
|
|
|
|
// Check drawdown
|
|
if(!CheckDrawdown()) return;
|
|
|
|
// Check news filter
|
|
if(UseNewsFilter && !CheckNewsFilter(time[rates_total - 1])) return;
|
|
|
|
// Check spread filter
|
|
if(spread[rates_total - 1] > MaxSpread) return;
|
|
|
|
// Check volume filter
|
|
if(!CheckVolumeFilter(tick_volume, rates_total)) return;
|
|
|
|
// Check MTF filter
|
|
if(UseMTFFilter && !CheckMTFFilter()) return;
|
|
|
|
// Get current values
|
|
int lastBar = rates_total - 1;
|
|
double currentClose = close[lastBar];
|
|
double currentEMAHigh = EMAHighBuffer[lastBar];
|
|
double currentEMALow = EMALowBuffer[lastBar];
|
|
|
|
// Detect pullback signals
|
|
DetectPullbackSignals(currentClose, currentEMAHigh, PrevEMAHigh, currentEMALow, PrevEMALow, PrevEMAMedium);
|
|
|
|
// Detect breakthrough signals
|
|
if(DetectBreakthroughBuy(currentClose, currentEMAHigh, PrevClose, PrevEMAHigh))
|
|
{
|
|
OpenBuyOrder();
|
|
}
|
|
else if(DetectBreakthroughSell(currentClose, currentEMALow, PrevClose, PrevEMALow))
|
|
{
|
|
OpenSellOrder();
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Detect pullback signals |
|
|
//+------------------------------------------------------------------+
|
|
void DetectPullbackSignals(double currentClose, double currentEMAHigh, double prevEMAHigh,
|
|
double currentEMALow, double prevEMALow, double prevEMAMedium)
|
|
{
|
|
// Buy pullback: price moved down to hit a line, then closed above it
|
|
if(PrevClose < prevEMAMedium && currentClose > currentEMALow)
|
|
{
|
|
PullbackBuy = true;
|
|
}
|
|
else if(PrevClose < prevEMALow && currentClose > currentEMALow)
|
|
{
|
|
PullbackBuy = true;
|
|
}
|
|
else if(PrevClose < prevEMAHigh && currentClose > currentEMAHigh)
|
|
{
|
|
PullbackBuy = true;
|
|
}
|
|
|
|
// Sell pullback: price moved up to hit a line, then closed below it
|
|
if(PrevClose > prevEMAMedium && currentClose < currentEMAHigh)
|
|
{
|
|
PullbackSell = true;
|
|
}
|
|
else if(PrevClose > prevEMAHigh && currentClose < currentEMAHigh)
|
|
{
|
|
PullbackSell = true;
|
|
}
|
|
else if(PrevClose > prevEMALow && currentClose < currentEMALow)
|
|
{
|
|
PullbackSell = true;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Detect breakthrough buy signal |
|
|
//+------------------------------------------------------------------+
|
|
bool DetectBreakthroughBuy(double currentClose, double currentEMAHigh, double prevClose, double prevEMAHigh)
|
|
{
|
|
return (prevClose <= prevEMAHigh && currentClose > currentEMAHigh);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Detect breakthrough sell signal |
|
|
//+------------------------------------------------------------------+
|
|
bool DetectBreakthroughSell(double currentClose, double currentEMALow, double prevClose, double prevEMALow)
|
|
{
|
|
return (prevClose >= prevEMALow && currentClose < currentEMALow);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check volume filter |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckVolumeFilter(const long &tick_volume[], int rates_total)
|
|
{
|
|
if(rates_total < VolumePeriod + 1) return false;
|
|
|
|
// Calculate average volume
|
|
double avgVolume = 0;
|
|
for(int i = rates_total - VolumePeriod - 1; i < rates_total - 1; i++)
|
|
{
|
|
avgVolume += tick_volume[i];
|
|
}
|
|
avgVolume /= VolumePeriod;
|
|
|
|
// Check if current volume is above average
|
|
return (tick_volume[rates_total - 1] > avgVolume);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check MTF filter |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckMTFFilter()
|
|
{
|
|
double d1Close = iClose(Symbol(), PERIOD_D1, 0);
|
|
double d1EMAHigh = GetEMAValue(PERIOD_D1, PRICE_HIGH, 0);
|
|
double d1EMALow = GetEMAValue(PERIOD_D1, PRICE_LOW, 0);
|
|
|
|
if(d1Close > d1EMAHigh)
|
|
{
|
|
// Only buy orders allowed
|
|
return true;
|
|
}
|
|
else if(d1Close < d1EMALow)
|
|
{
|
|
// Only sell orders allowed
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Price between lines - no orders allowed
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check news filter |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckNewsFilter(datetime currentTime)
|
|
{
|
|
// Convert to Thailand timezone (UTC+7)
|
|
// MT4 server time is usually UTC, so add 7 hours
|
|
datetime thailandTime = currentTime + 7 * 3600; // 7 hours in seconds
|
|
int thailandHour = TimeHour(thailandTime);
|
|
int thailandDay = TimeDayOfWeek(thailandTime);
|
|
|
|
// Check if current hour is in avoid list
|
|
for(int i = 0; i < ArraySize(AvoidHoursArray); i++)
|
|
{
|
|
if(thailandHour == AvoidHoursArray[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check if current day is in avoid list
|
|
for(int i = 0; i < ArraySize(AvoidDaysArray); i++)
|
|
{
|
|
if(thailandDay == AvoidDaysArray[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check drawdown |
|
|
//+------------------------------------------------------------------+
|
|
bool CheckDrawdown()
|
|
{
|
|
double currentEquity = AccountEquity();
|
|
|
|
// Update peak equity
|
|
if(currentEquity > AccountEquityPeak)
|
|
{
|
|
AccountEquityPeak = currentEquity;
|
|
}
|
|
|
|
// Calculate drawdown percentage
|
|
double drawdownPercent = ((AccountEquityPeak - currentEquity) / AccountEquityPeak) * 100;
|
|
|
|
// Check if drawdown exceeds limit
|
|
if(drawdownPercent >= MaxDrawdownPercent)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Open buy order |
|
|
//+------------------------------------------------------------------+
|
|
void OpenBuyOrder()
|
|
{
|
|
double lotSize = PullbackBuy ? LotSizePullback : LotSizeNormal;
|
|
double stopLoss = LowBorderBuffer[ArraySize(LowBorderBuffer) - 1];
|
|
double takeProfit = CalculateTakeProfit(lotSize, OP_BUY);
|
|
|
|
OrderTicket = OrderSend(Symbol(), OP_BUY, lotSize, Ask, 3, stopLoss, takeProfit, "MAEA Buy", MagicNumber, 0, clrBlue);
|
|
|
|
if(OrderTicket > 0)
|
|
{
|
|
OrderOpenPrice = Ask;
|
|
OrderStopLoss = stopLoss;
|
|
OrderTakeProfit = takeProfit;
|
|
BreakevenTriggered = false;
|
|
|
|
// Reset pullback flags
|
|
PullbackBuy = false;
|
|
PullbackSell = false;
|
|
|
|
Print("Buy order opened: Ticket=", OrderTicket, " Price=", Ask, " SL=", stopLoss, " TP=", takeProfit);
|
|
}
|
|
else
|
|
{
|
|
Print("Error opening buy order: ", GetLastError());
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Open sell order |
|
|
//+------------------------------------------------------------------+
|
|
void OpenSellOrder()
|
|
{
|
|
double lotSize = PullbackSell ? LotSizePullback : LotSizeNormal;
|
|
double stopLoss = HighBorderBuffer[ArraySize(HighBorderBuffer) - 1];
|
|
double takeProfit = CalculateTakeProfit(lotSize, OP_SELL);
|
|
|
|
OrderTicket = OrderSend(Symbol(), OP_SELL, lotSize, Bid, 3, stopLoss, takeProfit, "MAEA Sell", MagicNumber, 0, clrRed);
|
|
|
|
if(OrderTicket > 0)
|
|
{
|
|
OrderOpenPrice = Bid;
|
|
OrderStopLoss = stopLoss;
|
|
OrderTakeProfit = takeProfit;
|
|
BreakevenTriggered = false;
|
|
|
|
// Reset pullback flags
|
|
PullbackBuy = false;
|
|
PullbackSell = false;
|
|
|
|
Print("Sell order opened: Ticket=", OrderTicket, " Price=", Bid, " SL=", stopLoss, " TP=", takeProfit);
|
|
}
|
|
else
|
|
{
|
|
Print("Error opening sell order: ", GetLastError());
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate take profit in pips from USD target |
|
|
//+------------------------------------------------------------------+
|
|
double CalculateTakeProfit(double lotSize, int orderType)
|
|
{
|
|
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
|
|
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
|
|
double pipValue = tickValue / tickSize * Point;
|
|
|
|
double profitInPips = TakeProfitUSD / (lotSize * pipValue);
|
|
double profitInPrice = profitInPips * Point;
|
|
|
|
if(orderType == OP_BUY)
|
|
{
|
|
return Ask + profitInPrice;
|
|
}
|
|
else
|
|
{
|
|
return Bid - profitInPrice;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if we have an open order |
|
|
//+------------------------------------------------------------------+
|
|
bool HasOpenOrder()
|
|
{
|
|
if(OrderTicket == 0) return false;
|
|
|
|
if(OrderSelect(OrderTicket, SELECT_BY_TICKET))
|
|
{
|
|
if(OrderCloseTime() == 0)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
OrderTicket = 0;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OrderTicket = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Manage open orders |
|
|
//+------------------------------------------------------------------+
|
|
void ManageOpenOrders()
|
|
{
|
|
if(!HasOpenOrder()) return;
|
|
|
|
if(!OrderSelect(OrderTicket, SELECT_BY_TICKET)) return;
|
|
|
|
double currentPrice = OrderType() == OP_BUY ? Bid : Ask;
|
|
double currentProfit = OrderProfit();
|
|
double openPrice = OrderOpenPrice();
|
|
|
|
// Check take profit
|
|
if(currentProfit >= TakeProfitUSD)
|
|
{
|
|
CloseOrder();
|
|
return;
|
|
}
|
|
|
|
// Check stop loss
|
|
if(OrderType() == OP_BUY && currentPrice <= OrderStopLoss())
|
|
{
|
|
CloseOrder();
|
|
return;
|
|
}
|
|
else if(OrderType() == OP_SELL && currentPrice >= OrderStopLoss())
|
|
{
|
|
CloseOrder();
|
|
return;
|
|
}
|
|
|
|
// Calculate profit in points
|
|
double profitPoints = 0;
|
|
if(OrderType() == OP_BUY)
|
|
{
|
|
profitPoints = (currentPrice - openPrice) / Point;
|
|
}
|
|
else
|
|
{
|
|
profitPoints = (openPrice - currentPrice) / Point;
|
|
}
|
|
|
|
// Check breakeven
|
|
if(!BreakevenTriggered && profitPoints >= BreakevenPoints)
|
|
{
|
|
double newSL = openPrice;
|
|
if(OrderModify(OrderTicket, openPrice, newSL, OrderTakeProfit(), 0, clrNONE))
|
|
{
|
|
BreakevenTriggered = true;
|
|
OrderStopLoss = newSL;
|
|
Print("Breakeven triggered: SL moved to ", newSL);
|
|
}
|
|
}
|
|
|
|
// Check trailing stop
|
|
if(BreakevenTriggered && profitPoints >= BreakevenPoints + TrailingStopPoints)
|
|
{
|
|
double newSL;
|
|
if(OrderType() == OP_BUY)
|
|
{
|
|
newSL = currentPrice - TrailingStopPoints * Point;
|
|
if(newSL > OrderStopLoss())
|
|
{
|
|
if(OrderModify(OrderTicket, openPrice, newSL, OrderTakeProfit(), 0, clrNONE))
|
|
{
|
|
OrderStopLoss = newSL;
|
|
Print("Trailing stop: SL moved to ", newSL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newSL = currentPrice + TrailingStopPoints * Point;
|
|
if(newSL < OrderStopLoss())
|
|
{
|
|
if(OrderModify(OrderTicket, openPrice, newSL, OrderTakeProfit(), 0, clrNONE))
|
|
{
|
|
OrderStopLoss = newSL;
|
|
Print("Trailing stop: SL moved to ", newSL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Close order |
|
|
//+------------------------------------------------------------------+
|
|
void CloseOrder()
|
|
{
|
|
if(OrderSelect(OrderTicket, SELECT_BY_TICKET))
|
|
{
|
|
double closePrice = OrderType() == OP_BUY ? Bid : Ask;
|
|
if(OrderClose(OrderTicket, OrderLots(), closePrice, 3, clrNONE))
|
|
{
|
|
Print("Order closed: Ticket=", OrderTicket, " Profit=", OrderProfit());
|
|
OrderTicket = 0;
|
|
BreakevenTriggered = false;
|
|
}
|
|
else
|
|
{
|
|
Print("Error closing order: ", GetLastError());
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+ |