//+------------------------------------------------------------------+ //| EA ICT-style: Daily Bias -> MidTf FVG -> LowTf pullback entries | //+------------------------------------------------------------------+ #property strict #include CTrade trade; //--- Inputs input double RiskPerTrade = 1.0; // % equity risk per trade input int MagicNumber = 33333; input int Mid_TF_FVG_lookback = 200; // bars to scan for MidTf FVG input int Low_TF_FVG_lookback = 200; // bars to scan for LowTf FVG/MSS input double MinADX = 10.0; input int ADXPeriod = 14; input double MaxAcceptableSpread = 200; // points input double RR = 3.0; // Risk:Reward multiplier (TP = RR * risk distance) //--- Timeframe configuration (configurable) input ENUM_TIMEFRAMES TF_High = PERIOD_D1; // Higher timeframe used for bias input ENUM_TIMEFRAMES TF_Mid = PERIOD_H1; // Mid timeframe for FVG input ENUM_TIMEFRAMES TF_Low = PERIOD_M5; // Low timeframe for entries //--- Internal structs struct Zone { double top; double bottom; int from_index; int to_index; }; enum DailyBias { BIAS_UNKNOWN=0, BIAS_UP=1, BIAS_DOWN=-1 }; //--- Forward DailyBias DetermineDailyBias(); int FindMidTfFvg(DailyBias bias, Zone &foundZone); int FindLowTfFvg(Zone &midTfZone, DailyBias bias, Zone &lowTfZone); bool DetectLowTfMSS(int &mssType, double &mssPrice); double CalculateLotForRisk(double entryPrice, double stopPrice); bool HasActiveOrders(); void PlaceLimitOrder(int side, double price, double sl, double tp, double lot); //+------------------------------------------------------------------+ int OnInit() { PrintFormat("EA init: TF_High=%d TF_Mid=%d TF_Low=%d", (int)TF_High, (int)TF_Mid, (int)TF_Low); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ void OnTick() { static datetime lastBarTime=0; datetime t = iTime(_Symbol, TF_Low, 0); if(t==lastBarTime) return; lastBarTime = t; // basic symbol params check double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(point <= 0) { Print("Invalid SYMBOL_POINT -> abort tick"); return; } // spread check double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); double spreadPts = (ask - bid)/point; if(spreadPts > MaxAcceptableSpread) { PrintFormat("Spread too high: %.1f pts", spreadPts); return; } // 1) bias on TF_High DailyBias bias = DetermineDailyBias(); if(bias==BIAS_UNKNOWN) { Print("Bias unknown -> skip"); return; } PrintFormat("Bias = %d", bias); // 2) Mid TF FVG Zone midZone; if(FindMidTfFvg(bias, midZone) == 0) { Print("No MidTf FVG -> skip"); return; } PrintFormat("Mid zone: top=%.5f bottom=%.5f", midZone.top, midZone.bottom); // 3) Low TF FVG inside mid zone Zone lowZone; if(FindLowTfFvg(midZone, bias, lowZone) == 0) { Print("No LowTf FVG -> skip"); return; } PrintFormat("Low zone: top=%.5f bottom=%.5f", lowZone.top, lowZone.bottom); // 4) MSS detect on low TF int mssType=0; double mssPrice=0; if(!DetectLowTfMSS(mssType, mssPrice)) { Print("No LowTf MSS -> skip"); return; } PrintFormat("LowTf MSS type=%d price=%.5f", mssType, mssPrice); // 5) Entry at lowZone.bottom (buy) or top (sell) double entry = (bias==BIAS_UP) ? lowZone.bottom : lowZone.top; double sl=0, tp=0; // compute swing extreme on TF_Low with validation if(bias==BIAS_UP) { double swingLow = DBL_MAX; for(int i=0;i skip"); return; } sl = swingLow - 5*point; if(sl >= entry) { Print("SL >= entry -> skip"); return; } tp = entry + RR * (entry - sl); } else { double swingHigh = -DBL_MAX; for(int i=0;i swingHigh) swingHigh = hv; } if(swingHigh==-DBL_MAX) { Print("Not enough low TF bars -> skip"); return; } sl = swingHigh + 5*point; if(sl <= entry) { Print("SL <= entry -> skip"); return; } tp = entry - RR * (sl - entry); } // 6) lot calc double lot = CalculateLotForRisk(entry, sl); if(lot <= 0) { Print("Lot <= 0 -> skip"); return; } // 7) only one active for EA on symbol int side = (bias==BIAS_UP) ? POSITION_TYPE_BUY : POSITION_TYPE_SELL; if(HasActiveOrders()) { Print("Active order exists -> skip"); return; } PlaceLimitOrder(side, entry, sl, tp, lot); } //+------------------------------------------------------------------+ DailyBias DetermineDailyBias() { int idx1=1, idx2=2, tries=0; while(tries<5) { double close1 = iClose(_Symbol, TF_High, idx1); double high2 = iHigh (_Symbol, TF_High, idx2); double low2 = iLow (_Symbol, TF_High, idx2); if(close1 > high2) return BIAS_UP; if(close1 < low2) return BIAS_DOWN; idx1++; idx2++; tries++; if(idx2>500) break; } return BIAS_UNKNOWN; } //+------------------------------------------------------------------+ int FindMidTfFvg(DailyBias bias, Zone &foundZone) { int limit = Mid_TF_FVG_lookback; for(int i=1;i high_i2) { foundZone.top = low_i; foundZone.bottom = high_i2; foundZone.from_index = i+2; foundZone.to_index = i; return 1; } } else if(bias==BIAS_DOWN) { if(high_i < low_i2) { double t = MathMax(high_i, low_i2); double b = MathMin(high_i, low_i2); foundZone.top = t; foundZone.bottom = b; foundZone.from_index = i+2; foundZone.to_index = i; return 1; } } } return 0; } //+------------------------------------------------------------------+ int FindLowTfFvg(Zone &midTfZone, DailyBias bias, Zone &lowTfZone) { int limit = Low_TF_FVG_lookback; for(int i=1;i high_i2) { double top = low_i; double bottom = high_i2; if(bottom <= midTfZone.top && top >= midTfZone.bottom) { lowTfZone.top = top; lowTfZone.bottom = bottom; return 1; } } } else { if(high_i < low_i2) { double top = low_i2; double bottom = high_i; if(bottom <= midTfZone.top && top >= midTfZone.bottom) { lowTfZone.top = top; lowTfZone.bottom = bottom; return 1; } } } } return 0; } //+------------------------------------------------------------------+ bool DetectLowTfMSS(int &mssType, double &mssPrice) { double lastSwingHigh = -DBL_MAX; int idxHigh=-1; double lastSwingLow = DBL_MAX; int idxLow=-1; int look = 50; for(int i=2;i0 && h > lastSwingHigh) { lastSwingHigh=h; idxHigh=i; } if(l>0 && l < lastSwingLow) { lastSwingLow=l; idxLow=i; } } double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); if(ask > lastSwingHigh) { mssType=1; mssPrice = lastSwingHigh; return true; } if(bid < lastSwingLow) { mssType=-1; mssPrice = lastSwingLow; return true; } return false; } //+------------------------------------------------------------------+ double CalculateLotForRisk(double entryPrice, double stopPrice) { double equity = AccountInfoDouble(ACCOUNT_EQUITY); double riskMoney = equity * (RiskPerTrade/100.0); double point = SymbolInfoDouble(_Symbol,SYMBOL_POINT); double tickValue = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE); // Fallback: if broker returns 0 for tickValue/tickSize, estimate valuePerPoint using contract size: double contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE); if(tickValue<=0 || tickSize<=0) { // rough fallback: assume 1 lot -> contractSize * point movement value (this might be instrument-specific) tickValue = contractSize; tickSize = 1.0; } if(point<=0 || tickValue<=0 || tickSize<=0) { Print("Invalid symbol params for lot calc"); return 0; } double stopPoints = MathAbs(entryPrice - stopPrice)/point; if(stopPoints <= 0) return 0; double valuePerPoint = tickValue * (point / tickSize); if(valuePerPoint <= 0) { Print("valuePerPoint invalid"); return 0; } double rawLot = riskMoney / (stopPoints * valuePerPoint); double minLot = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); double lotStep= SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); double maxLot = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); if(lotStep<=0) lotStep=0.01; double n = MathFloor(rawLot / lotStep); double lot = n * lotStep; if(lot < minLot) lot = minLot; if(lot > maxLot) lot = maxLot; lot = NormalizeDouble(lot,2); PrintFormat("CalcLot: entry=%.5f stop=%.5f stopPts=%.1f rawLot=%.4f finalLot=%.2f", entryPrice, stopPrice, stopPoints, rawLot, lot); return lot; } //+------------------------------------------------------------------+ bool HasActiveOrders() { // check open positions for(int i=0;i=0;i--) { string name = ObjectName(0,i); if(StringFind(name,"ICT_ENTRY_")==0 || StringFind(name,"ICT_SL_")==0 || StringFind(name,"ICT_TP_")==0) ObjectDelete(0,name); } Print("EA deinitialized - cleaned objects."); } //+------------------------------------------------------------------+