//+------------------------------------------------------------------+ //| PaPP_Trading.mq5 | //| PaPP Trading | //+------------------------------------------------------------------+ #property copyright "PaPP Trading" #property version "2.00" #property description "PaPP Trading - Multi timeframe MA + volatilita + accelerazione" #property indicator_chart_window #property indicator_buffers 10 #property indicator_plots 10 input bool ShowLines = false; input int FontSize = 9; input int XPos = 10; input int YPos = 30; input int TP_Points = 100; // Take Profit (punti) input int SL_Points = 10000; // Stop Loss (punti) input bool ShowTPSL = true; // Draw TP/SL lines on chart //--- indicator buffers (SetIndexBuffer bound) double B1Y[], B6M[], B4M[], B1Mo[], B2W[], B1W[], B3D[], B1D[], BSc[], BAc[]; //--- handles int hMA[8]; int bars[8]; string pN[8] = {"1Y","6M","4M","1M","2W","1W","3G","1G"}; string _pfx = "PP_"; //--- rolling volatility window double hMin[8], hMax[8]; int volResetBar[8]; // bar index (non-series) of last reset int WINDOW = 200; // rolling window bars //--- last bar tracking for dashboard update datetime lastBarTime = 0; //+------------------------------------------------------------------+ int TimeToBars(int days) { datetime now = TimeCurrent(); if(now==0) { long s = (long)days*86400L; long p = PeriodSeconds((ENUM_TIMEFRAMES)_Period); return (int)MathMax(1,s/p); } datetime then = now - days*86400; int cnt = Bars(_Symbol,_Period,then,now); return MathMax(1,cnt); } //+------------------------------------------------------------------+ int OnInit() { IndicatorSetString(INDICATOR_SHORTNAME,"PaPP Trading v2"); IndicatorSetInteger(INDICATOR_DIGITS,_Digits); 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; } SetIndexBuffer(0,B1Y,INDICATOR_DATA); ArraySetAsSeries(B1Y,true); SetIndexBuffer(1,B6M,INDICATOR_DATA); ArraySetAsSeries(B6M,true); SetIndexBuffer(2,B4M,INDICATOR_DATA); ArraySetAsSeries(B4M,true); SetIndexBuffer(3,B1Mo,INDICATOR_DATA); ArraySetAsSeries(B1Mo,true); SetIndexBuffer(4,B2W,INDICATOR_DATA); ArraySetAsSeries(B2W,true); SetIndexBuffer(5,B1W,INDICATOR_DATA); ArraySetAsSeries(B1W,true); SetIndexBuffer(6,B3D,INDICATOR_DATA); ArraySetAsSeries(B3D,true); SetIndexBuffer(7,B1D,INDICATOR_DATA); ArraySetAsSeries(B1D,true); SetIndexBuffer(8,BSc,INDICATOR_DATA); ArraySetAsSeries(BSc,true); SetIndexBuffer(9,BAc,INDICATOR_DATA); ArraySetAsSeries(BAc,true); color cl[10] = {clrRed,clrOrange,clrGold,clrLimeGreen, clrDodgerBlue,clrViolet,clrBrown,clrGray,clrCyan,clrMagenta}; for(int i=0;i<8;i++) { PlotIndexSetInteger(i,PLOT_DRAW_TYPE,ShowLines?DRAW_LINE:DRAW_NONE); PlotIndexSetInteger(i,PLOT_LINE_COLOR,cl[i]); PlotIndexSetString(i,PLOT_LABEL,pN[i]+" MA"); PlotIndexSetInteger(i,PLOT_LINE_WIDTH,2); } PlotIndexSetInteger(8,PLOT_DRAW_TYPE,DRAW_NONE); PlotIndexSetInteger(9,PLOT_DRAW_TYPE,DRAW_NONE); for(int i=0;i<8;i++) { hMin[i]=1e10; hMax[i]=-1e10; volResetBar[i]=0; } return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { for(int i=0;i<8;i++) if(hMA[i]!=INVALID_HANDLE) IndicatorRelease(hMA[i]); ObjectsDeleteAll(0,_pfx); } //+------------------------------------------------------------------+ //| Buffer helpers (separate MA index from bar index) | //+------------------------------------------------------------------+ void SetMA(int b, int bar, double v) { if(b==0) B1Y[bar]=v; else if(b==1) B6M[bar]=v; else if(b==2) B4M[bar]=v; else if(b==3) B1Mo[bar]=v; else if(b==4) B2W[bar]=v; else if(b==5) B1W[bar]=v; else if(b==6) B3D[bar]=v; else if(b==7) B1D[bar]=v; } double GetMA(int bar, int m) { if(m==0) return B1Y[bar]; if(m==1) return B6M[bar]; if(m==2) return B4M[bar]; if(m==3) return B1Mo[bar]; if(m==4) return B2W[bar]; if(m==5) return B1W[bar]; if(m==6) return B3D[bar]; return B1D[bar]; } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(rates_total<2) return 0; ArraySetAsSeries(close,true); ArraySetAsSeries(time,true); //--- limit = number of new bars to process int limit = rates_total - prev_calculated; if(limit>1) limit = rates_total-1; //--- Copy only the needed range (performance) // needed = limit+1 bars from position startPos int needed = limit + 1; int startPos = rates_total - needed; if(startPos<0) { startPos=0; needed=rates_total; } //--- Temp arrays for MA data double tmp[8][]; for(int m=0;m<8;m++) { ArrayResize(tmp[m],needed); int copied = CopyBuffer(hMA[m],0,startPos,needed,tmp[m]); if(copied0?copied:0);i indicator buffers (series indexed: idx=0=newest) for(int idx=limit; idx>=0; idx--) { double mv[8]; for(int m=0;m<8;m++) { if(idxmv[m]) ? 1 : -1; BSc[idx]=sc; //--- Acceleration double aSum=0; for(int m=0;m<7;m++) aSum+=(vel[m]-vel[m+1]); BAc[idx]=aSum/7.0; //--- Rolling volatility window: reset & rebuild every WINDOW bars // volResetBar tracks the non-series bar index of last reset // Non-series index = rates_total - 1 - idx (since idx is series) int nsIdx = rates_total - 1 - idx; // non-series index (0=oldest) if(nsIdx == volResetBar[0] + WINDOW) { // Time to reset: scan back WINDOW bars to recompute min/max for(int m=0;m<8;m++) { hMin[m]=1e10; hMax[m]=-1e10; } int scanStart = MathMax(0, nsIdx - WINDOW); int scanEnd = MathMin(rates_total-1, nsIdx); for(int s=scanStart; s<=scanEnd; s++) { int si = rates_total - 1 - s; // series index double pp = close[si]; for(int m=0;m<8;m++) { double vm = GetMA(si,m); if(vm>0) { double d = MathAbs(pp - vm); if(dhMax[m]) hMax[m]=d; } } } for(int m=0;m<8;m++) volResetBar[m]=nsIdx; } else { // Normal update: extend min/max if this bar pushes them for(int m=0;m<8;m++) { if(mv[m]>0) { double d = MathAbs(p-mv[m]); if(dhMax[m]) hMax[m]=d; } } } } //--- Dashboard: only on new bar or first run bool newBar = (time[0]!=lastBarTime); if(newBar || prev_calculated==0) { lastBarTime = time[0]; DrawDashboard(); } return rates_total; } //+------------------------------------------------------------------+ void DrawDashboard() { double mv[8], vel[8]; double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); for(int m=0;m<8;m++) { mv[m]=GetMA(0,m); double prv = (GetMA(1,m)>0) ? GetMA(1,m) : mv[m]; vel[m]=mv[m]-prv; } string tf = EnumToString((ENUM_TIMEFRAMES)_Period); int x=XPos, y=YPos, lh=FontSize+4; //--- Title Lbl("T",_Symbol+" PaPP ["+tf+"] v2",x,y,FontSize+2,clrGold,true); y+=lh+4; double c0=GetMA(0,7); // B1D[0] = SMA(1) = latest close double c1=GetMA(1,7); Lbl("P","Oggi: "+DTS(c0,_Digits)+" | Ieri: "+DTS(c1,_Digits),x,y,FontSize,clrWhite,false); y+=lh+2; Lbl("S1","------------------------------",x,y,FontSize-1,clrGold,false); y+=lh; string hdr = StringFormat("%-6s %-12s %-4s %-8s %-5s %-7s","Periodo","Media","Pos","Dist","Vol%","Veloc"); Lbl("H",hdr,x,y,FontSize,clrGold,false); y+=lh; for(int m=0;m<8;m++) { if(mv[m]<=0) continue; double dist = bid-mv[m]; double absD = MathAbs(dist); //--- Volatility % (rolling window) double vn=50.0; if(hMax[m]>hMin[m] && hMax[m]>0) { vn=((absD-hMin[m])/(hMax[m]-hMin[m]))*100.0; vn=MathMax(0,MathMin(100,vn)); } string row = StringFormat("%-6s %-12.*f %-4s %-+8.*f %-5.0f%% %-+7.*f", pN[m],_Digits,mv[m], (dist>0)?"SU":"GIU", _Digits,dist,vn,_Digits,vel[m]); Lbl("R"+(string)m,row,x,y,FontSize,(dist>0)?clrLimeGreen:clrRed,false); y+=lh; } Lbl("S2","------------------------------",x,y,FontSize-1,clrGold,false); y+=lh; //--- Score int sc = (int)BSc[0]; string sS = StringFormat("SCORE: %+d/8",sc); string sD = " [LATERALE]"; if(sc>=5) sD=" [IPERCOMPRATO]"; else if(sc<=-5) sD=" [IPERVENDUTO]"; else if(sc>=2) sD=" [RIALZO]"; else if(sc<=-2) sD=" [RIBASSO]"; Lbl("Sc",sS+sD,x,y,FontSize+1,(sc>=2)?clrLimeGreen:(sc<=-2)?clrRed:clrGray,true); y+=lh+2; //--- Acceleration (threshold based on _Point) double acc = BAc[0]; double accThresh = _Point * 10.0; string aS = StringFormat("ACCEL: %+.*f",_Digits+1,acc); string aD = " [STABILE/FRENATA]"; if(acc>accThresh) aD=" [ACCELERAZIONE +]"; else if(acc<-accThresh) aD=" [ACCELERAZIONE -]"; Lbl("Ac",aS+aD,x,y,FontSize+1,(acc>accThresh)?clrLimeGreen:(acc<-accThresh)?clrRed:clrGray,true); y+=lh; //--- Average volatility double vAvg=0; int vCnt=0; for(int m=0;m<8;m++) { if(mv[m]>0 && hMax[m]>hMin[m] && hMax[m]>0) { double ad=MathAbs(bid-mv[m]); double vn=((ad-hMin[m])/(hMax[m]-hMin[m]))*100.0; vAvg+=MathMax(0,MathMin(100,vn)); vCnt++; } } if(vCnt>0) vAvg/=vCnt; string vS = StringFormat("VOL: %.0f%%",vAvg); string vD = " [NORMALE]"; if(vAvg>70) vD=" [ALTA - breakout in corso]"; else if(vAvg<40) vD=" [BASSA - compressione]"; Lbl("Vo",vS+vD,x,y,FontSize+1,(vAvg>70)?clrRed:(vAvg>40)?clrYellow:clrLimeGreen,true); y+=lh+2; Lbl("TPSL","TP: +"+string(TP_Points)+" pt ("+DTS(bid+TP_Points*_Point,_Digits)+ ") | SL: -"+string(SL_Points)+" pt ("+DTS(bid-SL_Points*_Point,_Digits)+")", x,y,FontSize,clrGray,false); y+=lh+6; //--- TP/SL lines on chart if(ShowTPSL) { string tpName = _pfx+"TPLINE"; string slName = _pfx+"SLLINE"; double tpPrice = bid + TP_Points * _Point; double slPrice = bid - SL_Points * _Point; if(ObjectFind(0,tpName)<0) ObjectCreate(0,tpName,OBJ_HLINE,0,0,0); if(ObjectFind(0,slName)<0) ObjectCreate(0,slName,OBJ_HLINE,0,0,0); ObjectSetDouble(0,tpName,OBJPROP_PRICE,tpPrice); ObjectSetDouble(0,slName,OBJPROP_PRICE,slPrice); ObjectSetInteger(0,tpName,OBJPROP_COLOR,clrLimeGreen); ObjectSetInteger(0,slName,OBJPROP_COLOR,clrRed); ObjectSetInteger(0,tpName,OBJPROP_WIDTH,1); ObjectSetInteger(0,slName,OBJPROP_WIDTH,1); ObjectSetInteger(0,tpName,OBJPROP_STYLE,STYLE_DASHDOT); ObjectSetInteger(0,slName,OBJPROP_STYLE,STYLE_DASHDOT); ObjectSetString(0,tpName,OBJPROP_TEXT,"TP +"+string(TP_Points)+" ("+DTS(tpPrice,_Digits)+")"); ObjectSetString(0,slName,OBJPROP_TEXT,"SL -"+string(SL_Points)+" ("+DTS(slPrice,_Digits)+")"); ObjectSetInteger(0,tpName,OBJPROP_BACK,true); ObjectSetInteger(0,slName,OBJPROP_BACK,true); ObjectSetInteger(0,tpName,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,slName,OBJPROP_SELECTABLE,false); } else { if(ObjectFind(0,_pfx+"TPLINE")>=0) ObjectDelete(0,_pfx+"TPLINE"); if(ObjectFind(0,_pfx+"SLLINE")>=0) ObjectDelete(0,_pfx+"SLLINE"); } Lbl("L0","LEGENDA:",x,y,FontSize,clrGold,true); y+=lh; Lbl("L1","SU/GIU = prezzo sopra/sotto media | Vol% = 0(min)-100(max) rolling "+string(WINDOW),x,y,FontSize-1,clrWhite,false); y+=lh-2; Lbl("L2","Score +8=tutto rialzo -8=tutto ribasso ~0=laterale",x,y,FontSize-1,clrWhite,false); y+=lh-2; Lbl("L3","Accel +=forza in aumento -=forza in calo ~0=stabile/frenata",x,y,FontSize-1,clrWhite,false); y+=lh-2; Lbl("L4","Vol>70%=esplosione Vol<40%=compressione (molla)",x,y,FontSize-1,clrWhite,false); } //+------------------------------------------------------------------+ void Lbl(string n,string t,int x,int y,int fs,color c,bool b) { string o=_pfx+n; if(ObjectFind(0,o)<0) ObjectCreate(0,o,OBJ_LABEL,0,0,0); ObjectSetInteger(0,o,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,o,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,o,OBJPROP_CORNER,CORNER_LEFT_UPPER); ObjectSetString(0,o,OBJPROP_TEXT,t); ObjectSetString(0,o,OBJPROP_FONT,b?"Lucida Console Bold":"Lucida Console"); ObjectSetInteger(0,o,OBJPROP_FONTSIZE,fs); ObjectSetInteger(0,o,OBJPROP_COLOR,c); ObjectSetInteger(0,o,OBJPROP_BACK,false); ObjectSetInteger(0,o,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,o,OBJPROP_HIDDEN,true); } //+------------------------------------------------------------------+ string DTS(double v,int d) { return DoubleToString(v,d); } //+------------------------------------------------------------------+