859 行
31 KiB
Text
859 行
31 KiB
Text
//+------------------------------------------------------------------+
| |
//| 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");
| |
}
| |
}
| |
| |
//+------------------------------------------------------------------+
|