MGNFYXAU/GOLD

859 lines
31 KiB
Text
Raw Permalink Normal View History

2025-10-23 06:22:25 +00:00
//+------------------------------------------------------------------+
//| GoldXAU_Scalper_Full.mq5 |
//| Gold-only scalper EA with multi-timeframe bias and trailing TP |
//+------------------------------------------------------------------+
#property copyright "Assistant"
#property version "1.12"
#property strict
#include <Trade\Trade.mqh>
CTrade trade;
//---------------------- User inputs --------------------------------
input string TradeSymbol = "XAUUSD"; // symbol (change if your broker uses a different name)
input double FixedLotSize = 0.01; // fixed lot size used when UseFixedLot=true
input bool UseFixedLot = true; // always trade fixed lot (true) or dynamic sizing (false)
input double RiskPercent = 0.5; // if UseFixedLot=false -> use this % of balance as risk per trade
// timeframe and indicator params
input ENUM_TIMEFRAMES TF = PERIOD_M5; // entry TF
input int SMA_Fast = 5;
input int SMA_Slow = 10;
input int SMA_Trend = 50;
input int ATR_Period = 14;
input double ATR_Multiplier = 0.9; // SL = ATR * multiplier
input double RewardRatio = 1.2; // TP = SL * RewardRatio
// scalper vs normal
input bool ScalperMode = false; // if true use smaller TP/SL multipliers for quick scalps
input double Scalper_RR = 0.9; // R:R for scalper (tp = rr * sl)
input int Scalper_MaxTrades = 2; // allow more small trades if scalper
// Trailing & rally behavior
input bool UseTrailingTP = true;
input double TrailingStartATRMult = 1.0; // start trailing once price is this * ATR in profit
input double TrailingPercent = 1.0; // percent behind market to place trailing TP (1.0 means TP = price*(1 - 0.01) for longs)
input double TrailingStepPoints = 0.5; // minimum price change to update TP (in price units)
// multi-timeframe bias
input bool UseMultiTFBias = true;
input ENUM_TIMEFRAMES BiasTF1 = PERIOD_H1;
input ENUM_TIMEFRAMES BiasTF2 = PERIOD_H4;
input ENUM_TIMEFRAMES BiasTF3 = PERIOD_D1;
input int BiasSMA_Period = 50; // SMA for bias check (on each bias TF)
input int BiasRequiredMajority = 2; // how many TFs must agree to consider bullish/bearish
// trade direction
enum ENUM_TRADE_DIR {DIR_BOTH=0, DIR_LONG_ONLY=1, DIR_SHORT_ONLY=2};
input ENUM_TRADE_DIR AllowedDirection = DIR_BOTH;
// safety
input int MagicNumber = 20251023;
input int MaxOpenTrades = 3;
input double MinFreeMarginBufferUSD = 5.0; // require at least this free margin (USD) to open trades
// misc
input int SlippagePoints = 200; // allowed slippage in points for order send
// NEW: Enhanced Risk Management
input double SpreadBufferMultiplier = 0.8; // add this * spread to SL for buffer
input double SlippageBufferPoints = 2.0; // add this many points to SL for slippage buffer
input int MaxDailyTrades = 5; // maximum trades per day
input int MinCooldownMinutes = 30; // minimum minutes between trades
input bool UseTimeFilters = true; // skip trading during news/gaps
input int SkipStartHour = 22; // skip trading from this hour (GMT)
input int SkipEndHour = 2; // until this hour (GMT)
input bool UseBreakEven = true; // move SL to breakeven at +0.5R profit
input double BreakEvenRMultiple = 0.5; // move SL to BE at this R profit
input bool UsePartialTP = false; // close half position at smaller TP
input double PartialTPRMultiple = 0.6; // close half at this R profit
input double TrailingATRMultiple = 0.25; // trail TP at this ATR distance
//---------------------- globals ------------------------------------
double pointSz = 0.0;
int symbolDigits = 0;
// NEW: Enhanced tracking variables
datetime lastTradeTime = 0;
int dailyTradeCount = 0;
datetime lastDailyReset = 0;
//---------------------- helper declarations -------------------------
double GetATR(const string symbol, ENUM_TIMEFRAMES tf, int period, int shift);
double GetSMA(const string symbol, ENUM_TIMEFRAMES tf, int period, int shift);
int CountMyPositions(const string symbol);
void PrintSymbolInfo(const string symbol);
bool IsMarketBiasBullish();
bool IsMarketBiasBearish();
double CalcLotByRisk_USD(const string symbol, double riskUSD, double entryPrice, double slPrice);
double NormalizeLotToStep(const string symbol, double lot);
bool CanOpenMoreTrades();
void ManageTrailing();
ulong OpenBuy(double lot, double sl, double tp);
ulong OpenSell(double lot, double sl, double tp);
double GetPriceAsk(const string symbol);
double GetPriceBid(const string symbol);
// NEW: Enhanced helper functions
bool IsTimeToTrade();
bool CanTradeNow();
double CalculateSpreadBuffer();
double CalculateSlippageBuffer();
void ManageBreakEven();
void ManagePartialTP();
void ResetDailyCounter();
//+------------------------------------------------------------------+
//| Expert initialization |
//+------------------------------------------------------------------+
int OnInit()
{
// ensure symbol exists and select it
if(!SymbolSelect(TradeSymbol,true))
{
Print("ERROR: cannot select symbol ", TradeSymbol);
return(INIT_FAILED);
}
pointSz = SymbolInfoDouble(TradeSymbol,SYMBOL_POINT);
symbolDigits = (int)SymbolInfoInteger(TradeSymbol,SYMBOL_DIGITS);
if(pointSz<=0)
pointSz = _Point;
PrintFormat("EA init symbol=%s point=%g symbolDigits=%d", TradeSymbol, pointSz, symbolDigits);
// print symbol trade properties for verification
PrintSymbolInfo(TradeSymbol);
// safety: ensure AllowedDirection valid (remove modification of input parameter)
// AllowedDirection is an input parameter and cannot be modified
if(AllowedDirection<DIR_BOTH || AllowedDirection>DIR_SHORT_ONLY)
{
Print("WARNING: Invalid AllowedDirection value. Using DIR_BOTH as default.");
}
Print("XAU Scalp EA initialized. Magic=", MagicNumber);
// Initialize enhanced tracking
lastTradeTime = 0;
dailyTradeCount = 0;
lastDailyReset = TimeCurrent();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinit |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA deinitialized, reason=", reason);
}
//+------------------------------------------------------------------+
//| OnTick |
//+------------------------------------------------------------------+
void OnTick()
{
// Reset daily counter if new day
ResetDailyCounter();
// Check if we can trade now (time filters, cooldown, daily limits)
if(!CanTradeNow())
return;
// Ensure we have enough market data
MqlRates rates[];
int copied = CopyRates(TradeSymbol,TF,0,3,rates); // need only last 2-3 bars
if(copied<3)
return;
// Calculate indicators on closed bar (shift=1)
double smaFastPrev = GetSMA(TradeSymbol,TF,SMA_Fast,1);
double smaSlowPrev = GetSMA(TradeSymbol,TF,SMA_Slow,1);
double smaTrendPrev = GetSMA(TradeSymbol,TF,SMA_Trend,1);
double atrPrev = GetATR(TradeSymbol,TF,ATR_Period,1);
// Validate indicators
if(smaFastPrev <= 0 || smaSlowPrev <= 0 || smaTrendPrev <= 0 || atrPrev <= 0)
{
Print("Invalid indicator values - skipping tick");
return;
}
// entry conditions
bool trendUp = (iClose(TradeSymbol,TF,1) > smaTrendPrev);
bool trendDown = (iClose(TradeSymbol,TF,1) < smaTrendPrev);
// detect cross on closed bar
double smaFastPrev2 = GetSMA(TradeSymbol,TF,SMA_Fast,2);
double smaSlowPrev2 = GetSMA(TradeSymbol,TF,SMA_Slow,2);
bool crossUp = (smaFastPrev > smaSlowPrev) && (smaFastPrev2 <= smaSlowPrev2);
bool crossDown = (smaFastPrev < smaSlowPrev) && (smaFastPrev2 >= smaSlowPrev2);
// multi TF bias check
bool biasBullish = true, biasBearish = true;
if(UseMultiTFBias)
{
biasBullish = IsMarketBiasBullish(); // majority H1/H4/D1 bullish?
biasBearish = IsMarketBiasBearish();
}
// Count my EA positions on symbol
int myPositions = CountMyPositions(TradeSymbol);
// trailing modify for existing positions
if(myPositions>0 && UseTrailingTP)
ManageTrailing();
// NEW: Manage break-even and partial TP
if(myPositions>0)
{
if(UseBreakEven)
ManageBreakEven();
if(UsePartialTP)
ManagePartialTP();
}
// STOP: limit trades
if(!CanOpenMoreTrades())
return;
// Decide to open long?
bool wantLong = false, wantShort = false;
if(AllowedDirection!=DIR_SHORT_ONLY)
{
// only consider long side if allowed
if(trendUp && crossUp)
{
if(!UseMultiTFBias || biasBullish)
wantLong = true;
}
}
if(AllowedDirection!=DIR_LONG_ONLY)
{
// consider short side if allowed
if(trendDown && crossDown)
{
if(!UseMultiTFBias || biasBearish)
wantShort = true;
}
}
// Prepare order params if needed
if(wantLong || wantShort)
{
double ask = GetPriceAsk(TradeSymbol);
double bid = GetPriceBid(TradeSymbol);
if(ask<=0 || bid<=0)
return;
double slPrice=0, tpPrice=0;
double lot = FixedLotSize;
if(!UseFixedLot)
{
// calculate lot by risk (risk = RiskPercent% of balance)
double balanceNow = AccountInfoDouble(ACCOUNT_BALANCE);
double riskUSD = balanceNow * (RiskPercent/100.0);
// estimate SL using ATR
double slAmount = atrPrev * ATR_Multiplier;
if(ScalperMode)
slAmount = atrPrev * (ATR_Multiplier*0.7);
if(wantLong)
slPrice = ask - slAmount;
else
slPrice = bid + slAmount;
lot = CalcLotByRisk_USD(TradeSymbol, riskUSD, (wantLong?ask:bid), slPrice);
lot = NormalizeLotToStep(TradeSymbol, lot);
}
else
{
// Use fixed lot, but ensure normalized to broker step
lot = NormalizeLotToStep(TradeSymbol, FixedLotSize);
}
// final safety lot clamp
double minLot = SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(TradeSymbol,SYMBOL_VOLUME_MAX);
if(minLot==0)
minLot = 0.01;
if(maxLot==0)
maxLot = 100;
if(lot < minLot)
lot = minLot;
if(lot > maxLot)
lot = maxLot;
// compute TP & SL if not already set
if(!UseFixedLot) { /* slPrice already computed */ }
else
{
// compute using ATR for fixed-lot mode
double slAmount = atrPrev * ATR_Multiplier;
if(ScalperMode)
slAmount = atrPrev * (ATR_Multiplier*0.7);
// NEW: Add spread and slippage buffers
double spreadBuffer = CalculateSpreadBuffer();
double slippageBuffer = CalculateSlippageBuffer();
slAmount += spreadBuffer + slippageBuffer;
if(wantLong)
slPrice = ask - slAmount;
else
slPrice = bid + slAmount;
}
double rr = RewardRatio;
if(ScalperMode)
rr = Scalper_RR;
if(wantLong)
{
tpPrice = ask + (ask - slPrice) * rr;
}
else
{
tpPrice = bid - (slPrice - bid) * rr;
}
// Validate SL/TP distances
if(wantLong && (slPrice >= ask || tpPrice <= ask))
{
Print("Invalid SL/TP for long trade");
return;
}
if(wantShort && (slPrice <= bid || tpPrice >= bid))
{
Print("Invalid SL/TP for short trade");
return;
}
// final safety checks: free margin
double freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
if(freeMargin < MinFreeMarginBufferUSD)
{
Print("Not enough free margin to open trades. FreeMargin=", freeMargin);
return;
}
// Open the trade(s)
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(SlippagePoints);
if(wantLong)
{
ulong ticket = OpenBuy(lot, slPrice, tpPrice);
if(ticket>0)
{
PrintFormat("Opened BUY ticket=%I64u lot=%.2f sl=%.3f tp=%.3f", ticket, lot, slPrice, tpPrice);
lastTradeTime = TimeCurrent();
dailyTradeCount++;
}
else
PrintFormat("Buy open failed, err=%d", GetLastError());
}
if(wantShort)
{
ulong ticket = OpenSell(lot, slPrice, tpPrice);
if(ticket>0)
{
PrintFormat("Opened SELL ticket=%I64u lot=%.2f sl=%.3f tp=%.3f", ticket, lot, slPrice, tpPrice);
lastTradeTime = TimeCurrent();
dailyTradeCount++;
}
else
PrintFormat("Sell open failed, err=%d", GetLastError());
}
} // end wantLong/wantShort
}
//+------------------------------------------------------------------+
//| Helpers: indicator wrappers |
//+------------------------------------------------------------------+
double GetATR(const string symbol, ENUM_TIMEFRAMES tf, int period, int shift)
{
int handle = iATR(symbol, tf, period);
if(handle == INVALID_HANDLE)
return(0);
double buffer[];
if(CopyBuffer(handle, 0, shift, 1, buffer) <= 0)
return(0);
return(buffer[0]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double GetSMA(const string symbol, ENUM_TIMEFRAMES tf, int period, int shift)
{
int handle = iMA(symbol, tf, period, 0, MODE_SMA, PRICE_CLOSE);
if(handle == INVALID_HANDLE)
return(0);
double buffer[];
if(CopyBuffer(handle, 0, shift, 1, buffer) <= 0)
return(0);
return(buffer[0]);
}
//+------------------------------------------------------------------+
//| Count positions opened by this EA |
//+------------------------------------------------------------------+
int CountMyPositions(const string symbol)
{
int count = 0;
for(int i=0; i<PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
string posSymbol = PositionGetString(POSITION_SYMBOL);
int posMagic = (int)PositionGetInteger(POSITION_MAGIC);
if(posSymbol == symbol && posMagic == MagicNumber)
count++;
}
}
return(count);
}
//+------------------------------------------------------------------+
//| Print symbol information for debugging |
//+------------------------------------------------------------------+
void PrintSymbolInfo(const string symbol)
{
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
double stepLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
PrintFormat("Symbol %s: MinLot=%.2f MaxLot=%.2f Step=%.2f TickValue=%.2f TickSize=%.5f",
symbol, minLot, maxLot, stepLot, tickValue, tickSize);
}
//+------------------------------------------------------------------+
//| Check if market bias is bullish across multiple timeframes |
//+------------------------------------------------------------------+
bool IsMarketBiasBullish()
{
int bullishCount = 0;
// Check H1 bias
double h1SMA = GetSMA(TradeSymbol, BiasTF1, BiasSMA_Period, 1);
double h1Close = iClose(TradeSymbol, BiasTF1, 1);
if(h1Close > h1SMA)
bullishCount++;
// Check H4 bias
double h4SMA = GetSMA(TradeSymbol, BiasTF2, BiasSMA_Period, 1);
double h4Close = iClose(TradeSymbol, BiasTF2, 1);
if(h4Close > h4SMA)
bullishCount++;
// Check D1 bias
double d1SMA = GetSMA(TradeSymbol, BiasTF3, BiasSMA_Period, 1);
double d1Close = iClose(TradeSymbol, BiasTF3, 1);
if(d1Close > d1SMA)
bullishCount++;
return(bullishCount >= BiasRequiredMajority);
}
//+------------------------------------------------------------------+
//| Check if market bias is bearish across multiple timeframes |
//+------------------------------------------------------------------+
bool IsMarketBiasBearish()
{
int bearishCount = 0;
// Check H1 bias
double h1SMA = GetSMA(TradeSymbol, BiasTF1, BiasSMA_Period, 1);
double h1Close = iClose(TradeSymbol, BiasTF1, 1);
if(h1Close < h1SMA)
bearishCount++;
// Check H4 bias
double h4SMA = GetSMA(TradeSymbol, BiasTF2, BiasSMA_Period, 1);
double h4Close = iClose(TradeSymbol, BiasTF2, 1);
if(h4Close < h4SMA)
bearishCount++;
// Check D1 bias
double d1SMA = GetSMA(TradeSymbol, BiasTF3, BiasSMA_Period, 1);
double d1Close = iClose(TradeSymbol, BiasTF3, 1);
if(d1Close < d1SMA)
bearishCount++;
return(bearishCount >= BiasRequiredMajority);
}
//+------------------------------------------------------------------+
//| Calculate lot size based on risk in USD |
//+------------------------------------------------------------------+
double CalcLotByRisk_USD(const string symbol, double riskUSD, double entryPrice, double slPrice)
{
double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
if(tickValue <= 0 || tickSize <= 0)
{
Print("Invalid tick value/size for ", symbol);
return(FixedLotSize);
}
double slDistance = MathAbs(entryPrice - slPrice);
double slInTicks = slDistance / tickSize;
double lossPerLot = slInTicks * tickValue;
if(lossPerLot <= 0)
{
Print("Invalid loss per lot calculation");
return(FixedLotSize);
}
double lot = riskUSD / lossPerLot;
return(lot);
}
//+------------------------------------------------------------------+
//| Normalize lot size to broker step |
//+------------------------------------------------------------------+
double NormalizeLotToStep(const string symbol, double lot)
{
double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
if(step <= 0)
step = 0.01;
double normalized = MathFloor(lot / step) * step;
return(normalized);
}
//+------------------------------------------------------------------+
//| Check if we can open more trades |
//+------------------------------------------------------------------+
bool CanOpenMoreTrades()
{
int currentTrades = CountMyPositions(TradeSymbol);
int maxTrades = ScalperMode ? Scalper_MaxTrades : MaxOpenTrades;
if(currentTrades >= maxTrades)
{
PrintFormat("Max trades reached: %d/%d", currentTrades, maxTrades);
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
//| Manage trailing take profit for open positions |
//+------------------------------------------------------------------+
void ManageTrailing()
{
for(int i=0; i<PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
string posSymbol = PositionGetString(POSITION_SYMBOL);
int posMagic = (int)PositionGetInteger(POSITION_MAGIC);
if(posSymbol == TradeSymbol && posMagic == MagicNumber)
{
double posPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double posTP = PositionGetDouble(POSITION_TP);
double posSL = PositionGetDouble(POSITION_SL);
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double currentPrice = (posType == POSITION_TYPE_BUY) ?
SymbolInfoDouble(TradeSymbol, SYMBOL_BID) :
SymbolInfoDouble(TradeSymbol, SYMBOL_ASK);
double atr = GetATR(TradeSymbol, TF, ATR_Period, 1);
double trailingStart = atr * TrailingStartATRMult;
double profit = 0;
if(posType == POSITION_TYPE_BUY)
profit = currentPrice - posPrice;
else
profit = posPrice - currentPrice;
if(profit >= trailingStart)
{
double newTP = 0;
double trailingDistance = atr * TrailingATRMultiple;
if(posType == POSITION_TYPE_BUY)
{
// NEW: ATR-based trailing instead of percentage
newTP = currentPrice - trailingDistance;
if(newTP > posTP + TrailingStepPoints)
{
trade.PositionModify(ticket, posSL, NormalizeDouble(newTP, symbolDigits));
PrintFormat("Trailing TP updated for BUY ticket %I64u: %.3f -> %.3f (ATR-based)", ticket, posTP, newTP);
}
}
else
{
// NEW: ATR-based trailing instead of percentage
newTP = currentPrice + trailingDistance;
if(newTP < posTP - TrailingStepPoints)
{
trade.PositionModify(ticket, posSL, NormalizeDouble(newTP, symbolDigits));
PrintFormat("Trailing TP updated for SELL ticket %I64u: %.3f -> %.3f (ATR-based)", ticket, posTP, newTP);
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Open buy order |
//+------------------------------------------------------------------+
ulong OpenBuy(double lot, double sl, double tp)
{
double ask = SymbolInfoDouble(TradeSymbol, SYMBOL_ASK);
if(ask <= 0)
return(0);
bool result = trade.Buy(lot, TradeSymbol, ask,
NormalizeDouble(sl, symbolDigits),
NormalizeDouble(tp, symbolDigits),
"XAU Scalper Buy");
return(result ? trade.ResultOrder() : 0);
}
//+------------------------------------------------------------------+
//| Open sell order |
//+------------------------------------------------------------------+
ulong OpenSell(double lot, double sl, double tp)
{
double bid = SymbolInfoDouble(TradeSymbol, SYMBOL_BID);
if(bid <= 0)
return(0);
bool result = trade.Sell(lot, TradeSymbol, bid,
NormalizeDouble(sl, symbolDigits),
NormalizeDouble(tp, symbolDigits),
"XAU Scalper Sell");
return(result ? trade.ResultOrder() : 0);
}
//+------------------------------------------------------------------+
//| Get current ask price |
//+------------------------------------------------------------------+
double GetPriceAsk(const string symbol)
{
return(SymbolInfoDouble(symbol, SYMBOL_ASK));
}
//+------------------------------------------------------------------+
//| Get current bid price |
//+------------------------------------------------------------------+
double GetPriceBid(const string symbol)
{
return(SymbolInfoDouble(symbol, SYMBOL_BID));
}
//+------------------------------------------------------------------+
//| NEW: Check if it's time to trade (time filters) |
//+------------------------------------------------------------------+
bool IsTimeToTrade()
{
if(!UseTimeFilters)
return(true);
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
// Skip trading during specified hours (e.g., 22:00-02:00 GMT for gaps)
if(SkipStartHour > SkipEndHour)
{
// Cross midnight (e.g., 22:00 to 02:00)
if(dt.hour >= SkipStartHour || dt.hour < SkipEndHour)
return(false);
}
else
{
// Same day (e.g., 10:00 to 15:00)
if(dt.hour >= SkipStartHour && dt.hour < SkipEndHour)
return(false);
}
return(true);
}
//+------------------------------------------------------------------+
//| NEW: Check if we can trade now (all conditions) |
//+------------------------------------------------------------------+
bool CanTradeNow()
{
// Check time filters
if(!IsTimeToTrade())
{
Print("Skipping trade - outside trading hours");
return(false);
}
// Check daily trade limit
if(dailyTradeCount >= MaxDailyTrades)
{
PrintFormat("Daily trade limit reached: %d/%d", dailyTradeCount, MaxDailyTrades);
return(false);
}
// Check cooldown period
if(lastTradeTime > 0)
{
int minutesSinceLastTrade = (int)((TimeCurrent() - lastTradeTime) / 60);
if(minutesSinceLastTrade < MinCooldownMinutes)
{
PrintFormat("Cooldown period active: %d/%d minutes", minutesSinceLastTrade, MinCooldownMinutes);
return(false);
}
}
return(true);
}
//+------------------------------------------------------------------+
//| NEW: Calculate spread buffer for SL |
//+------------------------------------------------------------------+
double CalculateSpreadBuffer()
{
double ask = SymbolInfoDouble(TradeSymbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(TradeSymbol, SYMBOL_BID);
double spread = ask - bid;
return(spread * SpreadBufferMultiplier);
}
//+------------------------------------------------------------------+
//| NEW: Calculate slippage buffer for SL |
//+------------------------------------------------------------------+
double CalculateSlippageBuffer()
{
return(SlippageBufferPoints * pointSz);
}
//+------------------------------------------------------------------+
//| NEW: Manage break-even functionality |
//+------------------------------------------------------------------+
void ManageBreakEven()
{
for(int i=0; i<PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
string posSymbol = PositionGetString(POSITION_SYMBOL);
int posMagic = (int)PositionGetInteger(POSITION_MAGIC);
if(posSymbol == TradeSymbol && posMagic == MagicNumber)
{
double posPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double posSL = PositionGetDouble(POSITION_SL);
double posTP = PositionGetDouble(POSITION_TP);
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double currentPrice = (posType == POSITION_TYPE_BUY) ?
SymbolInfoDouble(TradeSymbol, SYMBOL_BID) :
SymbolInfoDouble(TradeSymbol, SYMBOL_ASK);
double profit = 0;
if(posType == POSITION_TYPE_BUY)
profit = currentPrice - posPrice;
else
profit = posPrice - currentPrice;
// Calculate R multiple
double slDistance = MathAbs(posPrice - posSL);
if(slDistance > 0)
{
double rMultiple = profit / slDistance;
// Move SL to breakeven + small buffer
if(rMultiple >= BreakEvenRMultiple && posSL != posPrice)
{
double newSL = posPrice + (posType == POSITION_TYPE_BUY ? pointSz * 2 : -pointSz * 2);
trade.PositionModify(ticket, NormalizeDouble(newSL, symbolDigits), posTP);
PrintFormat("Moved SL to breakeven for ticket %I64u: %.3f -> %.3f", ticket, posSL, newSL);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| NEW: Manage partial take profit |
//+------------------------------------------------------------------+
void ManagePartialTP()
{
for(int i=0; i<PositionsTotal(); i++)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
string posSymbol = PositionGetString(POSITION_SYMBOL);
int posMagic = (int)PositionGetInteger(POSITION_MAGIC);
if(posSymbol == TradeSymbol && posMagic == MagicNumber)
{
double posPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double posSL = PositionGetDouble(POSITION_SL);
double posVolume = PositionGetDouble(POSITION_VOLUME);
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double currentPrice = (posType == POSITION_TYPE_BUY) ?
SymbolInfoDouble(TradeSymbol, SYMBOL_BID) :
SymbolInfoDouble(TradeSymbol, SYMBOL_ASK);
double profit = 0;
if(posType == POSITION_TYPE_BUY)
profit = currentPrice - posPrice;
else
profit = posPrice - currentPrice;
// Calculate R multiple
double slDistance = MathAbs(posPrice - posSL);
if(slDistance > 0)
{
double rMultiple = profit / slDistance;
// Close half position at partial TP
if(rMultiple >= PartialTPRMultiple && posVolume > 0.02) // Only if volume > 0.02
{
double closeVolume = posVolume * 0.5; // Close half
if(trade.PositionClosePartial(ticket, closeVolume))
{
PrintFormat("Partial TP executed for ticket %I64u: closed %.2f lots at %.3f", ticket, closeVolume, currentPrice);
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| NEW: Reset daily trade counter |
//+------------------------------------------------------------------+
void ResetDailyCounter()
{
MqlDateTime currentDT, lastDT;
TimeToStruct(TimeCurrent(), currentDT);
TimeToStruct(lastDailyReset, lastDT);
// If new day, reset counter
if(currentDT.day != lastDT.day || currentDT.mon != lastDT.mon || currentDT.year != lastDT.year)
{
dailyTradeCount = 0;
lastDailyReset = TimeCurrent();
Print("Daily trade counter reset - new day");
}
}
//+------------------------------------------------------------------+