diff --git a/OrdersEA_Smart_Grid_BO.mq5 b/OrdersEA_Smart_Grid_BO.mq5 new file mode 100755 index 0000000..ebf1814 --- /dev/null +++ b/OrdersEA_Smart_Grid_BO.mq5 @@ -0,0 +1,1442 @@ +//+------------------------------------------------------------------+ +//| OrdersEA_Smart_Grid_BO.mq5 | +//| Copyright 2024, Garfield Heron | +//| https://fetcherpay.com | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2024, Garfield Heron" +#property link "https://fetcherpay.com" +#property version "5.0" + +#include +#include + +#define VERSION "Version 5.0 Smart Grid Breakout BO MT5" +#define MAX_TRADES 600 +#define MAX_LOG_TRADES 1200 + +//--- Input Parameters +input string Email= "garfield@fetcherpay.com"; +input int MagicNum= 333; + +//--- Smart Grid Settings +input string GridSettings = "=== Smart Grid Settings ==="; +input bool UseAutoPivots = true; +input double InpManualHigh= 0; // Manual HIGH level (0 = use AutoPivots) +input double InpManualLow= 0; // Manual LOW level (0 = use AutoPivots) +input double Entry= 10; // Grid spacing in points +input double TP= 15; // Take-profit in points per level +input double Lots=0.01; +input int MaxLevels = 10; // Max levels per side (cap; adaptive may place fewer) + +//--- Range Filter Settings +input string FilterSettings = "=== Range Filters ==="; +input bool UseRSIFilter = true; +input int RSIPeriod = 14; +input int RSILower = 35; +input int RSIUpper = 65; +input bool UseADXFilter = true; +input int ADXPeriod = 14; +input double ADXMax = 30; // Widened default from 25 +input bool UseATRFilter = true; +input int ATRPeriod = 14; +input double ATRMultiplier = 1.5; + +//--- Adaptive Filters (4.4) +input string AdaptiveSettings = "=== Adaptive Filters ==="; +input int InpRelaxFilterAfterDays = 3; // If no trade for N days, relax filters (0=disabled) +input double InpRelaxADXFactor = 1.3; // Multiply ADXMax by this when relaxed +input double InpRelaxLotFactor = 0.5; // Multiply lots by this when relaxed + +//--- Adaptive Entry (3.3) +input bool InpAdaptiveEntry = false; // Auto-scale grid spacing from ATR +input double InpEntryATRFactor = 0.5; // spacing = ATR * factor / MaxLevels (per level) + +//--- Spread Filter (3.4) +input string SpreadSettings = "=== Spread Filter ==="; +input int InpMaxSpreadPoints = 30; // 0 = disabled + +//--- Stop Loss (3.5) +input string SLSettings = "=== Stop Loss ==="; +input bool InpUseStopLoss = false; // false = NO stop loss (original grid behavior) +input int StopLoss = 300; // Only applied if InpUseStopLoss=true + +//--- Session Filter (4.1) +input string SessionSettings = "=== Session Filter ==="; +input bool InpUseSessionFilter = false; +input int InpSessionStartHour = 1; // Broker time, inclusive +input int InpSessionEndHour = 10; // Broker time, exclusive + +//--- Breakeven (4.2) +input string BreakevenSettings = "=== Breakeven ==="; +input bool InpUseBreakeven = false; // Move remaining SLs to breakeven after first TP hits +input int InpBreakevenBufferPoints = 5; // Spread cushion above entry + +//--- Correlation Cap (4.3) +input string CorrelationSettings = "=== Correlation Cap ==="; +input int InpMaxLongSymbols = 0; // Max distinct symbols with open longs (0 = unlimited) +input int InpMaxShortSymbols = 0; // Max distinct symbols with open shorts (0 = unlimited) + +//--- Range Drift (4.6) +input string DriftSettings = "=== Range Drift ==="; +input bool InpRangeDriftEnable = false; +input int InpMoveRangeTrigger = 200; // Re-center grid when mid drifts by N points + +//--- Risk Management +input string RiskSettings = "=== Risk Management ==="; +input int TRADE_RANGE= 50; +input double LongLimit= 0; +input double ShortLimit= 0; +input string GetOut= "N"; +input string OpenNewTrades="Y"; +input int TakeProfitLevelPercent= 0; // % equity gain to close cycle (0=disabled) +input int TakeProfitLevelDollarAmount= 0; // $ gain to close cycle (0=disabled) +input int EquityFactorPercent= 0; +input int LotsFactorPercent= 0; +input int BaseEquity= 10000; +input bool Master= false; +input bool DiagnosticModeOn= false; +input double InpMaxDailyDrawdown = 5.0; // Max daily EA drawdown % (per-EA) +input double InpMaxWeeklyDrawdown = 10.0; // Max weekly EA drawdown % (per-EA, 0=disable) + +//--- Profit Protection +input string ProfitSettings = "=== Profit Protection ==="; +input bool InpStopAfterProfit = false; // Stop new grids after profitable cycle +input bool InpCycleReport = true; // Emit notification on cycle end + +//--- Weekend Protection +input string WeekendSettings = "=== Weekend Protection ==="; +input bool InpCloseBeforeWeekend = true; +input int InpWeekendCloseHour = 17; +input bool InpCancelPendingBeforeWeekend = true; + +//--- Trade Object +CTrade trade; +CPositionInfo positionInfo; + +//--- Indicator Handles +int RSIHandle = INVALID_HANDLE; +int ADXHandle = INVALID_HANDLE; +int ATRHandle = INVALID_HANDLE; + +//--- Pivot Point Variables +double PivotP = 0; +double PivotR1 = 0; +double PivotR2 = 0; +double PivotS1 = 0; +double PivotS2 = 0; +double GridHigh = 0; +double GridLow = 0; + +//--- Stats Variables (1.3 — now actually computed) +int longs = 0; +int shorts = 0; +double longAvgPrice = 0; +double longAvgLots = 0; +double shortAvgPrice = 0; +double shortAvgLots = 0; +double longProfit = 0; +double shortProfit = 0; + +//--- State +double initEquity; +int lotDigits; +bool bEnableLongs = false; +bool bEnableShorts = false; +bool bGetOutOK = false; +bool bOpenNewTradesOK = false; +bool bGetOutHandled = false; + +//--- Daily Drawdown (per-EA, 3.1B) +double dailyStartEquity = 0; +datetime lastEquityReset = 0; +double realizedPnLToday = 0; +datetime lastRealizedScan = 0; + +//--- Weekly Drawdown (per-EA) +double weeklyStartEquity = 0; +datetime lastWeeklyReset = 0; +double realizedPnLWeek = 0; +datetime lastWeeklyScan = 0; + +//--- Weekend Protection +bool weekendCloseExecuted = false; + +//--- Master one-shot (1.4) +bool masterShutdownDone = false; + +//--- Pivot recalc (3.8) +datetime lastPivotCalcDate = 0; + +//--- Grid State (3.7 — now persisted) +bool gridPlaced = false; +double cycleStartEquity = 0; +datetime cycleStartTime = 0; +bool cycleProfitStop = false; +int cyclePartialTPCount = 0; // For breakeven logic +datetime lastTradePlacedTime = 0; // For adaptive filters + +//--- Runtime derived +double currentEntryPts = 0; // Resolved at bar open (adaptive or fixed) +bool filtersRelaxed = false; // Set by adaptive filter check + +//+------------------------------------------------------------------+ +//| Log helper with symbol prefix (3.6) | +//+------------------------------------------------------------------+ +void PrintS(string msg) + { + Print("[", _Symbol, ":", MagicNum, "] ", msg); + } + +void Log(string st) + { + if(DiagnosticModeOn) + PrintS(st); + } + +//+------------------------------------------------------------------+ +//| Determine filling mode supported by symbol (1.2) | +//+------------------------------------------------------------------+ +ENUM_ORDER_TYPE_FILLING GetMarketFilling() + { + int modes = (int)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE); + if((modes & SYMBOL_FILLING_IOC) != 0) return ORDER_FILLING_IOC; + if((modes & SYMBOL_FILLING_FOK) != 0) return ORDER_FILLING_FOK; + return ORDER_FILLING_RETURN; + } + +// ORDER_FILLING_RETURN is always valid for pending orders per MT5 docs — +// SYMBOL_FILLING_MODE bitmask only exposes FOK/IOC flags (RETURN is implicit). +ENUM_ORDER_TYPE_FILLING GetPendingFilling() + { + return ORDER_FILLING_RETURN; + } + +//+------------------------------------------------------------------+ +//| State persistence (3.7) | +//+------------------------------------------------------------------+ +string GvKey(string suffix) + { + return "OrdersEA_BO_" + IntegerToString(MagicNum) + "_" + _Symbol + "_" + suffix; + } + +void SaveGridState() + { + GlobalVariableSet(GvKey("gridPlaced"), gridPlaced ? 1.0 : 0.0); + GlobalVariableSet(GvKey("cycleStartEquity"), cycleStartEquity); + GlobalVariableSet(GvKey("cycleStartTime"), (double)cycleStartTime); + GlobalVariableSet(GvKey("cycleProfitStop"), cycleProfitStop ? 1.0 : 0.0); + GlobalVariableSet(GvKey("lastPivotCalcDate"), (double)lastPivotCalcDate); + GlobalVariableSet(GvKey("lastTradeTime"), (double)lastTradePlacedTime); + GlobalVariableSet(GvKey("dailyStartEquity"), dailyStartEquity); + GlobalVariableSet(GvKey("lastEquityReset"), (double)lastEquityReset); + GlobalVariableSet(GvKey("weeklyStartEquity"), weeklyStartEquity); + GlobalVariableSet(GvKey("lastWeeklyReset"), (double)lastWeeklyReset); + } + +void LoadGridState() + { + if(GlobalVariableCheck(GvKey("gridPlaced"))) gridPlaced = GlobalVariableGet(GvKey("gridPlaced")) > 0.5; + if(GlobalVariableCheck(GvKey("cycleStartEquity"))) cycleStartEquity = GlobalVariableGet(GvKey("cycleStartEquity")); + if(GlobalVariableCheck(GvKey("cycleStartTime"))) cycleStartTime = (datetime)GlobalVariableGet(GvKey("cycleStartTime")); + if(GlobalVariableCheck(GvKey("cycleProfitStop"))) cycleProfitStop = GlobalVariableGet(GvKey("cycleProfitStop")) > 0.5; + if(GlobalVariableCheck(GvKey("lastPivotCalcDate"))) lastPivotCalcDate = (datetime)GlobalVariableGet(GvKey("lastPivotCalcDate")); + if(GlobalVariableCheck(GvKey("lastTradeTime"))) lastTradePlacedTime = (datetime)GlobalVariableGet(GvKey("lastTradeTime")); + if(GlobalVariableCheck(GvKey("dailyStartEquity"))) dailyStartEquity = GlobalVariableGet(GvKey("dailyStartEquity")); + if(GlobalVariableCheck(GvKey("lastEquityReset"))) lastEquityReset = (datetime)GlobalVariableGet(GvKey("lastEquityReset")); + if(GlobalVariableCheck(GvKey("weeklyStartEquity"))) weeklyStartEquity = GlobalVariableGet(GvKey("weeklyStartEquity")); + if(GlobalVariableCheck(GvKey("lastWeeklyReset"))) lastWeeklyReset = (datetime)GlobalVariableGet(GvKey("lastWeeklyReset")); + } + +//+------------------------------------------------------------------+ +//| Today-at-broker / Monday-at-broker | +//+------------------------------------------------------------------+ +datetime TodayStartBroker() + { + datetime now = TimeCurrent(); + return (datetime)((now / 86400) * 86400); + } + +datetime WeekStartBroker() + { + datetime today = TodayStartBroker(); + MqlDateTime dt; + TimeToStruct(today, dt); + int offset = (dt.day_of_week == 0) ? 6 : (dt.day_of_week - 1); + return (datetime)(today - offset * 86400); + } + +//+------------------------------------------------------------------+ +//| Sum deals for this EA in a window (3.1B) | +//+------------------------------------------------------------------+ +double SumRealizedPnLSince(datetime from) + { + datetime to = TimeCurrent() + 60; + if(!HistorySelect(from, to)) return 0; + double total = 0; + int deals = HistoryDealsTotal(); + for(int i = 0; i < deals; i++) + { + ulong dealTicket = HistoryDealGetTicket(i); + if(dealTicket == 0) continue; + if(HistoryDealGetInteger(dealTicket, DEAL_MAGIC) != MagicNum) continue; + if(HistoryDealGetString(dealTicket, DEAL_SYMBOL) != _Symbol) continue; + long entry = HistoryDealGetInteger(dealTicket, DEAL_ENTRY); + if(entry != DEAL_ENTRY_OUT && entry != DEAL_ENTRY_INOUT) continue; + total += HistoryDealGetDouble(dealTicket, DEAL_PROFIT); + total += HistoryDealGetDouble(dealTicket, DEAL_SWAP); + total += HistoryDealGetDouble(dealTicket, DEAL_COMMISSION); + } + return total; + } + +double GetRealizedPnLToday() + { + if(TimeCurrent() - lastRealizedScan < 60 && realizedPnLToday != 0) + return realizedPnLToday; + realizedPnLToday = SumRealizedPnLSince(TodayStartBroker()); + lastRealizedScan = TimeCurrent(); + return realizedPnLToday; + } + +double GetRealizedPnLWeek() + { + if(TimeCurrent() - lastWeeklyScan < 120 && realizedPnLWeek != 0) + return realizedPnLWeek; + realizedPnLWeek = SumRealizedPnLSince(WeekStartBroker()); + lastWeeklyScan = TimeCurrent(); + return realizedPnLWeek; + } + +//+------------------------------------------------------------------+ +//| Compute floating P&L for this EA | +//+------------------------------------------------------------------+ +double GetFloatingPnL() + { + double total = 0; + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + total += PositionGetDouble(POSITION_PROFIT); + total += PositionGetDouble(POSITION_SWAP); + } + return total; + } + +//+------------------------------------------------------------------+ +//| Check Drawdown — daily + weekly, per-EA (3.1B) | +//+------------------------------------------------------------------+ +bool CheckDailyDrawdown() + { + static bool dailyWarned = false; + static bool weeklyWarned = false; + + // --- Daily rollover --- + datetime today = TodayStartBroker(); + if(today != lastEquityReset) + { + dailyStartEquity = AccountInfoDouble(ACCOUNT_EQUITY); + lastEquityReset = today; + realizedPnLToday = 0; + lastRealizedScan = 0; + dailyWarned = false; + PrintS("Daily equity reset: $" + DoubleToString(dailyStartEquity, 2)); + SaveGridState(); + } + + // --- Weekly rollover (Monday 00:00 broker) --- + datetime weekStart = WeekStartBroker(); + if(weekStart != lastWeeklyReset) + { + weeklyStartEquity = AccountInfoDouble(ACCOUNT_EQUITY); + lastWeeklyReset = weekStart; + realizedPnLWeek = 0; + lastWeeklyScan = 0; + weeklyWarned = false; + PrintS("Weekly equity reset: $" + DoubleToString(weeklyStartEquity, 2)); + SaveGridState(); + } + + double floating = GetFloatingPnL(); + + // --- Daily check --- + if(InpMaxDailyDrawdown > 0 && dailyStartEquity > 0) + { + double dailyPnL = GetRealizedPnLToday() + floating; + double dailyLimit = dailyStartEquity * InpMaxDailyDrawdown / 100.0; + if(dailyPnL <= -dailyLimit) + { + if(!dailyWarned) + { + PrintS("⚠️ DAILY DD HIT: $" + DoubleToString(dailyPnL, 2) + + " (limit: -$" + DoubleToString(dailyLimit, 2) + ")"); + SendNotification("Grid EA " + _Symbol + ": DAILY DD reached ($" + + DoubleToString(dailyPnL, 2) + ")"); + dailyWarned = true; + } + return false; + } + dailyWarned = false; + } + + // --- Weekly check --- + if(InpMaxWeeklyDrawdown > 0 && weeklyStartEquity > 0) + { + double weeklyPnL = GetRealizedPnLWeek() + floating; + double weeklyLimit = weeklyStartEquity * InpMaxWeeklyDrawdown / 100.0; + if(weeklyPnL <= -weeklyLimit) + { + if(!weeklyWarned) + { + PrintS("⚠️ WEEKLY DD HIT: $" + DoubleToString(weeklyPnL, 2) + + " (limit: -$" + DoubleToString(weeklyLimit, 2) + ")"); + SendNotification("Grid EA " + _Symbol + ": WEEKLY DD reached ($" + + DoubleToString(weeklyPnL, 2) + ")"); + weeklyWarned = true; + } + return false; + } + weeklyWarned = false; + } + + return true; + } + +//+------------------------------------------------------------------+ +//| Check Weekend Protection | +//+------------------------------------------------------------------+ +bool CheckWeekendProtection() + { + if(!InpCloseBeforeWeekend) return true; + MqlDateTime dt; + TimeToStruct(TimeCurrent(), dt); + + if(dt.day_of_week != FRIDAY) + { + if(weekendCloseExecuted) weekendCloseExecuted = false; + return true; + } + if(weekendCloseExecuted) return true; + + if(dt.hour >= InpWeekendCloseHour) + { + PrintS("⚠️ WEEKEND CLOSE — closing all positions"); + SendNotificationEx("WEEKEND CLOSE", "Closing positions before weekend"); + CloseAllPositions("Weekend protection"); + if(InpCancelPendingBeforeWeekend) + CancelAllOrders("Weekend protection"); + weekendCloseExecuted = true; + gridPlaced = false; + SaveGridState(); + return false; + } + return true; + } + +//+------------------------------------------------------------------+ +//| Session Filter (4.1) | +//+------------------------------------------------------------------+ +bool CheckSessionFilter() + { + if(!InpUseSessionFilter) return true; + MqlDateTime dt; + TimeToStruct(TimeCurrent(), dt); + int h = dt.hour; + bool inSession; + if(InpSessionStartHour <= InpSessionEndHour) + inSession = (h >= InpSessionStartHour && h < InpSessionEndHour); + else + inSession = (h >= InpSessionStartHour || h < InpSessionEndHour); + if(!inSession && DiagnosticModeOn) + PrintS("Session filter: outside window (" + IntegerToString(h) + "h)"); + return inSession; + } + +//+------------------------------------------------------------------+ +//| Spread Filter (3.4) | +//+------------------------------------------------------------------+ +bool CheckSpreadFilter() + { + if(InpMaxSpreadPoints <= 0) return true; + int spread = (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); + if(spread > InpMaxSpreadPoints) + { + if(DiagnosticModeOn) + PrintS("Spread filter: " + IntegerToString(spread) + "pts > " + IntegerToString(InpMaxSpreadPoints)); + return false; + } + return true; + } + +//+------------------------------------------------------------------+ +//| Correlation Cap (4.3) — count distinct symbols w/ open positions | +//+------------------------------------------------------------------+ +int CountSymbolsWithOpenPositions(int posType) + { + string symbols[]; + int count = 0; + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + if(PositionGetInteger(POSITION_TYPE) != posType) continue; + string sym = PositionGetString(POSITION_SYMBOL); + bool found = false; + for(int j = 0; j < count; j++) + if(symbols[j] == sym) { found = true; break; } + if(!found) + { + ArrayResize(symbols, count + 1); + symbols[count] = sym; + count++; + } + } + return count; + } + +bool CheckCorrelationCap(bool forLong) + { + int cap = forLong ? InpMaxLongSymbols : InpMaxShortSymbols; + if(cap <= 0) return true; + int type = forLong ? POSITION_TYPE_BUY : POSITION_TYPE_SELL; + int current = CountSymbolsWithOpenPositions(type); + // If this symbol already has positions, we're not adding a new distinct symbol + bool thisSymbolHasPos = false; + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; + if(PositionGetInteger(POSITION_TYPE) == type) { thisSymbolHasPos = true; break; } + } + if(thisSymbolHasPos) return true; + if(current >= cap) + { + if(DiagnosticModeOn) + PrintS("Correlation cap: " + IntegerToString(current) + "/" + IntegerToString(cap) + + (forLong ? " long" : " short") + " symbols open"); + return false; + } + return true; + } + +//+------------------------------------------------------------------+ +//| Adaptive Filters (4.4) | +//+------------------------------------------------------------------+ +void UpdateFilterRelaxation() + { + filtersRelaxed = false; + if(InpRelaxFilterAfterDays <= 0) return; + if(lastTradePlacedTime == 0) return; // never traded — don't relax yet + long secsIdle = (long)TimeCurrent() - (long)lastTradePlacedTime; + if(secsIdle > (long)InpRelaxFilterAfterDays * 86400) + filtersRelaxed = true; + } + +double EffectiveADXMax() + { + return filtersRelaxed ? (ADXMax * InpRelaxADXFactor) : ADXMax; + } + +//+------------------------------------------------------------------+ +//| Calculate Pivot Points | +//+------------------------------------------------------------------+ +void CalculatePivotPoints() + { + MqlRates rates[1]; + int copied = CopyRates(_Symbol, PERIOD_D1, 1, 1, rates); + if(copied < 1) + { + PrintS("Failed to get daily rates for pivot calculation"); + return; + } + double prevHigh = rates[0].high; + double prevLow = rates[0].low; + double prevClose = rates[0].close; + + PivotP = (prevHigh + prevLow + prevClose) / 3.0; + PivotR1 = (2.0 * PivotP) - prevLow; + PivotS1 = (2.0 * PivotP) - prevHigh; + PivotR2 = PivotP + (prevHigh - prevLow); + PivotS2 = PivotP - (prevHigh - prevLow); + + if(UseAutoPivots) + { + double atr = 0; + if(ATRHandle != INVALID_HANDLE) + { + double atrBuf[1]; + if(CopyBuffer(ATRHandle, 0, 0, 1, atrBuf) > 0) + atr = atrBuf[0]; + } + if(UseATRFilter && atr > 0) + { + GridHigh = NormalizeDouble(PivotP + (atr * ATRMultiplier), _Digits); + GridLow = NormalizeDouble(PivotP - (atr * ATRMultiplier), _Digits); + } + else + { + GridHigh = PivotR1; + GridLow = PivotS1; + } + PrintS("AutoPivots: HIGH=" + DoubleToString(GridHigh, _Digits) + + " LOW=" + DoubleToString(GridLow, _Digits) + + " ATR=" + DoubleToString(atr, _Digits)); + } + PrintS("Pivot: P=" + DoubleToString(PivotP, _Digits) + + " R1=" + DoubleToString(PivotR1, _Digits) + + " S1=" + DoubleToString(PivotS1, _Digits)); + + lastPivotCalcDate = TodayStartBroker(); + SaveGridState(); + } + +//+------------------------------------------------------------------+ +//| Resolve Entry spacing at runtime (3.3 adaptive) | +//+------------------------------------------------------------------+ +double ResolveEntryPoints() + { + if(!InpAdaptiveEntry) return Entry; + if(ATRHandle == INVALID_HANDLE) return Entry; + double buf[1]; + if(CopyBuffer(ATRHandle, 0, 0, 1, buf) <= 0) return Entry; + double atr = buf[0]; + double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); + if(point <= 0 || atr <= 0) return Entry; + double maxLvl = MathMax(1, MaxLevels); + double pts = (atr * InpEntryATRFactor) / maxLvl / point; + return MathMax(5.0, pts); // floor at 5pts + } + +//+------------------------------------------------------------------+ +//| Check if Market is Ranging | +//+------------------------------------------------------------------+ +bool IsRangingMarket() + { + bool isRanging = true; + double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); + + if(UseRSIFilter && RSIHandle != INVALID_HANDLE) + { + double rsiBuf[1]; + if(CopyBuffer(RSIHandle, 0, 0, 1, rsiBuf) > 0) + { + double rsi = rsiBuf[0]; + if(rsi < RSILower || rsi > RSIUpper) + { + if(DiagnosticModeOn) PrintS("RSI extreme: " + DoubleToString(rsi, 1)); + isRanging = false; + } + } + } + + if(UseADXFilter && ADXHandle != INVALID_HANDLE && isRanging) + { + double adxBuf[1]; + if(CopyBuffer(ADXHandle, 0, 0, 1, adxBuf) > 0) + { + double adx = adxBuf[0]; + double adxCap = EffectiveADXMax(); + if(adx > adxCap) + { + if(DiagnosticModeOn) + PrintS("ADX trending: " + DoubleToString(adx, 1) + " > " + DoubleToString(adxCap, 1) + + (filtersRelaxed ? " (relaxed)" : "")); + isRanging = false; + } + } + } + + double actualHigh = (InpManualHigh > 0) ? InpManualHigh : GridHigh; + double actualLow = (InpManualLow > 0) ? InpManualLow : GridLow; + if(currentPrice > actualHigh || currentPrice < actualLow) + { + if(DiagnosticModeOn) PrintS("Price outside grid range"); + isRanging = false; + } + return isRanging; + } + +//+------------------------------------------------------------------+ +//| Hard breakout confirmation — price beyond R2/S2 | +//| Reserved for optional entry guard: only place BO orders if price | +//| has already cleared the outer pivot (stronger breakout signal). | +//+------------------------------------------------------------------+ +bool IsHardBreakout() + { + double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); + if(currentPrice < PivotS2 || currentPrice > PivotR2) + { + PrintS("Hard breakout: price=" + DoubleToString(currentPrice, _Digits) + + " S2=" + DoubleToString(PivotS2, _Digits) + + " R2=" + DoubleToString(PivotR2, _Digits)); + return true; + } + return false; + } + +//+------------------------------------------------------------------+ +//| Calculate Lot Size | +//+------------------------------------------------------------------+ +double CalcLots() + { + double tmp = (AccountInfoDouble(ACCOUNT_EQUITY) - initEquity); + double a = EquityFactorPercent; + double b = LotsFactorPercent; + double lots; + + if(0 == EquityFactorPercent || 0 == LotsFactorPercent) + lots = Lots; + else + { + a = initEquity * a / 100.0; + b = b / 100.0; + if(tmp > 0) tmp = MathPow(1 + b, (tmp / a)); + else if(tmp < 0) tmp = MathPow(1 - b, MathAbs(tmp / a)); + else tmp = 1; + lots = NormalizeDouble(Lots * tmp, lotDigits); + } + + if(filtersRelaxed) lots *= InpRelaxLotFactor; + + double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); + double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); + double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); + if(lots < minLot) lots = minLot; + if(lots > maxLot) lots = maxLot; + if(step > 0) lots = MathFloor(lots / step + 1e-9) * step; + return NormalizeDouble(lots, lotDigits); + } + +//+------------------------------------------------------------------+ +//| Send Notification | +//+------------------------------------------------------------------+ +void SendNotificationEx(string title, string subject) + { + if(MQLInfoInteger(MQL_OPTIMIZATION)) return; + double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); + string msg = "[" + _Symbol + "] " + title + ": " + subject; + msg += " | Px: " + DoubleToString(bid, _Digits); + msg += " | L: " + IntegerToString(longs) + " @ " + DoubleToString(longAvgPrice, _Digits); + msg += " | S: " + IntegerToString(shorts) + " @ " + DoubleToString(shortAvgPrice, _Digits); + msg += " | Eq: " + DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2); + SendNotification(msg); + PrintS(title + ": " + subject); + } + +//+------------------------------------------------------------------+ +//| Compute averages for this EA (1.3) | +//+------------------------------------------------------------------+ +void CalcAvgPrice() + { + longs = 0; shorts = 0; + longAvgPrice = 0; longAvgLots = 0; + shortAvgPrice = 0; shortAvgLots = 0; + longProfit = 0; shortProfit = 0; + + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + + long ptype = PositionGetInteger(POSITION_TYPE); + double vol = PositionGetDouble(POSITION_VOLUME); + double open = PositionGetDouble(POSITION_PRICE_OPEN); + double profit = PositionGetDouble(POSITION_PROFIT) + PositionGetDouble(POSITION_SWAP); + + if(ptype == POSITION_TYPE_BUY) + { + longAvgPrice += open * vol; + longAvgLots += vol; + longProfit += profit; + longs++; + } + else if(ptype == POSITION_TYPE_SELL) + { + shortAvgPrice += open * vol; + shortAvgLots += vol; + shortProfit += profit; + shorts++; + } + } + if(longAvgLots > 0) longAvgPrice /= longAvgLots; + if(shortAvgLots > 0) shortAvgPrice /= shortAvgLots; + } + +//+------------------------------------------------------------------+ +//| Breakeven move (4.2) | +//+------------------------------------------------------------------+ +void ApplyBreakeven() + { + if(!InpUseBreakeven) return; + if(cyclePartialTPCount <= 0) return; // Nothing filled profitably yet + double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); + double buffer = InpBreakevenBufferPoints * point; + + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + + long ptype = PositionGetInteger(POSITION_TYPE); + double open = PositionGetDouble(POSITION_PRICE_OPEN); + double curSL = PositionGetDouble(POSITION_SL); + double tp = PositionGetDouble(POSITION_TP); + double newSL = (ptype == POSITION_TYPE_BUY) ? (open - buffer) : (open + buffer); + newSL = NormalizeDouble(newSL, _Digits); + + bool alreadyAtBE = (ptype == POSITION_TYPE_BUY) + ? (curSL >= open - point) + : (curSL <= open + point && curSL > 0); + if(alreadyAtBE) continue; + + if(!trade.PositionModify(ticket, newSL, tp)) + PrintS("Breakeven modify failed #" + IntegerToString((int)ticket) + + " err=" + IntegerToString((int)trade.ResultRetcode())); + else + PrintS("Breakeven set #" + IntegerToString((int)ticket) + " SL=" + DoubleToString(newSL, _Digits)); + } + } + +//+------------------------------------------------------------------+ +//| Count partial TP fills since cycle start | +//+------------------------------------------------------------------+ +int CountPartialTPsSinceCycle() + { + if(cycleStartTime == 0) return 0; + if(!HistorySelect(cycleStartTime, TimeCurrent() + 60)) return 0; + int n = 0; + int total = HistoryDealsTotal(); + for(int i = 0; i < total; i++) + { + ulong ticket = HistoryDealGetTicket(i); + if(ticket == 0) continue; + if(HistoryDealGetInteger(ticket, DEAL_MAGIC) != MagicNum) continue; + if(HistoryDealGetString(ticket, DEAL_SYMBOL) != _Symbol) continue; + long entry = HistoryDealGetInteger(ticket, DEAL_ENTRY); + if(entry != DEAL_ENTRY_OUT && entry != DEAL_ENTRY_INOUT) continue; + if(HistoryDealGetDouble(ticket, DEAL_PROFIT) > 0) n++; + } + return n; + } + +//+------------------------------------------------------------------+ +//| Profit target check (2.1 + 2.2) | +//+------------------------------------------------------------------+ +bool CheckProfitTarget() + { + if(cycleStartEquity <= 0) return false; + double gain = AccountInfoDouble(ACCOUNT_EQUITY) - cycleStartEquity; + double pctGain = (gain / cycleStartEquity) * 100.0; + + bool pctHit = (TakeProfitLevelPercent > 0 && pctGain >= TakeProfitLevelPercent); + bool dollHit = (TakeProfitLevelDollarAmount > 0 && gain >= TakeProfitLevelDollarAmount); + + if(pctHit || dollHit) + { + PrintS("PROFIT TARGET — gain=$" + DoubleToString(gain, 2) + + " (" + DoubleToString(pctGain, 2) + "%) closing cycle"); + SendNotificationEx("PROFIT TARGET HIT", + "Gain $" + DoubleToString(gain, 2) + + " (" + DoubleToString(pctGain, 2) + "%)"); + CloseAllPositions("Profit target"); + CancelAllOrders("Profit target"); + EmitCycleReport(true); + gridPlaced = false; + cycleStartEquity = 0; + cycleStartTime = 0; + cyclePartialTPCount = 0; + SaveGridState(); + return true; + } + return false; + } + +//+------------------------------------------------------------------+ +//| Cycle Report (4.5) | +//+------------------------------------------------------------------+ +void EmitCycleReport(bool targetHit) + { + if(!InpCycleReport) return; + if(cycleStartEquity <= 0) return; + double gain = AccountInfoDouble(ACCOUNT_EQUITY) - cycleStartEquity; + long dur = (long)TimeCurrent() - (long)cycleStartTime; + int hrs = (int)(dur / 3600); + int mins = (int)((dur % 3600) / 60); + string tag = targetHit ? "TARGET" : "END"; + SendNotificationEx("CYCLE " + tag, + "P&L: $" + DoubleToString(gain, 2) + + " | Dur: " + IntegerToString(hrs) + "h" + IntegerToString(mins) + "m" + + " | Partials: " + IntegerToString(cyclePartialTPCount)); + } + +//+------------------------------------------------------------------+ +//| Order helpers | +//+------------------------------------------------------------------+ +int CountPendingOrders(int type) + { + int count = 0; + for(int i = OrdersTotal() - 1; i >= 0; i--) + { + ulong ticket = OrderGetTicket(i); + if(ticket == 0) continue; + if(OrderGetString(ORDER_SYMBOL) != _Symbol) continue; + if(OrderGetInteger(ORDER_MAGIC) != MagicNum) continue; + if(OrderGetInteger(ORDER_TYPE) == type) count++; + } + return count; + } + +void CancelAllOrders(string reason) + { + int cancelled = 0; + for(int i = OrdersTotal() - 1; i >= 0; i--) + { + ulong ticket = OrderGetTicket(i); + if(ticket == 0) continue; + if(OrderGetString(ORDER_SYMBOL) != _Symbol) continue; + if(OrderGetInteger(ORDER_MAGIC) != MagicNum) continue; + if(trade.OrderDelete(ticket)) cancelled++; + } + if(cancelled > 0) PrintS("Cancelled " + IntegerToString(cancelled) + " orders: " + reason); + } + +void CloseAllPositions(string reason) + { + int closed = 0; + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + if(trade.PositionClose(ticket)) closed++; + } + if(closed > 0) PrintS("Closed " + IntegerToString(closed) + " positions: " + reason); + } + +//+------------------------------------------------------------------+ +//| Close positions by direction (edge cleanup 5.0) | +//+------------------------------------------------------------------+ +void ClosePositionsBySide(long posType, string reason) + { + int closed = 0; + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + if(PositionGetInteger(POSITION_TYPE) != posType) continue; + if(trade.PositionClose(ticket)) closed++; + } + if(closed > 0) PrintS("Closed " + IntegerToString(closed) + " " + + (posType == POSITION_TYPE_BUY ? "BUY" : "SELL") + + " positions: " + reason); + } + +//+------------------------------------------------------------------+ +//| Place Buy/Sell Stop Orders (Breakout BO) | +//+------------------------------------------------------------------+ +bool PlaceBuyStop(double priceLevel, int level) + { + if(level >= MaxLevels) return false; + + double lots = CalcLots(); + double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); + double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK); + double stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * point; + + // BUY STOP must be above current ask by at least stopLevel + double minPrice = currentAsk + stopLevel; + if(priceLevel < minPrice) + { + if(DiagnosticModeOn) PrintS("BuyStop " + IntegerToString(level) + " too close — skip"); + return false; + } + + double sl = 0; + if(InpUseStopLoss && StopLoss > 0) + sl = NormalizeDouble(priceLevel - (StopLoss * point), _Digits); + double tp = NormalizeDouble(priceLevel + (TP * point), _Digits); + if(tp <= priceLevel + stopLevel) + tp = NormalizeDouble(priceLevel + stopLevel + (TP * point), _Digits); + + MqlTradeRequest req = {}; + MqlTradeResult res = {}; + req.action = TRADE_ACTION_PENDING; + req.symbol = _Symbol; + req.volume = lots; + req.price = NormalizeDouble(priceLevel, _Digits); + req.sl = sl; + req.tp = tp; + req.deviation = 10; + req.magic = MagicNum; + req.comment = "BO Buy " + IntegerToString(level); + req.type = ORDER_TYPE_BUY_STOP; + req.type_filling = GetPendingFilling(); + + if(!trade.OrderSend(req, res)) + { + PrintS("BuyStop error: " + trade.ResultRetcodeDescription() + " (" + IntegerToString((int)res.retcode) + ")"); + return false; + } + if(res.retcode == TRADE_RETCODE_DONE || res.retcode == TRADE_RETCODE_PLACED) + { + PrintS("BuyStop L" + IntegerToString(level) + " @ " + DoubleToString(req.price, _Digits) + + " TP=" + DoubleToString(tp, _Digits) + " #" + IntegerToString((int)res.order)); + lastTradePlacedTime = TimeCurrent(); + return true; + } + return false; + } + +bool PlaceSellStop(double priceLevel, int level) + { + if(level >= MaxLevels) return false; + + double lots = CalcLots(); + double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); + double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID); + double stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * point; + + // SELL STOP must be below current bid by at least stopLevel + double maxPrice = currentBid - stopLevel; + if(priceLevel > maxPrice) + { + if(DiagnosticModeOn) PrintS("SellStop " + IntegerToString(level) + " too close — skip"); + return false; + } + + double sl = 0; + if(InpUseStopLoss && StopLoss > 0) + sl = NormalizeDouble(priceLevel + (StopLoss * point), _Digits); + double tp = NormalizeDouble(priceLevel - (TP * point), _Digits); + if(tp >= priceLevel - stopLevel) + tp = NormalizeDouble(priceLevel - stopLevel - (TP * point), _Digits); + + MqlTradeRequest req = {}; + MqlTradeResult res = {}; + req.action = TRADE_ACTION_PENDING; + req.symbol = _Symbol; + req.volume = lots; + req.price = NormalizeDouble(priceLevel, _Digits); + req.sl = sl; + req.tp = tp; + req.deviation = 10; + req.magic = MagicNum; + req.comment = "BO Sell " + IntegerToString(level); + req.type = ORDER_TYPE_SELL_STOP; + req.type_filling = GetPendingFilling(); + + if(!trade.OrderSend(req, res)) + { + PrintS("SellStop error: " + trade.ResultRetcodeDescription() + " (" + IntegerToString((int)res.retcode) + ")"); + return false; + } + if(res.retcode == TRADE_RETCODE_DONE || res.retcode == TRADE_RETCODE_PLACED) + { + PrintS("SellStop L" + IntegerToString(level) + " @ " + DoubleToString(req.price, _Digits) + + " TP=" + DoubleToString(tp, _Digits) + " #" + IntegerToString((int)res.order)); + lastTradePlacedTime = TimeCurrent(); + return true; + } + return false; + } + +//+------------------------------------------------------------------+ +//| Handle GetOut mode | +//+------------------------------------------------------------------+ +void HandleGetOut() + { + bool getOutLongs = (GetOut=="L"||GetOut=="l"||GetOut=="A"||GetOut=="a"||GetOut=="X"||GetOut=="x"); + bool getOutShorts = (GetOut=="S"||GetOut=="s"||GetOut=="A"||GetOut=="a"||GetOut=="X"||GetOut=="x"); + + if(!bGetOutHandled) + { + PrintS("GET OUT (" + GetOut + ") — closing " + + (getOutLongs ? "L " : "") + (getOutShorts ? "S" : "")); + bGetOutHandled = true; + } + + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + long ptype = PositionGetInteger(POSITION_TYPE); + if(ptype == POSITION_TYPE_BUY && getOutLongs) trade.PositionClose(ticket); + if(ptype == POSITION_TYPE_SELL && getOutShorts) trade.PositionClose(ticket); + } + for(int i = OrdersTotal() - 1; i >= 0; i--) + { + ulong ticket = OrderGetTicket(i); + if(ticket == 0) continue; + if(OrderGetString(ORDER_SYMBOL) != _Symbol) continue; + if(OrderGetInteger(ORDER_MAGIC) != MagicNum) continue; + trade.OrderDelete(ticket); + } + gridPlaced = false; + SaveGridState(); + } + +//+------------------------------------------------------------------+ +//| Master global shutdown — one-shot (1.4) | +//+------------------------------------------------------------------+ +void GlobalShutdown() + { + int posCount = 0, ordCount = 0; + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + posCount++; + trade.PositionClose(ticket); + } + for(int i = OrdersTotal() - 1; i >= 0; i--) + { + ulong ticket = OrderGetTicket(i); + if(ticket == 0) continue; + if(OrderGetInteger(ORDER_MAGIC) != MagicNum) continue; + ordCount++; + trade.OrderDelete(ticket); + } + PrintS("MASTER SHUTDOWN — closed " + IntegerToString(posCount) + " pos, " + + IntegerToString(ordCount) + " orders"); + gridPlaced = false; + SaveGridState(); + } + +//+------------------------------------------------------------------+ +//| Range Drift (4.6) | +//+------------------------------------------------------------------+ +bool CheckRangeDrift() + { + if(!InpRangeDriftEnable) return false; + if(InpManualHigh > 0 || InpManualLow > 0) return false; // manual mode — don't drift + if(GridHigh <= 0 || GridLow <= 0) return false; + + double price = SymbolInfoDouble(_Symbol, SYMBOL_BID); + double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); + double mid = (GridHigh + GridLow) / 2.0; + double drift = MathAbs(price - mid) / point; + + if(drift > InpMoveRangeTrigger) + { + double shift = price - mid; + GridHigh = NormalizeDouble(GridHigh + shift, _Digits); + GridLow = NormalizeDouble(GridLow + shift, _Digits); + PrintS("Range drift — shifted by " + DoubleToString(shift, _Digits) + + " new HIGH=" + DoubleToString(GridHigh, _Digits) + + " LOW=" + DoubleToString(GridLow, _Digits)); + return true; + } + return false; + } + +//+------------------------------------------------------------------+ +//| Expert initialization | +//+------------------------------------------------------------------+ +int OnInit() + { + trade.SetExpertMagicNumber(MagicNum); + trade.SetDeviationInPoints(10); + trade.SetTypeFilling(GetMarketFilling()); + + initEquity = (double)BaseEquity; + lotDigits = 2; + double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); + if(minLot > 0) + { + double l = minLot; lotDigits = 0; + while(l < 1 && lotDigits < 6) { l *= 10; lotDigits++; } + } + + if(UseRSIFilter) RSIHandle = iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE); + if(UseADXFilter) ADXHandle = iADX(_Symbol, PERIOD_CURRENT, ADXPeriod); + if(UseATRFilter || InpAdaptiveEntry || UseAutoPivots) ATRHandle = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod); + + LoadGridState(); + + if(lastPivotCalcDate != TodayStartBroker()) + CalculatePivotPoints(); + + bGetOutOK = (GetOut=="L"||GetOut=="l"||GetOut=="A"||GetOut=="a"|| + GetOut=="S"||GetOut=="s"||GetOut=="N"||GetOut=="n"|| + GetOut=="X"||GetOut=="x"); + bOpenNewTradesOK = (OpenNewTrades=="Y"||OpenNewTrades=="y"||OpenNewTrades=="L"||OpenNewTrades=="l"|| + OpenNewTrades=="N"||OpenNewTrades=="n"||OpenNewTrades=="S"||OpenNewTrades=="s"); + bEnableLongs = (OpenNewTrades=="Y"||OpenNewTrades=="y"||OpenNewTrades=="L"||OpenNewTrades=="l"); + bEnableShorts = (OpenNewTrades=="Y"||OpenNewTrades=="y"||OpenNewTrades=="S"||OpenNewTrades=="s"); + + if(!bGetOutOK) PrintS("WARNING: invalid GetOut value: " + GetOut); + if(!bOpenNewTradesOK) PrintS("WARNING: invalid OpenNewTrades value: " + OpenNewTrades); + + PrintS(VERSION + " initialized — Magic=" + IntegerToString(MagicNum) + + " SL=" + (InpUseStopLoss ? "ON(" + IntegerToString(StopLoss) + ")" : "OFF") + + " AdaptiveEntry=" + (InpAdaptiveEntry ? "ON" : "OFF")); + return INIT_SUCCEEDED; + } + +//+------------------------------------------------------------------+ +//| Expert deinitialization | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { + SaveGridState(); + if(RSIHandle != INVALID_HANDLE) IndicatorRelease(RSIHandle); + if(ADXHandle != INVALID_HANDLE) IndicatorRelease(ADXHandle); + if(ATRHandle != INVALID_HANDLE) IndicatorRelease(ATRHandle); + PrintS("Deinitialized (reason=" + IntegerToString(reason) + ")"); + } + +//+------------------------------------------------------------------+ +//| Expert tick function | +//+------------------------------------------------------------------+ +void OnTick() + { + // --- Master one-shot (1.4) --- + if(Master) + { + if(!masterShutdownDone) + { + GlobalShutdown(); + masterShutdownDone = true; + } + return; + } + + // --- Per-EA daily drawdown (3.1B) --- + if(!CheckDailyDrawdown()) + { + if(gridPlaced) + { + CancelAllOrders("Daily drawdown"); + gridPlaced = false; + SaveGridState(); + } + return; + } + + // --- Weekend protection --- + if(!CheckWeekendProtection()) + return; + + // --- GetOut mode --- + if(GetOut != "N" && GetOut != "n") + { + HandleGetOut(); + return; + } + else if(bGetOutHandled) + bGetOutHandled = false; + + // --- Pivot recalc (3.8 — reliable, runs once per broker day whenever tick arrives) --- + datetime today = TodayStartBroker(); + if(today != lastPivotCalcDate) + { + PrintS("New day — recalc pivots, reset cycle flags"); + if(gridPlaced) + { + CancelAllOrders("End of day"); + gridPlaced = false; + } + if(cycleProfitStop) + cycleProfitStop = false; + CalculatePivotPoints(); // also updates lastPivotCalcDate & saves + } + + // --- Profit target check runs every tick (cheap) --- + if(CheckProfitTarget()) + return; + + // --- Stats + breakeven throttled to every 5s (history scans are expensive) --- + static datetime lastStatsTime = 0; + if(TimeCurrent() - lastStatsTime >= 5) + { + CalcAvgPrice(); + cyclePartialTPCount = CountPartialTPsSinceCycle(); + ApplyBreakeven(); + lastStatsTime = TimeCurrent(); + } + + // --- Bar-close gate for expensive checks --- + static datetime lastBarTime = 0; + datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0); + if(currentBarTime == lastBarTime) return; + lastBarTime = currentBarTime; + + // --- Update adaptive-filter state each bar --- + UpdateFilterRelaxation(); + + // --- Session + spread filters --- + if(!CheckSessionFilter()) return; + if(!CheckSpreadFilter()) return; + + // --- BO: only trade when trending; cancel everything when market returns to range --- + if(IsRangingMarket()) + { + if(gridPlaced) + { + PrintS("Market returned to range — cancelling BO orders"); + CancelAllOrders("Range detected"); + gridPlaced = false; + SaveGridState(); + } + // --- BO edge cleanup: close filled BO positions that reversed back into range --- + double actualHigh = (InpManualHigh > 0) ? InpManualHigh : GridHigh; + double actualLow = (InpManualLow > 0) ? InpManualLow : GridLow; + double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); + if(actualHigh > 0) + ClosePositionsBySide(POSITION_TYPE_BUY, "BO edge cleanup — reversal below GridHigh"); + if(actualLow > 0) + ClosePositionsBySide(POSITION_TYPE_SELL, "BO edge cleanup — reversal above GridLow"); + return; + } + + // --- Range drift (4.6) — re-center if price drifted --- + if(gridPlaced && CheckRangeDrift()) + { + CancelAllOrders("Range drift"); + gridPlaced = false; // will replace below + } + + // --- Grid already placed: monitor fills & decide when to reset --- + if(gridPlaced) + { + int buyLimits = CountPendingOrders(ORDER_TYPE_BUY_STOP); + int sellLimits = CountPendingOrders(ORDER_TYPE_SELL_STOP); + int buyPos = 0, sellPos = 0; + for(int i = PositionsTotal() - 1; i >= 0; i--) + { + ulong ticket = PositionGetTicket(i); + if(ticket == 0) continue; + if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; + if(PositionGetInteger(POSITION_MAGIC) != MagicNum) continue; + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) buyPos++; + if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) sellPos++; + } + + // Cancel remaining BO stops once a direction fills (one-fill-per-side, avoid pyramiding) + if(buyPos > 0 && buyLimits > 0) + { + for(int i = OrdersTotal() - 1; i >= 0; i--) + { + ulong ticket = OrderGetTicket(i); + if(ticket == 0) continue; + if(OrderGetString(ORDER_SYMBOL) != _Symbol) continue; + if(OrderGetInteger(ORDER_MAGIC) != MagicNum) continue; + if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP) + trade.OrderDelete(ticket); + } + } + if(sellPos > 0 && sellLimits > 0) + { + for(int i = OrdersTotal() - 1; i >= 0; i--) + { + ulong ticket = OrderGetTicket(i); + if(ticket == 0) continue; + if(OrderGetString(ORDER_SYMBOL) != _Symbol) continue; + if(OrderGetInteger(ORDER_MAGIC) != MagicNum) continue; + if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP) + trade.OrderDelete(ticket); + } + } + + // Cycle closed? + if(buyLimits == 0 && sellLimits == 0 && buyPos == 0 && sellPos == 0) + { + if(InpStopAfterProfit && AccountInfoDouble(ACCOUNT_EQUITY) > cycleStartEquity) + { + PrintS("Profitable cycle — stop for today"); + cycleProfitStop = true; + } + EmitCycleReport(false); + gridPlaced = false; + cycleStartEquity = 0; + cycleStartTime = 0; + cyclePartialTPCount = 0; + SaveGridState(); + } + return; + } + + // --- Block new grids if profit-stop active --- + if(cycleProfitStop) + { + if(DiagnosticModeOn) PrintS("Profit-stop active — no new grid today"); + return; + } + + // --- Trade enable checks --- + if(OpenNewTrades == "N" || OpenNewTrades == "n") return; + if(!bEnableLongs && !bEnableShorts) return; + + // --- Correlation cap (4.3) --- + bool canLong = bEnableLongs && CheckCorrelationCap(true); + bool canShort = bEnableShorts && CheckCorrelationCap(false); + if(!canLong && !canShort) return; + + // --- Resolve grid geometry --- + double actualHigh = (InpManualHigh > 0) ? InpManualHigh : GridHigh; + double actualLow = (InpManualLow > 0) ? InpManualLow : GridLow; + if(actualHigh <= actualLow) + { + PrintS("Invalid grid bounds — skip"); + return; + } + + currentEntryPts = ResolveEntryPoints(); + double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); + double entryPrice = currentEntryPts * point; + double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); + + // --- Derive effective levels from actual range (3.2) --- + double span = actualHigh - actualLow; + int derivedLevels = (entryPrice > 0) ? (int)(span / entryPrice) : MaxLevels; + int effectiveLevels = MathMin(MaxLevels, MathMax(1, derivedLevels)); + + PrintS("Placing BO orders — px=" + DoubleToString(currentPrice, _Digits) + + " [" + DoubleToString(actualLow, _Digits) + "-" + DoubleToString(actualHigh, _Digits) + "]" + + " entry=" + DoubleToString(currentEntryPts, 0) + "pts" + + " levels=" + IntegerToString(effectiveLevels) + + (filtersRelaxed ? " (relaxed)" : "")); + + int buyCount = 0, sellCount = 0; + double noTradeZone = entryPrice * 2; + + for(int i = 0; i < effectiveLevels; i++) + { + // BO: buy above resistance, sell below support + double buyLevel = actualHigh + ((i + 1) * entryPrice); + double sellLevel = actualLow - ((i + 1) * entryPrice); + + if(canLong && buyLevel > currentPrice + noTradeZone) + if(PlaceBuyStop(buyLevel, i)) buyCount++; + if(canShort && sellLevel < currentPrice - noTradeZone) + if(PlaceSellStop(sellLevel, i)) sellCount++; + } + + PrintS("BO orders placed: " + IntegerToString(buyCount) + " buy, " + + IntegerToString(sellCount) + " sell stops"); + + if(buyCount > 0 || sellCount > 0) + { + gridPlaced = true; + cycleStartEquity = AccountInfoDouble(ACCOUNT_EQUITY); + cycleStartTime = TimeCurrent(); + cyclePartialTPCount = 0; + SaveGridState(); + } + } +//+------------------------------------------------------------------+ diff --git a/bo-audnzd.set b/bo-audnzd.set new file mode 100644 index 0000000..3c0d51f --- /dev/null +++ b/bo-audnzd.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - AUDNZD === +; Slow-moving cross — BO fires on RBA/RBNZ policy divergence +; Timeframe: H1 + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=10 +TP=1000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=22 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.2 + +; === Stop Loss === +InpUseStopLoss=true +StopLoss=180 + +; === Risk Management === +TRADE_RANGE=40 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=2.0 +InpMaxWeeklyDrawdown=5.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444012 + +; === Session Notes === +; Very slow pair — BO events are rare but can be strong when they occur +; Both central banks are commodity-linked; global risk-off affects both equally diff --git a/bo-audusd.set b/bo-audusd.set new file mode 100644 index 0000000..cc9fbb0 --- /dev/null +++ b/bo-audusd.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - AUDUSD === +; Commodity-linked pair — trends on China/risk data +; Timeframe: H1 + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=20 +TP=2000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=25 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.5 + +; === Stop Loss === +InpUseStopLoss=true +StopLoss=300 + +; === Risk Management === +TRADE_RANGE=60 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444005 + +; === Session Notes === +; Strong moves on RBA decisions, Chinese PMI, iron ore price swings +; Asia session active for this pair diff --git a/bo-eurchf.set b/bo-eurchf.set new file mode 100644 index 0000000..47756dd --- /dev/null +++ b/bo-eurchf.set @@ -0,0 +1,59 @@ +; === OrdersEA_Smart_Grid_BO - EURCHF === +; Historically low-volatility pair — BO only fires on genuine regime breaks +; Timeframe: H1 +; WARNING: SNB surprise interventions can produce 500+ pt moves instantly +; BO with SL is safer than Grid for this pair during intervention events + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=10 +TP=1000 +Lots=0.01 +MaxLevels=2 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=20 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.2 + +; === Stop Loss (especially important here — SNB events are catastrophic without it) === +InpUseStopLoss=true +StopLoss=200 + +; === Risk Management === +TRADE_RANGE=40 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=2.0 +InpMaxWeeklyDrawdown=5.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444010 + +; === Session Notes === +; Low daily range — BO will fire rarely, only on real breakout events +; SNB quarterly assessments: use GetOut=A before each one +; DO NOT run overnight without SL — 2015 CHF event removed 20% in seconds diff --git a/bo-eurgbp.set b/bo-eurgbp.set new file mode 100644 index 0000000..9d7bb65 --- /dev/null +++ b/bo-eurgbp.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - EURGBP === +; Low-range pair except on UK/EU political divergence events +; Timeframe: H1 + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=10 +TP=1000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=22 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.2 + +; === Stop Loss === +InpUseStopLoss=true +StopLoss=200 + +; === Risk Management === +TRADE_RANGE=40 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=2.0 +InpMaxWeeklyDrawdown=5.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444011 + +; === Session Notes === +; Best BO opportunities: UK/EU trade data, BOE vs ECB policy divergence +; Generally quiet — expect infrequent fills diff --git a/bo-eurjpy.set b/bo-eurjpy.set new file mode 100644 index 0000000..f567f4d --- /dev/null +++ b/bo-eurjpy.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - EURJPY === +; Risk proxy pair — trends sharply on risk sentiment shifts +; Timeframe: H1 + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=25 +TP=2000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=25 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.7 + +; === Stop Loss === +InpUseStopLoss=true +StopLoss=400 + +; === Risk Management === +TRADE_RANGE=70 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444009 + +; === Session Notes === +; Strong during European session when risk appetite shifts +; BOJ intervention risk — monitor JPY pairs across the board diff --git a/bo-eurusd.set b/bo-eurusd.set new file mode 100644 index 0000000..85f65bd --- /dev/null +++ b/bo-eurusd.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - EURUSD === +; Most liquid pair, tight spreads, clean technical moves +; Timeframe: H1 + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=20 +TP=2000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=25 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.5 + +; === Stop Loss === +InpUseStopLoss=true +StopLoss=300 + +; === Risk Management === +TRADE_RANGE=60 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444001 + +; === Session Notes === +; Best BO windows: London open (07:00 GMT), NY open (13:30 GMT) +; Avoid: ECB press conferences, NFP first 5 min (too much noise) diff --git a/bo-gbpjpy.set b/bo-gbpjpy.set new file mode 100644 index 0000000..797ee08 --- /dev/null +++ b/bo-gbpjpy.set @@ -0,0 +1,58 @@ +; === OrdersEA_Smart_Grid_BO - GBPJPY === +; "The Beast" — extreme volatility, excellent breakout pair +; Timeframe: H1 +; WARNING: 200+ pt moves common; SL is non-negotiable + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=50 +TP=5000 +Lots=0.01 +MaxLevels=2 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=28 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=2.2 + +; === Stop Loss (MANDATORY — GBPJPY has no mercy on unprotected positions) === +InpUseStopLoss=true +StopLoss=800 + +; === Risk Management === +TRADE_RANGE=120 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=true +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444004 + +; === Session Notes === +; GBPJPY BO targets: London open, US session risk events +; Avoid: BOJ + BOE same-day events (whipsaw kills BO fills) +; Reduce Lots to 0.01 and monitor first 2 weeks closely diff --git a/bo-gbpusd.set b/bo-gbpusd.set new file mode 100644 index 0000000..6837767 --- /dev/null +++ b/bo-gbpusd.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - GBPUSD === +; Higher volatility than EURUSD — wider entry and SL +; Timeframe: H1 + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=25 +TP=2500 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=25 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.6 + +; === Stop Loss === +InpUseStopLoss=true +StopLoss=350 + +; === Risk Management === +TRADE_RANGE=70 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444002 + +; === Session Notes === +; Cable breaks hard on UK data, BOE announcements +; Avoid BoE rate decision days unless watching live diff --git a/bo-major.set b/bo-major.set new file mode 100644 index 0000000..a374081 --- /dev/null +++ b/bo-major.set @@ -0,0 +1,57 @@ +; === OrdersEA_Smart_Grid_BO - Major Pairs Template === +; Strategy: Breakout — buy above resistance, sell below support +; Best for: EURUSD, GBPUSD, AUDUSD, USDJPY +; Timeframe: H1 +; Activates when ADX > 25 (trending) and price breaks grid boundary + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=20 +TP=2000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters (inverted logic in BO: low ADXMax = quick trend detection) === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=25 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.5 + +; === Stop Loss (mandatory for breakout) === +InpUseStopLoss=true +StopLoss=300 + +; === Risk Management === +TRADE_RANGE=60 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === Session Notes === +; BO fires on breakout of daily pivot range (ATR-scaled) +; Best: London open (07:00-09:00 GMT), NY open (13:30-15:00 GMT) +; Avoid: Low-volume overnight sessions (Asia) +; SL should be below GridHigh for buys, above GridLow for sells diff --git a/bo-nzdusd.set b/bo-nzdusd.set new file mode 100644 index 0000000..6d7817a --- /dev/null +++ b/bo-nzdusd.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - NZDUSD === +; Similar to AUDUSD, slightly lower volatility +; Timeframe: H1 + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=20 +TP=2000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=25 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.4 + +; === Stop Loss === +InpUseStopLoss=true +StopLoss=280 + +; === Risk Management === +TRADE_RANGE=55 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444006 + +; === Session Notes === +; RBNZ decisions can cause strong BO moves +; Lower daily range than AUDUSD — expect fewer fills diff --git a/bo-steady.set b/bo-steady.set new file mode 100644 index 0000000..79ebae9 --- /dev/null +++ b/bo-steady.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - Steady/Low-Vol Pairs Template === +; Strategy: Breakout — buy above resistance, sell below support +; Best for: EURCHF, EURGBP, AUDNZD +; Timeframe: H1 +; These pairs trend less — lower ADXMax catches moderate trends early +; WARNING: EURCHF carries SNB intervention risk (see pair notes below) + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=10 +TP=1000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=22 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.2 + +; === Stop Loss (mandatory) === +InpUseStopLoss=true +StopLoss=200 + +; === Risk Management === +TRADE_RANGE=40 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=2.0 +InpMaxWeeklyDrawdown=5.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === Session Notes === +; Lower volatility = smaller moves but more frequent breakouts of ATR range +; Tight SL important — false breakouts common on these pairs diff --git a/bo-usdcad.set b/bo-usdcad.set new file mode 100644 index 0000000..627aed9 --- /dev/null +++ b/bo-usdcad.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - USDCAD === +; Oil-correlated pair — trends strongly on WTI moves and BOC events +; Timeframe: H1 + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=20 +TP=2000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=25 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.5 + +; === Stop Loss === +InpUseStopLoss=true +StopLoss=300 + +; === Risk Management === +TRADE_RANGE=60 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444007 + +; === Session Notes === +; Watch WTI crude oil price — USDCAD is inversely correlated +; BOC rate announcements produce clean BO moves diff --git a/bo-usdchf.set b/bo-usdchf.set new file mode 100644 index 0000000..e7058b2 --- /dev/null +++ b/bo-usdchf.set @@ -0,0 +1,57 @@ +; === OrdersEA_Smart_Grid_BO - USDCHF === +; Safe-haven pair — breakouts on risk-off events +; Timeframe: H1 +; NOTE: Inversely correlated with EURUSD — avoid running both long/short simultaneously + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=20 +TP=2000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=25 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.4 + +; === Stop Loss === +InpUseStopLoss=true +StopLoss=280 + +; === Risk Management === +TRADE_RANGE=55 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444008 + +; === Session Notes === +; SNB intervention risk — USDCHF can gap on SNB surprise moves +; Safe-haven demand spikes on geopolitical events diff --git a/bo-usdjpy.set b/bo-usdjpy.set new file mode 100644 index 0000000..fb96b03 --- /dev/null +++ b/bo-usdjpy.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - USDJPY === +; Risk-on/risk-off pair — strong trending behaviour on macro events +; Timeframe: H1 + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=20 +TP=2000 +Lots=0.01 +MaxLevels=3 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=25 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=1.5 + +; === Stop Loss === +InpUseStopLoss=true +StopLoss=300 + +; === Risk Management === +TRADE_RANGE=60 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=false +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444003 + +; === Session Notes === +; BOJ intervention risk — USDJPY can gap 200+ pts on intervention +; Watch for BOJ announcements; use GetOut=A if intervention suspected diff --git a/bo-volatile.set b/bo-volatile.set new file mode 100644 index 0000000..d0bb0e5 --- /dev/null +++ b/bo-volatile.set @@ -0,0 +1,56 @@ +; === OrdersEA_Smart_Grid_BO - Volatile Pairs Template === +; Strategy: Breakout — buy above resistance, sell below support +; Best for: GBPJPY, XAUUSD, GBPUSD (high volatility) +; Timeframe: H1 +; WARNING: Large moves — SL is critical, size down + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=50 +TP=5000 +Lots=0.01 +MaxLevels=2 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=28 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=2.0 + +; === Stop Loss (mandatory) === +InpUseStopLoss=true +StopLoss=800 + +; === Risk Management === +TRADE_RANGE=100 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=true +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === Session Notes === +; GBPJPY/XAUUSD can gap 200+ pts — BO suits them well but cut losses fast +; Monitor DiagnosticMode output on first few trades +; Consider halving Lots on first live session diff --git a/bo-xagusd.set b/bo-xagusd.set new file mode 100644 index 0000000..28c7083 --- /dev/null +++ b/bo-xagusd.set @@ -0,0 +1,58 @@ +; === OrdersEA_Smart_Grid_BO - XAGUSD (Silver) === +; More volatile than Gold per pip — high leverage, high reward breakouts +; Timeframe: H1 +; WARNING: Silver can move 3-4x the range of Gold in percentage terms + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=60 +TP=3000 +Lots=0.01 +MaxLevels=2 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=28 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=2.2 + +; === Stop Loss (MANDATORY) === +InpUseStopLoss=true +StopLoss=1000 + +; === Risk Management === +TRADE_RANGE=150 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=true +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444014 + +; === Session Notes === +; Silver correlates with Gold but amplifies moves +; Wider spreads than Gold — Entry=60 ensures we're past the spread cost +; Reduce Lots further if running alongside XAUUSD (correlated exposure) diff --git a/bo-xauusd.set b/bo-xauusd.set new file mode 100644 index 0000000..f2d86cd --- /dev/null +++ b/bo-xauusd.set @@ -0,0 +1,59 @@ +; === OrdersEA_Smart_Grid_BO - XAUUSD (Gold) === +; Strongest trending instrument — excellent BO candidate on macro events +; Timeframe: H1 +; WARNING: 500+ pt moves on FOMC, NFP, geopolitical shocks — SL is critical + +; === Grid / BO Settings === +UseAutoPivots=true +InpManualHigh=0 +InpManualLow=0 +Entry=100 +TP=5000 +Lots=0.01 +MaxLevels=2 + +; === Range Filters === +UseRSIFilter=false +RSIPeriod=14 +RSILower=40 +RSIUpper=60 +UseADXFilter=true +ADXPeriod=14 +ADXMax=30 +UseATRFilter=true +ATRPeriod=14 +ATRMultiplier=2.5 + +; === Stop Loss (MANDATORY — Gold can wipe 500 pts without SL) === +InpUseStopLoss=true +StopLoss=1500 + +; === Risk Management === +TRADE_RANGE=200 +LongLimit=0 +ShortLimit=0 +GetOut=N +OpenNewTrades=Y +TakeProfitLevelPercent=0 +TakeProfitLevelDollarAmount=0 +EquityFactorPercent=0 +LotsFactorPercent=0 +BaseEquity=10000 +Master=false +DiagnosticModeOn=true +InpMaxDailyDrawdown=3.0 +InpMaxWeeklyDrawdown=7.0 + +; === Weekend Protection === +InpCloseBeforeWeekend=true +InpWeekendCloseHour=17 +InpCancelPendingBeforeWeekend=true + +; === EA Settings === +MagicNum=444013 + +; === Session Notes === +; Best BO events: FOMC decisions, NFP, geopolitical escalation, CPI surprises +; Gold trends very cleanly once ADX > 30 +; ADXMax=30 set higher than other pairs — Gold needs confirmed trend before BO fires +; Avoid thin Asian overnight sessions for BO entry