//+------------------------------------------------------------------+ //| FadePivot2_v2.mq5 | //| Garfield Heron / Abbey Road Tech | //| https://abbeyroadtechnology.com | //+------------------------------------------------------------------+ #property copyright "© 2024" #property link "https://abbeyroadtechnology.com" #property version "1.75" #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 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 (only if not close-only-pending) input long MagicNumber = 98765; // Magic number for this EA /***** New Day-of-Week Inputs *****/ input bool TradeMonday = true; // If true, trade on Mondays input bool TradeTuesday = true; // If true, trade on Tuesdays input bool TradeWednesday = true; // ... input bool TradeThursday = true; input bool TradeFriday = true; input bool TradeSaturday = false; // Typically false input bool TradeSunday = false; // Typically false /***** Global Variables *****/ static datetime TradeDate = 0; // Tracks the current day static bool OrdersPlaced = false; /***** Pivots *****/ static double P, R1, R2, S1, S2; /***** Prototypes *****/ void CalculateDailyPivots(); void PlaceFadeOrders(); bool HasOpenOrPendingWithMagic(long magic); bool CloseAllByMagic(long magic); void PlaceLimitOrder(ENUM_ORDER_TYPE orderType, double entryPrice, double slPrice, double tpPrice); bool IsTradingDay(); // helper to decide if current day-of-week is allowed //+------------------------------------------------------------------+ //| Expert initialization | //+------------------------------------------------------------------+ int OnInit() { Print("FadePivot2_v2 EA initializing (with day-of-week filter)..."); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Print("FadePivot2_v2 EA deinitialized. reason=", reason); } //+------------------------------------------------------------------+ //| OnTick | //+------------------------------------------------------------------+ void OnTick() { // 0) Check if it's an allowed trading day if(!IsTradingDay()) { // If it's not a day to trade, we can skip logic or still do pivot calc // For example, we skip everything: return; } // 1) Check for a new day (the 0th D1 bar changed) datetime today = iTime(_Symbol, PERIOD_D1, 0); if(today != TradeDate) { // New day => recalc pivots, reset flags CalculateDailyPivots(); TradeDate = today; OrdersPlaced = false; Print("New daily bar. Pivots recalculated. R2=", R2, ", S2=", S2); } // 2) If we haven't placed orders for this day, do so now if(!OrdersPlaced) { if(!OnlyOneSetPerDay) { // If OnlyOneSetPerDay=false, place new orders daily PlaceFadeOrders(); OrdersPlaced = true; } else { // If OnlyOneSetPerDay=true, skip if an open/pending trade // unless AllowNewOrdersIfPreviousOpen==true if(!HasOpenOrPendingWithMagic(MagicNumber) || AllowNewOrdersIfPreviousOpen) { PlaceFadeOrders(); OrdersPlaced = true; } else { Print("Skipping new orders because OnlyOneSetPerDay=true ", "and we already have open/pending trades (Magic=", MagicNumber, ")."); } } } // 3) EoD close logic if(CloseEndOfDay) { MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); // Example: close everything at 23:59 if(dt.hour == 23 && dt.min == 59) { Print("FadePivot2_v2: End-of-Day => cleaning up trades/orders..."); CloseAllByMagic(MagicNumber); } } } //+------------------------------------------------------------------+ //| Check if current day-of-week is allowed for trading | //+------------------------------------------------------------------+ bool IsTradingDay() { MqlDateTime dt; TimeToStruct(TimeCurrent(), dt); int dayOfWeek = dt.day_of_week; // 0=Sunday, 1=Monday,...6=Saturday switch(dayOfWeek) { 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; } // fallback 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); P = (prevHigh + prevLow + prevClose)/3.0; R1 = 2.0*P - prevLow; S1 = 2.0*P - prevHigh; R2 = P + (prevHigh - prevLow); S2 = P - (prevHigh - prevLow); } //+------------------------------------------------------------------+ //| PlaceFadeOrders | //+------------------------------------------------------------------+ void PlaceFadeOrders() { int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT); // SELL LIMIT at R2 { double entryPrice = NormalizeDouble(R2, digits); double distanceR1R2 = MathAbs(R2 - R1); double slPrice = R2 + (distanceR1R2 * StopLossFactor); double tpPrice = (R1 * TakeProfitFactor) + (TP_OffsetPips * pt); 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); double slPrice = S2 - (distanceS1S2 * StopLossFactor); double tpPrice = (S1 * TakeProfitFactor) + (TP_OffsetPips * pt); Print("Placing BUY LIMIT at S2= ", entryPrice, ", SL= ", slPrice, ", TP= ", tpPrice); PlaceLimitOrder(ORDER_TYPE_BUY_LIMIT, entryPrice, slPrice, tpPrice); } } //+------------------------------------------------------------------+ //| PlaceLimitOrder - Helper for pending orders | //+------------------------------------------------------------------+ 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; request.type_filling = ORDER_FILLING_FOK; request.type_time = ORDER_TIME_GTC; request.comment = "FadePivot2_v2"; // Additional checks (optional): // e.g. check volume validity, check margin, check limit price validity if(!OrderSend(request, result)) { Print(__FUNCTION__, ": OrderSend failed. LastErr=", GetLastError()); 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 retcode=", result.retcode, ", lastErr=", GetLastError()); } } //+------------------------------------------------------------------+ //| HasOpenOrPendingWithMagic | //+------------------------------------------------------------------+ bool HasOpenOrPendingWithMagic(long magic) { // check open positions if(PositionSelect(_Symbol)) { if((long)PositionGetInteger(POSITION_MAGIC) == magic) return(true); } // check pending orders int totalOrders = (int)OrdersTotal(); for(int i=0; i skip open position logic if(!CloseOnlyPendingEndOfDay) { // 1) Possibly close open position(s) if(CloseOpenOrdersEndOfDay && PositionSelect(_Symbol)) { if((long)PositionGetInteger(POSITION_MAGIC) == magic) { ulong ticket = PositionGetInteger(POSITION_TICKET); double vol = PositionGetDouble(POSITION_VOLUME); long pType = PositionGetInteger(POSITION_TYPE); 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_v2 EoD Close"; if(!OrderSend(clReq, clRes) || clRes.retcode != TRADE_RETCODE_DONE) { Print("CloseAllByMagic: close pos retcode=", clRes.retcode, ", lastErr=", GetLastError()); success = false; } else { Print("Closed position #", ticket, " EoD."); } } } } // 2) Delete pending orders either way int totalOrders = (int)OrdersTotal(); for(int i=totalOrders-1; i>=0; i--) { ulong ticket = OrderGetTicket(i); if(OrderSelect(ticket)) { long omagic = OrderGetInteger(ORDER_MAGIC); string sym = OrderGetString(ORDER_SYMBOL); long otype = OrderGetInteger(ORDER_TYPE); if(omagic == magic && sym == _Symbol) { // If it's a pending order, remove it 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_v2 EoD Remove"; if(!OrderSend(odReq, odRes) || odRes.retcode != TRADE_RETCODE_DONE) { Print("CloseAllByMagic: remove order retcode=", odRes.retcode, ", lastErr=", GetLastError()); success = false; } else { Print("Removed pending order #", ticket, " EoD."); } } } } } return success; }