//+------------------------------------------------------------------+ //| PaPP_Median_EA.mq5 | //| PaPP v2 | //+------------------------------------------------------------------+ #include //+------------------------------------------------------------------+ #property copyright "PaPP v2" #property version "2.00" #property description "PaPP Median EA - Mean Reversion puro" #property description "Linea mediana (media 8 MA 1g-1y) = unico segnale" #property description "Sopra = SELL | Sotto = BUY" input double LotSize = 0.01; input int TrailStart = 0; input int TrailStep = 0; input int MaxPosPerSide = 3; input bool DebugPrint = true; input int Magic = 2024002; input int Slippage = 30; int hMA[8]; int bars[8]; datetime lastBar = 0; bool buyFired = false; bool sellFired = false; //+------------------------------------------------------------------+ int TimeToBars(int d) { datetime n = TimeCurrent(); if(n==0) { long s = (long)d*86400L, p = PeriodSeconds((ENUM_TIMEFRAMES)_Period); return (int)MathMax(1,s/p); } return MathMax(1,Bars(_Symbol,_Period,n-d*86400,n)); } //+------------------------------------------------------------------+ int OnInit() { int days[8] = {365,182,121,30,14,7,3,1}; for(int i=0;i<8;i++) { bars[i] = (i<7) ? TimeToBars(days[i]) : 1; hMA[i] = iMA(_Symbol,_Period,bars[i],0,MODE_SMA,PRICE_CLOSE); if(hMA[i]==INVALID_HANDLE) return INIT_FAILED; } if(DebugPrint) Print("=== PaPP Median EA v2.00 INIT ==="); Print(" Lot=",LotSize," Trail=",TrailStart,"/",TrailStep," MaxPos=",MaxPosPerSide); for(int i=0;i<8;i++) Print(" MA[",i,"] bars=",bars[i]); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { for(int i=0;i<8;i++) if(hMA[i]!=INVALID_HANDLE) IndicatorRelease(hMA[i]); } //+------------------------------------------------------------------+ double GetMA(int idx,int m) { double buf[1]; if(CopyBuffer(hMA[m],0,idx+1,1,buf)==1) return buf[0]; return 0; } //+------------------------------------------------------------------+ int CountPos(int type) { int n=0; for(int i=PositionsTotal()-1; i>=0; i--) { ulong t=PositionGetTicket(i); if(t>0 && PositionSelectByTicket(t)) if(PositionGetInteger(POSITION_MAGIC)==Magic && PositionGetString(POSITION_SYMBOL)==_Symbol) if(PositionGetInteger(POSITION_TYPE)==type) n++; } return n; } //+------------------------------------------------------------------+ void TrailAll() { if(TrailStart<=0 || TrailStep<=0) return; double point = SymbolInfoDouble(_Symbol,SYMBOL_POINT); double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); CTrade trade; for(int i=PositionsTotal()-1; i>=0; i--) { ulong t=PositionGetTicket(i); if(t<=0 || !PositionSelectByTicket(t)) continue; if(PositionGetInteger(POSITION_MAGIC)!=Magic || PositionGetString(POSITION_SYMBOL)!=_Symbol) continue; bool isBuy = (PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY); double entry = PositionGetDouble(POSITION_PRICE_OPEN); double currSL = PositionGetDouble(POSITION_SL); double currTP = PositionGetDouble(POSITION_TP); double newSL=0; if(isBuy && bid >= entry+TrailStart*point) { newSL = bid - TrailStep*point; if(newSL > currSL+point && trade.PositionModify(t,newSL,currTP)) if(DebugPrint) Print(">>> TRAIL BUY t",t," SL->",DoubleToString(newSL,_Digits)); } if(!isBuy && ask <= entry-TrailStart*point) { newSL = ask + TrailStep*point; if(currSL==0 || newSL < currSL-point) { if(trade.PositionModify(t,newSL,currTP)) if(DebugPrint) Print(">>> TRAIL SELL t",t," SL->",DoubleToString(newSL,_Digits)); } } } } //+------------------------------------------------------------------+ void UpdateTPSL(double median) { double point = SymbolInfoDouble(_Symbol,SYMBOL_POINT); CTrade trade; for(int i=PositionsTotal()-1; i>=0; i--) { ulong t=PositionGetTicket(i); if(t<=0 || !PositionSelectByTicket(t)) continue; if(PositionGetInteger(POSITION_MAGIC)!=Magic || PositionGetString(POSITION_SYMBOL)!=_Symbol) continue; bool isBuy = PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY; double entry = PositionGetDouble(POSITION_PRICE_OPEN); double currSL = PositionGetDouble(POSITION_SL); double currTP = PositionGetDouble(POSITION_TP); double tpDist = MathAbs(median-entry); if(tpDist>> UPDATE t",t," TP=",DoubleToString(newTP,_Digits), " SL=",DoubleToString(newSL,_Digits)); trade.PositionModify(t,newSL,newTP); } } //+------------------------------------------------------------------+ void OnTick() { TrailAll(); datetime curBar = iTime(_Symbol,_Period,0); if(curBar==lastBar) return; lastBar = curBar; double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); double point = SymbolInfoDouble(_Symbol,SYMBOL_POINT); //--- Leggi le 8 MA e calcola mediana double mv[8]; int valid=0; double sum=0; for(int m=0;m<8;m++) { mv[m]=GetMA(0,m); if(mv[m]>0) { sum+=mv[m]; valid++; } } if(valid==0 || sum<=0) { if(DebugPrint) Print("SKIP: valid=",valid); return; } double median = sum/valid; //--- Update TP/SL per tutte le posizioni con la mediana corrente UpdateTPSL(median); //--- Banda = min/max di MA0-MA6 (1Y a 3G), esclude MA7 (SMA(1)=noise) double minMA=mv[0], maxMA=mv[0]; for(int m=1;m<7;m++) if(mv[m]>0) { if(mv[m]maxMA) maxMA=mv[m]; } double buyBand=minMA, sellBand=maxMA; double distPct = (bid-median)/median*100; int nBuy = CountPos(POSITION_TYPE_BUY); int nSell = CountPos(POSITION_TYPE_SELL); //--- Reset escursion flags quando price rientra nel cluster if(bid>=buyBand && bid<=sellBand) { buyFired=false; sellFired=false; } //--- MA alignment: se tutte monotone = trend, blocca mean reversion bool trendUp=true, trendDown=true; for(int m=0;m<7;m++) if(mv[m] > mv[m+1]) trendUp=false; else if(mv[m] < mv[m+1]) trendDown=false; else { trendUp=false; trendDown=false; } bool allowBuy = !buyFired && nBuy0) Print(" MA",m,"=",DoubleToString(mv[m],_Digits), " diff=",DoubleToString(bid-mv[m],_Digits)); Print("---"); } //--- Entry: fuori dal cluster, TP=mediana, SL simmetrico if(bid < buyBand && allowBuy) { double entry = ask; double tpDist = MathAbs(median-entry); double sl = NormalizeDouble(entry-tpDist,_Digits); double tp = NormalizeDouble(median,_Digits); if(sl>=point) { if(DebugPrint) Print(">>> BUY: bid=",DoubleToString(bid,_Digits), " < buyBand=",DoubleToString(buyBand,_Digits), " TP=",DoubleToString(tp,_Digits)," SL=",DoubleToString(sl,_Digits), " (",DoubleToString(tpDist/point,0),"pts)"); MqlTradeRequest req={}; MqlTradeResult res={}; req.action = TRADE_ACTION_DEAL; req.symbol = _Symbol; req.volume = LotSize; req.type = ORDER_TYPE_BUY; req.price = entry; req.sl = sl; req.tp = tp; req.deviation = Slippage; req.magic = Magic; req.comment = "Pv2B "+DoubleToString(distPct,1)+"%"; if(OrderSend(req,res) && res.retcode==TRADE_RETCODE_DONE) { if(DebugPrint) Print(">>> BUY OPENED t",res.order); buyFired=true; } else if(DebugPrint) Print("BUY fail: c",res.retcode); } else if(DebugPrint) Print("BUY SKIP: tpDist too small (",DoubleToString(tpDist/point,0),"pts)"); } if(bid > sellBand && allowSell) { double entry = bid; double tpDist = MathAbs(entry-median); double sl = NormalizeDouble(entry+tpDist,_Digits); double tp = NormalizeDouble(median,_Digits); if(tpDist>=point) { if(DebugPrint) Print(">>> SELL: bid=",DoubleToString(bid,_Digits), " > sellBand=",DoubleToString(sellBand,_Digits), " TP=",DoubleToString(tp,_Digits)," SL=",DoubleToString(sl,_Digits), " (",DoubleToString(tpDist/point,0),"pts)"); MqlTradeRequest req={}; MqlTradeResult res={}; req.action = TRADE_ACTION_DEAL; req.symbol = _Symbol; req.volume = LotSize; req.type = ORDER_TYPE_SELL; req.price = entry; req.sl = sl; req.tp = tp; req.deviation = Slippage; req.magic = Magic; req.comment = "Pv2S "+DoubleToString(distPct,1)+"%"; if(OrderSend(req,res) && res.retcode==TRADE_RETCODE_DONE) { if(DebugPrint) Print(">>> SELL OPENED t",res.order); sellFired=true; } else if(DebugPrint) Print("SELL fail: c",res.retcode); } else if(DebugPrint) Print("SELL SKIP: tpDist too small (",DoubleToString(tpDist/point,0),"pts)"); } if(DebugPrint && bid>=buyBand && bid<=sellBand) Print("NO ENTRY: inside cluster [",DoubleToString(buyBand,_Digits), " - ",DoubleToString(sellBand,_Digits),"]"); } //+------------------------------------------------------------------+