Initial commit: MQL Trading Bots
- MultiSignal Confluence EA v1.11 (stop loss bug fixed) - Harmonic Pattern Finder v2 (optimized) - Candlestick Pattern EA (fixed) - Pivot Fade EA v4 (fixed) - Bot series (10001, 10002, EnhancedEA) Performance: ~19% return in 2 months on 00k account
This commit is contained in:
832
FadePivot2_v4_Fixed.mq5
Normal file
832
FadePivot2_v4_Fixed.mq5
Normal file
@@ -0,0 +1,832 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| FadePivot2_v4_Fixed.mq5 |
|
||||
//| Garfield Heron / Abbey Road Tech |
|
||||
//| https://abbeyroadtechnology.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "2024"
|
||||
#property link "https://abbeyroadtechnology.com"
|
||||
#property version "2.10"
|
||||
#property strict
|
||||
|
||||
/***** User Inputs *****/
|
||||
input double LotSize = 0.01; // Lot size for trades
|
||||
input int Slippage = 3; // Max slippage in points
|
||||
input int TP_OffsetPips = 0; // +/- offset from R1 or S1 (in pips)
|
||||
input double StopLossFactor = 1.0; // Multiplier for (R2 - R1) or (S1 - S2)
|
||||
input double TakeProfitFactor = 1.0; // Multiplier for take-profit distance (0 = target R1/S1 exactly)
|
||||
input bool OnlyOneSetPerDay = true; // If true, place only once per day
|
||||
input bool AllowNewOrdersIfPreviousOpen = true; // Place new orders even if old remain
|
||||
input bool CloseEndOfDay = true; // If true, do EoD cleanup
|
||||
input bool CloseOnlyPendingEndOfDay = false; // If true => close only pending orders at EoD
|
||||
input bool CloseOpenOrdersEndOfDay = false; // If true, close open positions at EoD
|
||||
input long MagicNumber = 98765; // Magic number for this EA
|
||||
|
||||
/***** Trailing-Stop Inputs *****/
|
||||
input double TrailingStartPips = 10.0; // in profit at least this many pips to start trailing
|
||||
input double TrailingDistancePips = 5.0; // trailing distance behind best price
|
||||
|
||||
/***** Day-of-Week Logic *****/
|
||||
input bool TradeMonday = true;
|
||||
input bool TradeTuesday = true;
|
||||
input bool TradeWednesday = true;
|
||||
input bool TradeThursday = true;
|
||||
input bool TradeFriday = true;
|
||||
input bool TradeSaturday = false;
|
||||
input bool TradeSunday = false;
|
||||
|
||||
/***** Global variables *****/
|
||||
static datetime TradeDate = 0; // Tracks the current day
|
||||
static bool OrdersPlaced = false;
|
||||
static bool EodHandled = false; // Track if EOD already processed
|
||||
|
||||
/***** Pivots *****/
|
||||
static double P, R1, R2, S1, S2;
|
||||
|
||||
/***** For multiple-trade trailing: track best price per position ticket *****/
|
||||
#define MAX_POSITIONS 100
|
||||
|
||||
static ulong posTicketArray[MAX_POSITIONS];
|
||||
static double bestPriceArray[MAX_POSITIONS];
|
||||
static int posCount=0;
|
||||
|
||||
// Prototypes
|
||||
void AddOrUpdateBestPrice(ulong ticket, long posType, double newPrice);
|
||||
double GetBestPrice(ulong ticket, long posType);
|
||||
void RemovePosition(ulong ticket);
|
||||
void CleanUpClosedPositions();
|
||||
|
||||
void CalculateDailyPivots();
|
||||
void PlaceFadeOrders();
|
||||
bool HasOpenOrPendingWithMagic(long magic);
|
||||
bool CloseAllByMagic(long magic);
|
||||
void PlaceLimitOrder(ENUM_ORDER_TYPE orderType, double entryPrice, double slPrice, double tpPrice);
|
||||
void ApplyTrailingStop();
|
||||
bool IsTradingDay();
|
||||
void ModifyPositionSL(long posType, double newSL, ulong ticket);
|
||||
bool ValidateLimitOrderPrice(ENUM_ORDER_TYPE orderType, double entryPrice);
|
||||
double GetStopLevel();
|
||||
|
||||
/*****=====================================================================
|
||||
* OnInit / OnDeinit
|
||||
*======================================================================*****/
|
||||
int OnInit()
|
||||
{
|
||||
Print("FadePivot2_v4_Fixed EA initializing (multi-position trailing + day-of-week)...");
|
||||
|
||||
// Input validation
|
||||
if(LotSize <= 0)
|
||||
{
|
||||
Print("ERROR: LotSize must be positive");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
if(StopLossFactor < 0)
|
||||
{
|
||||
Print("ERROR: StopLossFactor cannot be negative");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
if(TakeProfitFactor < 0)
|
||||
{
|
||||
Print("ERROR: TakeProfitFactor cannot be negative");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
if(TrailingStartPips < 0 || TrailingDistancePips < 0)
|
||||
{
|
||||
Print("ERROR: Trailing stop values cannot be negative");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
if(TakeProfitFactor > 0 && TakeProfitFactor > StopLossFactor)
|
||||
{
|
||||
Print("WARNING: TakeProfitFactor > StopLossFactor may result in unfavorable R:R ratio");
|
||||
}
|
||||
|
||||
// Check if symbol supports the requested order types
|
||||
int fillingMode = (int)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
|
||||
if(fillingMode == 0)
|
||||
{
|
||||
Print("WARNING: Symbol may not support requested order filling mode");
|
||||
}
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
Print("FadePivot2_v4_Fixed EA deinitialized. reason=", reason);
|
||||
posCount = 0;
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* OnTick
|
||||
*======================================================================*****/
|
||||
void OnTick()
|
||||
{
|
||||
// 1) Check day-of-week
|
||||
if(!IsTradingDay())
|
||||
return;
|
||||
|
||||
// 2) Check for new D1 bar
|
||||
datetime today = iTime(_Symbol, PERIOD_D1, 0);
|
||||
if(today != TradeDate)
|
||||
{
|
||||
CalculateDailyPivots();
|
||||
TradeDate = today;
|
||||
OrdersPlaced = false;
|
||||
EodHandled = false; // Reset EOD flag for new day
|
||||
Print("New daily bar. Pivots recalc. R2=", R2, " S2=", S2);
|
||||
|
||||
CleanUpClosedPositions();
|
||||
}
|
||||
|
||||
// 3) Place daily fade orders if needed
|
||||
if(!OrdersPlaced)
|
||||
{
|
||||
if(!OnlyOneSetPerDay)
|
||||
{
|
||||
PlaceFadeOrders();
|
||||
OrdersPlaced = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!HasOpenOrPendingWithMagic(MagicNumber) || AllowNewOrdersIfPreviousOpen)
|
||||
{
|
||||
PlaceFadeOrders();
|
||||
OrdersPlaced = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Skipping new orders (OneSetPerDay + existing trades).");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4) End-of-day close logic (improved timing)
|
||||
if(CloseEndOfDay && !EodHandled)
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
// Check from 23:58 onwards to ensure we don't miss the window
|
||||
if(dt.hour == 23 && dt.min >= 58)
|
||||
{
|
||||
Print("FadePivot2_v4_Fixed: EoD => cleanup...");
|
||||
CloseAllByMagic(MagicNumber);
|
||||
EodHandled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset EOD flag at midnight
|
||||
if(EodHandled)
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
if(dt.hour == 0 && dt.min == 0)
|
||||
EodHandled = false;
|
||||
}
|
||||
|
||||
// 5) Trailing Stop for multiple trades
|
||||
ApplyTrailingStop();
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* IsTradingDay()
|
||||
*======================================================================*****/
|
||||
bool IsTradingDay()
|
||||
{
|
||||
MqlDateTime mt;
|
||||
TimeToStruct(TimeCurrent(), mt);
|
||||
int dow = mt.day_of_week; // 0=Sun,1=Mon,2=Tue,3=Wed,4=Thu,5=Fri,6=Sat
|
||||
switch(dow)
|
||||
{
|
||||
case 0: return TradeSunday;
|
||||
case 1: return TradeMonday;
|
||||
case 2: return TradeTuesday;
|
||||
case 3: return TradeWednesday;
|
||||
case 4: return TradeThursday;
|
||||
case 5: return TradeFriday;
|
||||
case 6: return TradeSaturday;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* CalculateDailyPivots
|
||||
*======================================================================*****/
|
||||
void CalculateDailyPivots()
|
||||
{
|
||||
int shift = 1; // "yesterday"
|
||||
double prevHigh = iHigh(_Symbol, PERIOD_D1, shift);
|
||||
double prevLow = iLow(_Symbol, PERIOD_D1, shift);
|
||||
double prevClose= iClose(_Symbol, PERIOD_D1, shift);
|
||||
|
||||
if(prevHigh == 0 || prevLow == 0 || prevClose == 0)
|
||||
{
|
||||
Print("ERROR: Failed to get previous day data for pivot calculation");
|
||||
return;
|
||||
}
|
||||
|
||||
P = (prevHigh + prevLow + prevClose)/3.0;
|
||||
R1 = 2.0*P - prevLow;
|
||||
S1 = 2.0*P - prevHigh;
|
||||
R2 = P + (prevHigh - prevLow);
|
||||
S2 = P - (prevHigh - prevLow);
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* GetStopLevel - helper to get minimum stop distance
|
||||
*======================================================================*****/
|
||||
double GetStopLevel()
|
||||
{
|
||||
long stopLevelPoints = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
if(stopLevelPoints <= 0)
|
||||
stopLevelPoints = 10; // Default to 10 points if broker returns 0
|
||||
return stopLevelPoints * _Point;
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* ValidateLimitOrderPrice - ensure limit order is valid
|
||||
*======================================================================*****/
|
||||
bool ValidateLimitOrderPrice(ENUM_ORDER_TYPE orderType, double entryPrice)
|
||||
{
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
double stopLevel = GetStopLevel();
|
||||
|
||||
if(orderType == ORDER_TYPE_SELL_LIMIT)
|
||||
{
|
||||
if(entryPrice <= ask + stopLevel)
|
||||
{
|
||||
Print("ERROR: SELL LIMIT price (", entryPrice, ") must be above Ask (", ask, ") + stop level (", stopLevel, ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(orderType == ORDER_TYPE_BUY_LIMIT)
|
||||
{
|
||||
if(entryPrice >= bid - stopLevel)
|
||||
{
|
||||
Print("ERROR: BUY LIMIT price (", entryPrice, ") must be below Bid (", bid, ") - stop level (", stopLevel, ")");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* PlaceFadeOrders
|
||||
*======================================================================*****/
|
||||
void PlaceFadeOrders()
|
||||
{
|
||||
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
double stopLevel = GetStopLevel();
|
||||
|
||||
// SELL LIMIT at R2
|
||||
{
|
||||
double entryPrice = NormalizeDouble(R2, digits);
|
||||
double distanceR1R2 = MathAbs(R2 - R1);
|
||||
|
||||
// Validate entry price
|
||||
if(!ValidateLimitOrderPrice(ORDER_TYPE_SELL_LIMIT, entryPrice))
|
||||
return;
|
||||
|
||||
// SL: above R2 by distance * factor
|
||||
double slPrice = R2 + (distanceR1R2 * StopLossFactor);
|
||||
|
||||
// FIX: TP calculation - use distance from entry, not multiply price
|
||||
// Target is R1 with optional offset, or use factor to scale the distance
|
||||
double tpPrice;
|
||||
if(TakeProfitFactor == 0)
|
||||
tpPrice = R1 + (TP_OffsetPips * pt); // Target R1 exactly with offset
|
||||
else
|
||||
tpPrice = R2 - (distanceR1R2 * TakeProfitFactor) + (TP_OffsetPips * pt);
|
||||
|
||||
// Ensure SL is valid distance from entry
|
||||
if((slPrice - entryPrice) < stopLevel)
|
||||
{
|
||||
Print("WARNING: Sell SL too close, adjusting to stop level");
|
||||
slPrice = entryPrice + stopLevel + pt;
|
||||
}
|
||||
|
||||
// Ensure TP is valid distance from entry
|
||||
if((entryPrice - tpPrice) < stopLevel)
|
||||
{
|
||||
Print("WARNING: Sell TP too close, adjusting to stop level");
|
||||
tpPrice = entryPrice - stopLevel - pt;
|
||||
}
|
||||
|
||||
// Normalize prices
|
||||
slPrice = NormalizeDouble(slPrice, digits);
|
||||
tpPrice = NormalizeDouble(tpPrice, digits);
|
||||
|
||||
Print("Placing SELL LIMIT at R2= ", entryPrice,
|
||||
", SL= ", slPrice, ", TP= ", tpPrice);
|
||||
PlaceLimitOrder(ORDER_TYPE_SELL_LIMIT, entryPrice, slPrice, tpPrice);
|
||||
}
|
||||
|
||||
// BUY LIMIT at S2
|
||||
{
|
||||
double entryPrice = NormalizeDouble(S2, digits);
|
||||
double distanceS1S2 = MathAbs(S1 - S2);
|
||||
|
||||
// Validate entry price
|
||||
if(!ValidateLimitOrderPrice(ORDER_TYPE_BUY_LIMIT, entryPrice))
|
||||
return;
|
||||
|
||||
// SL: below S2 by distance * factor
|
||||
double slPrice = S2 - (distanceS1S2 * StopLossFactor);
|
||||
|
||||
// FIX: TP calculation - use distance from entry
|
||||
double tpPrice;
|
||||
if(TakeProfitFactor == 0)
|
||||
tpPrice = S1 + (TP_OffsetPips * pt); // Target S1 exactly with offset
|
||||
else
|
||||
tpPrice = S2 + (distanceS1S2 * TakeProfitFactor) + (TP_OffsetPips * pt);
|
||||
|
||||
// Ensure SL is valid distance from entry
|
||||
if((entryPrice - slPrice) < stopLevel)
|
||||
{
|
||||
Print("WARNING: Buy SL too close, adjusting to stop level");
|
||||
slPrice = entryPrice - stopLevel - pt;
|
||||
}
|
||||
|
||||
// Ensure TP is valid distance from entry
|
||||
if((tpPrice - entryPrice) < stopLevel)
|
||||
{
|
||||
Print("WARNING: Buy TP too close, adjusting to stop level");
|
||||
tpPrice = entryPrice + stopLevel + pt;
|
||||
}
|
||||
|
||||
// Normalize prices
|
||||
slPrice = NormalizeDouble(slPrice, digits);
|
||||
tpPrice = NormalizeDouble(tpPrice, digits);
|
||||
|
||||
Print("Placing BUY LIMIT at S2= ", entryPrice,
|
||||
", SL= ", slPrice, ", TP= ", tpPrice);
|
||||
PlaceLimitOrder(ORDER_TYPE_BUY_LIMIT, entryPrice, slPrice, tpPrice);
|
||||
}
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* PlaceLimitOrder - helper with retry logic
|
||||
*======================================================================*****/
|
||||
void PlaceLimitOrder(ENUM_ORDER_TYPE orderType,
|
||||
double entryPrice,
|
||||
double slPrice,
|
||||
double tpPrice)
|
||||
{
|
||||
MqlTradeRequest request;
|
||||
MqlTradeResult result;
|
||||
ZeroMemory(request);
|
||||
ZeroMemory(result);
|
||||
|
||||
request.action = TRADE_ACTION_PENDING;
|
||||
request.symbol = _Symbol;
|
||||
request.magic = MagicNumber;
|
||||
request.volume = LotSize;
|
||||
request.deviation = Slippage;
|
||||
request.type = orderType;
|
||||
request.price = entryPrice;
|
||||
request.sl = slPrice;
|
||||
request.tp = tpPrice;
|
||||
|
||||
// Try different filling modes
|
||||
if(!OrderSend(request, result))
|
||||
{
|
||||
// Try with RETURN filling mode if FOK fails
|
||||
request.type_filling = ORDER_FILLING_RETURN;
|
||||
if(!OrderSend(request, result))
|
||||
{
|
||||
// Try IOC as last resort
|
||||
request.type_filling = ORDER_FILLING_IOC;
|
||||
OrderSend(request, result);
|
||||
}
|
||||
}
|
||||
|
||||
request.type_time = ORDER_TIME_GTC;
|
||||
request.comment = (orderType == ORDER_TYPE_SELL_LIMIT) ? "FadePivot SELL" : "FadePivot BUY";
|
||||
|
||||
if(!OrderSend(request, result))
|
||||
{
|
||||
int err = GetLastError();
|
||||
Print(__FUNCTION__, ": OrderSend failed. LastErr=", err);
|
||||
|
||||
// Retry logic for specific errors
|
||||
if(err == ERR_REQUOTE || err == ERR_PRICE_CHANGED || err == ERR_OFF_QUOTES)
|
||||
{
|
||||
Sleep(100);
|
||||
if(OrderSend(request, result))
|
||||
{
|
||||
Print("Retry succeeded after error ", err);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
|
||||
{
|
||||
Print("Limit order placed: type=",
|
||||
(orderType==ORDER_TYPE_SELL_LIMIT ? "SellLimit":"BuyLimit"),
|
||||
", ticket=", result.order);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Limit order failed. retcode=", result.retcode, ", lastErr=", GetLastError());
|
||||
|
||||
// Check for specific retcodes that might benefit from retry
|
||||
if(result.retcode == TRADE_RETCODE_REQUOTE ||
|
||||
result.retcode == TRADE_RETCODE_PRICE_OFF)
|
||||
{
|
||||
Sleep(100);
|
||||
ZeroMemory(result);
|
||||
if(OrderSend(request, result) &&
|
||||
(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE))
|
||||
{
|
||||
Print("Retry succeeded. ticket=", result.order);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* ApplyTrailingStop - handles multiple trades
|
||||
*======================================================================*****/
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
double trailingStart = TrailingStartPips * _Point;
|
||||
double trailingDist = TrailingDistancePips * _Point;
|
||||
|
||||
uint totalPositions = PositionsTotal();
|
||||
for(uint i=0; i < totalPositions; i++)
|
||||
{
|
||||
ulong posTicket = PositionGetTicket(i);
|
||||
if(posTicket == 0)
|
||||
continue;
|
||||
|
||||
if(!PositionSelectByTicket(posTicket))
|
||||
continue;
|
||||
|
||||
string sym = PositionGetString(POSITION_SYMBOL);
|
||||
long mgc = PositionGetInteger(POSITION_MAGIC);
|
||||
if(sym != _Symbol || mgc != MagicNumber)
|
||||
continue;
|
||||
|
||||
long posType = PositionGetInteger(POSITION_TYPE);
|
||||
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double currentSL = PositionGetDouble(POSITION_SL);
|
||||
|
||||
double cpx = (posType==POSITION_TYPE_BUY)
|
||||
? SymbolInfoDouble(_Symbol, SYMBOL_BID)
|
||||
: SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
double pipsInProfit=0.0;
|
||||
if(posType==POSITION_TYPE_BUY)
|
||||
pipsInProfit = (cpx - openPrice)/_Point;
|
||||
else
|
||||
pipsInProfit = (openPrice - cpx)/_Point;
|
||||
|
||||
if(pipsInProfit < TrailingStartPips)
|
||||
continue;
|
||||
|
||||
double oldBestPrice = GetBestPrice(posTicket, posType);
|
||||
bool improved = false;
|
||||
double newBest= oldBestPrice;
|
||||
|
||||
if(posType == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(oldBestPrice < 1e-8)
|
||||
{
|
||||
newBest= cpx; // Use current price (already in profit)
|
||||
improved= true;
|
||||
}
|
||||
else if(cpx > oldBestPrice)
|
||||
{
|
||||
newBest = cpx;
|
||||
improved= true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(oldBestPrice < 1e-8)
|
||||
{
|
||||
newBest= cpx;
|
||||
improved= true;
|
||||
}
|
||||
else if(cpx < oldBestPrice)
|
||||
{
|
||||
newBest= cpx;
|
||||
improved= true;
|
||||
}
|
||||
}
|
||||
|
||||
if(improved)
|
||||
AddOrUpdateBestPrice(posTicket, posType, newBest);
|
||||
|
||||
double potentialSL = 0.0;
|
||||
if(posType==POSITION_TYPE_BUY)
|
||||
{
|
||||
potentialSL = newBest - trailingDist;
|
||||
// Only move SL up
|
||||
if(potentialSL > currentSL + (_Point*0.5))
|
||||
{
|
||||
// Ensure new SL is not too close to current price
|
||||
if((cpx - potentialSL) >= GetStopLevel())
|
||||
ModifyPositionSL(posType, potentialSL, posTicket);
|
||||
}
|
||||
}
|
||||
else // SELL
|
||||
{
|
||||
potentialSL = newBest + trailingDist;
|
||||
// Only move SL down
|
||||
if(currentSL < 1e-8 || potentialSL < currentSL - (_Point*0.5))
|
||||
{
|
||||
// Ensure new SL is not too close to current price
|
||||
if((potentialSL - cpx) >= GetStopLevel())
|
||||
ModifyPositionSL(posType, potentialSL, posTicket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* ModifyPositionSL
|
||||
*======================================================================*****/
|
||||
void ModifyPositionSL(long posType, double newSL, ulong ticket)
|
||||
{
|
||||
MqlTradeRequest req;
|
||||
MqlTradeResult res;
|
||||
ZeroMemory(req);
|
||||
ZeroMemory(res);
|
||||
|
||||
req.action = TRADE_ACTION_SLTP;
|
||||
req.symbol = _Symbol;
|
||||
req.magic = MagicNumber;
|
||||
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
return;
|
||||
|
||||
double oldTP = PositionGetDouble(POSITION_TP);
|
||||
double vol = PositionGetDouble(POSITION_VOLUME);
|
||||
|
||||
req.position = ticket;
|
||||
req.volume = vol;
|
||||
req.sl = NormalizeDouble(newSL, _Digits);
|
||||
req.tp = oldTP;
|
||||
req.comment = "TrailingStopUpdate";
|
||||
|
||||
if(!OrderSend(req, res))
|
||||
{
|
||||
Print("ModifyPositionSL: OrderSend fail. code=", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
if(res.retcode==TRADE_RETCODE_DONE || res.retcode==TRADE_RETCODE_PLACED)
|
||||
Print("TrailingStopUpdate: new SL=", DoubleToString(newSL,_Digits), " for ticket=", ticket);
|
||||
else
|
||||
Print("TrailingStop retcode=", res.retcode, " err=", GetLastError());
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* HasOpenOrPendingWithMagic - FIXED for hedging mode
|
||||
*======================================================================*****/
|
||||
bool HasOpenOrPendingWithMagic(long magic)
|
||||
{
|
||||
// Check ALL positions (not just first - supports hedging mode)
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0)
|
||||
continue;
|
||||
|
||||
if(PositionSelectByTicket(ticket))
|
||||
{
|
||||
if(PositionGetInteger(POSITION_MAGIC) == magic &&
|
||||
PositionGetString(POSITION_SYMBOL) == _Symbol)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check pending orders
|
||||
int totalOrders = (int)OrdersTotal();
|
||||
for(int i=0; i<totalOrders; i++)
|
||||
{
|
||||
ulong ticket = OrderGetTicket(i);
|
||||
if(ticket == 0)
|
||||
continue;
|
||||
|
||||
if(OrderSelect(ticket))
|
||||
{
|
||||
long ordMagic = OrderGetInteger(ORDER_MAGIC);
|
||||
string ordSymbol = OrderGetString(ORDER_SYMBOL);
|
||||
if(ordMagic == magic && ordSymbol == _Symbol)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* CloseAllByMagic - End-of-Day (FIXED for multiple positions)
|
||||
*======================================================================*****/
|
||||
bool CloseAllByMagic(long magic)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if(!CloseOnlyPendingEndOfDay && CloseOpenOrdersEndOfDay)
|
||||
{
|
||||
// Close ALL positions with this magic (supports hedging)
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0)
|
||||
continue;
|
||||
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
continue;
|
||||
|
||||
if(PositionGetInteger(POSITION_MAGIC) != magic ||
|
||||
PositionGetString(POSITION_SYMBOL) != _Symbol)
|
||||
continue;
|
||||
|
||||
long pType = PositionGetInteger(POSITION_TYPE);
|
||||
double vol = PositionGetDouble(POSITION_VOLUME);
|
||||
double cPx = (pType==POSITION_TYPE_BUY)
|
||||
? SymbolInfoDouble(_Symbol, SYMBOL_BID)
|
||||
: SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
MqlTradeRequest clReq;
|
||||
MqlTradeResult clRes;
|
||||
ZeroMemory(clReq);
|
||||
ZeroMemory(clRes);
|
||||
|
||||
clReq.action = TRADE_ACTION_DEAL;
|
||||
clReq.symbol = _Symbol;
|
||||
clReq.volume = vol;
|
||||
clReq.magic = magic;
|
||||
clReq.position = ticket;
|
||||
clReq.deviation = Slippage;
|
||||
clReq.type = (pType==POSITION_TYPE_BUY)? ORDER_TYPE_SELL: ORDER_TYPE_BUY;
|
||||
clReq.price = cPx;
|
||||
clReq.comment = "FadePivot2_v4 EoD Close";
|
||||
|
||||
if(!OrderSend(clReq, clRes))
|
||||
{
|
||||
Print("CloseAllByMagic: close pos failed. err=", GetLastError());
|
||||
success = false;
|
||||
}
|
||||
else if(clRes.retcode != TRADE_RETCODE_DONE)
|
||||
{
|
||||
Print("CloseAllByMagic: close pos retcode=", clRes.retcode);
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Closed position #", ticket, " EoD.");
|
||||
RemovePosition(ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove pending orders
|
||||
int totalOrders = (int)OrdersTotal();
|
||||
for(int i=totalOrders-1; i>=0; i--)
|
||||
{
|
||||
ulong ticket = OrderGetTicket(i);
|
||||
if(ticket == 0)
|
||||
continue;
|
||||
|
||||
if(!OrderSelect(ticket))
|
||||
continue;
|
||||
|
||||
long omagic = OrderGetInteger(ORDER_MAGIC);
|
||||
string sym = OrderGetString(ORDER_SYMBOL);
|
||||
long otype = OrderGetInteger(ORDER_TYPE);
|
||||
|
||||
if(omagic != magic || sym != _Symbol)
|
||||
continue;
|
||||
|
||||
if(otype == ORDER_TYPE_BUY_LIMIT || otype == ORDER_TYPE_SELL_LIMIT ||
|
||||
otype == ORDER_TYPE_BUY_STOP || otype == ORDER_TYPE_SELL_STOP)
|
||||
{
|
||||
MqlTradeRequest odReq;
|
||||
MqlTradeResult odRes;
|
||||
ZeroMemory(odReq);
|
||||
ZeroMemory(odRes);
|
||||
|
||||
odReq.action = TRADE_ACTION_REMOVE;
|
||||
odReq.order = ticket;
|
||||
odReq.symbol = _Symbol;
|
||||
odReq.magic = magic;
|
||||
odReq.comment= "FadePivot2_v4 EoD Remove";
|
||||
|
||||
if(!OrderSend(odReq, odRes))
|
||||
{
|
||||
Print("CloseAllByMagic: remove order failed. err=", GetLastError());
|
||||
success = false;
|
||||
}
|
||||
else if(odRes.retcode != TRADE_RETCODE_DONE)
|
||||
{
|
||||
Print("CloseAllByMagic: remove order retcode=", odRes.retcode);
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Removed pending order #", ticket, " EoD.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* Array-based "map" for best price handling (with overflow protection)
|
||||
*======================================================================*****/
|
||||
|
||||
void AddOrUpdateBestPrice(ulong ticket, long posType, double newPrice)
|
||||
{
|
||||
// Try to find existing
|
||||
for(int i=0; i<posCount; i++)
|
||||
{
|
||||
if(posTicketArray[i] == ticket)
|
||||
{
|
||||
bestPriceArray[i] = newPrice;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found - try to add
|
||||
if(posCount >= MAX_POSITIONS)
|
||||
{
|
||||
// Try cleanup first
|
||||
CleanUpClosedPositions();
|
||||
|
||||
if(posCount >= MAX_POSITIONS)
|
||||
{
|
||||
Print("ERROR: Position tracking array full! Cannot add ticket ", ticket);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
posTicketArray[posCount] = ticket;
|
||||
bestPriceArray[posCount] = newPrice;
|
||||
posCount++;
|
||||
}
|
||||
|
||||
double GetBestPrice(ulong ticket, long posType)
|
||||
{
|
||||
for(int i=0; i<posCount; i++)
|
||||
{
|
||||
if(posTicketArray[i] == ticket)
|
||||
{
|
||||
return bestPriceArray[i];
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void RemovePosition(ulong ticket)
|
||||
{
|
||||
for(int i=0; i<posCount; i++)
|
||||
{
|
||||
if(posTicketArray[i] == ticket)
|
||||
{
|
||||
for(int j=i; j<posCount-1; j++)
|
||||
{
|
||||
posTicketArray[j] = posTicketArray[j+1];
|
||||
bestPriceArray[j] = bestPriceArray[j+1];
|
||||
}
|
||||
posCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****=====================================================================
|
||||
* Cleanup any closed positions from the best price array
|
||||
*======================================================================*****/
|
||||
void CleanUpClosedPositions()
|
||||
{
|
||||
for(int i= posCount-1; i>=0; i--)
|
||||
{
|
||||
ulong storedTicket = posTicketArray[i];
|
||||
bool found = false;
|
||||
|
||||
uint totalPos = PositionsTotal();
|
||||
for(uint p=0; p<totalPos; p++)
|
||||
{
|
||||
if(PositionGetTicket(p) == storedTicket)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
for(int j=i; j<posCount-1; j++)
|
||||
{
|
||||
posTicketArray[j] = posTicketArray[j+1];
|
||||
bestPriceArray[j] = bestPriceArray[j+1];
|
||||
}
|
||||
posCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user