//+------------------------------------------------------------------+ //| PaPP_Median.mq5 | //| PaPP v2 | //+------------------------------------------------------------------+ #property copyright "PaPP v2" #property version "2.00" #property description "PaPP Median - Mediana 7 MA (3g-1y)" #property description "Calcolo ancorato a D1 = linea uguale su ogni timeframe" #property indicator_chart_window #property indicator_buffers 8 #property indicator_plots 8 input int FontSize = 9; input bool Smooth = true; // interpola tra i valori D1 -> linea morbida input bool ShowMA = true; // mostra anche le 7 MA (oltre alla mediana) input bool ShowPanel = true; // box con sfondo dietro al pannello info input color PanelBg = C'20,20,25'; // colore sfondo del box #define ANCHOR_TF PERIOD_D1 // calcolo ancorato a D1: linea identica su ogni TF #define KSLOPE 5 // barre D1 per velocita'/accelerazione #define NVOL 14 // barre D1 per la volatilita' #define CLWIN 252 // finestra D1 (~1 anno) per il percentile del cluster double Buff_Median[]; double B0[],B1[],B2[],B3[],B4[],B5[],B6[]; int gDays[7] = {365,182,121,30,14,7,3}; color gCol[7] = {clrDodgerBlue,clrDeepSkyBlue,clrTurquoise,clrLimeGreen,clrOrange,clrTomato,clrRed}; int hMA[7]; int bars[7]; struct PBuf { double v[]; }; // buffer dinamico per copiare una MA string _pfx = "PM_"; // oggetti pannello string _pfx2 = "PME_"; // etichette a fine linea //+------------------------------------------------------------------+ // scrive il valore nel buffer della MA m void SetBuf(int m,int idx,double v) { switch(m) { case 0: B0[idx]=v; break; case 1: B1[idx]=v; break; case 2: B2[idx]=v; break; case 3: B3[idx]=v; break; case 4: B4[idx]=v; break; case 5: B5[idx]=v; break; case 6: B6[idx]=v; break; } } //+------------------------------------------------------------------+ // legge il valore corrente (barra 0) del buffer della MA m double GetBuf(int m) { switch(m) { case 0: return B0[0]; case 1: return B1[0]; case 2: return B2[0]; case 3: return B3[0]; case 4: return B4[0]; case 5: return B5[0]; case 6: return B6[0]; } return 0; } //+------------------------------------------------------------------+ int TimeToBars(int d) { datetime n = TimeCurrent(); if(n==0) { long s = (long)d*86400L, p = PeriodSeconds(ANCHOR_TF); return (int)MathMax(1,s/p); } return MathMax(1,Bars(_Symbol,ANCHOR_TF,n-d*86400,n)); } //+------------------------------------------------------------------+ int OnInit() { for(int i=0;i<7;i++) { bars[i] = TimeToBars(gDays[i]); hMA[i] = iMA(_Symbol,ANCHOR_TF,bars[i],0,MODE_SMA,PRICE_CLOSE); if(hMA[i]==INVALID_HANDLE) return INIT_FAILED; } SetIndexBuffer(0,Buff_Median,INDICATOR_DATA); SetIndexBuffer(1,B0,INDICATOR_DATA); SetIndexBuffer(2,B1,INDICATOR_DATA); SetIndexBuffer(3,B2,INDICATOR_DATA); SetIndexBuffer(4,B3,INDICATOR_DATA); SetIndexBuffer(5,B4,INDICATOR_DATA); SetIndexBuffer(6,B5,INDICATOR_DATA); SetIndexBuffer(7,B6,INDICATOR_DATA); ArraySetAsSeries(Buff_Median,true); ArraySetAsSeries(B0,true); ArraySetAsSeries(B1,true); ArraySetAsSeries(B2,true); ArraySetAsSeries(B3,true); ArraySetAsSeries(B4,true); ArraySetAsSeries(B5,true); ArraySetAsSeries(B6,true); //--- plot 0 = mediana (linea principale) PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_LINE); PlotIndexSetInteger(0,PLOT_LINE_COLOR,clrGold); PlotIndexSetInteger(0,PLOT_LINE_WIDTH,2); PlotIndexSetString(0,PLOT_LABEL,"PaPP Median"); PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0); //--- plot 1..7 = le 7 MA for(int p=0;p<7;p++) { PlotIndexSetInteger(p+1,PLOT_DRAW_TYPE,ShowMA?DRAW_LINE:DRAW_NONE); PlotIndexSetInteger(p+1,PLOT_LINE_COLOR,gCol[p]); PlotIndexSetInteger(p+1,PLOT_LINE_WIDTH,1); PlotIndexSetString(p+1,PLOT_LABEL,"MA "+IntegerToString(gDays[p])+"g"); PlotIndexSetDouble(p+1,PLOT_EMPTY_VALUE,0.0); } IndicatorSetString(INDICATOR_SHORTNAME,"PaPP Median"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { for(int i=0;i<7;i++) if(hMA[i]!=INVALID_HANDLE) IndicatorRelease(hMA[i]); ObjectsDeleteAll(0,_pfx); ObjectsDeleteAll(0,_pfx2); } //+------------------------------------------------------------------+ // d1shift = indice (serie) della barra D1 da cui leggere la SMA double GetMA(int m,int d1shift) { if(d1shift<0) return 0; double buf[1]; if(CopyBuffer(hMA[m],0,d1shift,1,buf)==1) return buf[0]; return 0; } //+------------------------------------------------------------------+ double Median(double &a[],int n) { if(n<=0) return 0; ArraySort(a); if((n&1)==1) return a[n/2]; return 0.5*(a[n/2-1]+a[n/2]); } //+------------------------------------------------------------------+ // Mediana delle 7 MA su una specifica barra D1 double MedianAtD1(int d1shift) { if(d1shift<0) return 0; double vals[]; int cnt=0; ArrayResize(vals,7); for(int m=0;m<7;m++) { double v=GetMA(m,d1shift); if(v>0) { vals[cnt]=v; cnt++; } } ArrayResize(vals,cnt); return Median(vals,cnt); } //+------------------------------------------------------------------+ // valore valido = positivo e finito (esclude EMPTY_VALUE di warmup) bool IsVal(double v) { return (v>0.0 && v<1.0e12); } //+------------------------------------------------------------------+ // mediana dei primi c valori di src double MedArr(double &src[],int c) { if(c<=0) return 0; double a[]; ArrayResize(a,c); for(int j=0;j=2) { double mn=0; for(int q=0;q0) cA[cc++]=cv; vA[vc++]=MathAbs(VelAtJ(mb,j,KSLOPE)); aA[ac++]=MathAbs(AccAtJ(mb,j,KSLOPE)); double ov=VolAtJ(mb,j,NVOL); if(ov>0) oA[oc++]=ov; } cluCur=ClusterAtJ(mb,1); velCur=VelAtJ(mb,1,KSLOPE); accCur=AccAtJ(mb,1,KSLOPE); volCur=VolAtJ(mb,1,NVOL); cluPct=PctlOf(cA,cc,cluCur); volPct=PctlOf(oA,oc,volCur); velPct=PctlOf(vA,vc,MathAbs(velCur)); // forza (su |valore|) accPct=PctlOf(aA,ac,MathAbs(accCur)); } //+------------------------------------------------------------------+ // descrizione a parole per una grandezza di sola ampiezza (cluster, volatilita') string MagWord(double pct,string w0,string w1,string w2,string w3,string w4) { if(pct<0.20) return w0; if(pct<0.40) return w1; if(pct<0.60) return w2; if(pct<0.80) return w3; return w4; } //+------------------------------------------------------------------+ // descrizione a parole per un segnale con segno (velocita', accelerazione) string DirWord(double val,double absPct,string up,string down,string flat) { if(absPct<0.15) return flat; string forza = (absPct<0.40)?"lieve":((absPct<0.70)?"moderata":"forte"); return (val>0?up:down)+" "+forza; } //+------------------------------------------------------------------+ // descrizione a parole per la distanza prezzo-mediana (con segno) string DistWord(double val,double absPct) { if(absPct<0.15) return "sulla mediana"; string q = (absPct<0.40)?"poco":((absPct<0.70)?"moderatamente":"molto"); return q+(val>0?" sopra":" sotto"); } //+------------------------------------------------------------------+ // mediana delle 7 MA a barra j dai buffer mb double MedMAatJ(PBuf &mb[],int j) { double v[7]; int c=0; for(int m=0;m<7;m++){ double x=mb[m].v[j]; if(IsVal(x)) v[c++]=x; } return MedArr(v,c); } //+------------------------------------------------------------------+ // distanza prezzo-mediana: percentile storico (su |valore|) sugli ultimi 'win' giorni D1 void DistStats(int win,double curDist,double &absPct) { absPct=0.5; int need=win+2; double cl[]; ArraySetAsSeries(cl,true); if(CopyClose(_Symbol,ANCHOR_TF,0,need,cl)!=need) return; PBuf mb[7]; for(int m=0;m<7;m++){ ArraySetAsSeries(mb[m].v,true); if(CopyBuffer(hMA[m],0,0,need,mb[m].v)!=need) return; } double dA[]; int dc=0; ArrayResize(dA,win); for(int j=1;j<=win;j++) { double md=MedMAatJ(mb,j); if(md<=0) continue; dA[dc++]=MathAbs((cl[j]-md)/md*100.0); } absPct=PctlOf(dA,dc,MathAbs(curDist)); } //+------------------------------------------------------------------+ // interpolazione lineare con fallback se uno dei valori manca double Interp(double vStart,double vEnd,double frac) { if(vStart>0 && vEnd>0) return vStart+frac*(vEnd-vStart); if(vEnd>0) return vEnd; return vStart; } //+------------------------------------------------------------------+ // Mediana dei primi n valori di src double MedianN(const double &src[],int n) { if(n<=0) return 0; double a[]; ArrayResize(a,n); for(int i=0;i0) { vv[c]=cols[s][m]; c++; } med_d1[s] = MedianN(vv,c); } //--- range di barre grafico da (ri)calcolare: // tutto al primo load o quando nasce una nuova barra D1, altrimenti solo oggi static int s_lastD1 = -1; int recalcFrom; if(prev_calculated==0 || d1bars!=s_lastD1) recalcFrom = rates_total-1; else { datetime todayOpen = iTime(_Symbol,ANCHOR_TF,0); recalcFrom = 0; while(recalcFrom < rates_total-1 && time[recalcFrom] >= todayOpen) recalcFrom++; } s_lastD1 = d1bars; for(int idx=recalcFrom; idx>=0; idx--) { int d1cur = iBarShift(_Symbol,ANCHOR_TF,time[idx],false); if(d1cur<0 || d1cur>=d1bars) { Buff_Median[idx]=0; for(int m=0;m<7;m++) SetBuf(m,idx,0.0); continue; } int sj=d1cur+1; // barra D1 precedente (inizio giornata) //--- frazione di giornata trascorsa (solo se Smooth) double frac=0.0; if(Smooth) { datetime t0 = iTime(_Symbol,ANCHOR_TF,d1cur); datetime t1 = (d1cur>0) ? iTime(_Symbol,ANCHOR_TF,d1cur-1) : t0 + PeriodSeconds(ANCHOR_TF); frac = (t1>t0) ? (double)(time[idx]-t0)/(double)(t1-t0) : 0.0; frac = MathMax(0.0,MathMin(1.0,frac)); } //--- mediana if(!Smooth) Buff_Median[idx] = (sj0)?clrRed:clrLimeGreen; double distAbsPct; DistStats(CLWIN,distPct,distAbsPct); double cluCur,cluPct,velCur,velPct,accCur,accPct,volCur,volPct; AllMetrics(CLWIN,cluCur,cluPct,velCur,velPct,accCur,accPct,volCur,volPct); string sT = "PaPP Median ["+StringSubstr(EnumToString((ENUM_TIMEFRAMES)_Period),7)+"]"; string sM = "Median: "+DoubleToString(median,_Digits); string sD = StringFormat("Dist: %+.2f%% - %s (P%.0f)",distPct,DistWord(distPct,distAbsPct),distAbsPct*100); string sV = StringFormat("Cluster %.3f%% - %s (P%.0f)",cluCur,MagWord(cluPct,"molto stretto","stretto","normale","largo","molto largo"),cluPct*100); string sVE = StringFormat("Velocita' %+.3f%% - %s",velCur,DirWord(velCur,velPct,"in salita","in discesa","quasi piatta")); string sAC = StringFormat("Accel %+.4f%% - %s",accCur,DirWord(accCur,accPct,"in accelerazione","in decelerazione","stabile")); string sVO = StringFormat("Volatilita' %.4f%% - %s (P%.0f)",volCur,MagWord(volPct,"molto bassa","bassa","normale","alta","molto alta"),volPct*100); string sMA[7]; for(int m=0;m<7;m++) sMA[m]=StringFormat("MA %3dg: %s",gDays[m],DoubleToString(GetMA(m,1),_Digits)); string sH = "Sopra=SELL | Sotto=BUY"; //--- 2) larghezza/altezza del box: misuro la larghezza REALE in pixel (TextGetSize) int maxw=0; maxw=MathMax(maxw,TxtW(sT,FontSize+2,true)); maxw=MathMax(maxw,TxtW(sM,FontSize,false)); maxw=MathMax(maxw,TxtW(sD,FontSize,true)); maxw=MathMax(maxw,TxtW(sV,FontSize,false)); maxw=MathMax(maxw,TxtW(sVE,FontSize,false)); maxw=MathMax(maxw,TxtW(sAC,FontSize,false)); maxw=MathMax(maxw,TxtW(sVO,FontSize,false)); maxw=MathMax(maxw,TxtW(sH,FontSize-1,false)); for(int m=0;m<7;m++) maxw=MathMax(maxw,TxtW(sMA[m],FontSize-1,false)); int boxX=x-6, boxY=y0-6; int w=maxw+(x-boxX)+10; // padding sinistro (x-boxX) + destro 10 int bodyH=15*lh+6; // 7 righe in alto + 7 MA + riga finale + padding PanelBox(boxX,boxY,w,bodyH); //--- 3) scritte (aggiornate in-place: niente ObjectsDeleteAll -> niente lampeggio) int y=y0; Lbl("T",sT,x,y,FontSize+2,clrGold,true); y+=lh+4; Lbl("M",sM,x,y,FontSize,clrGold,false); y+=lh; Lbl("D",sD,x,y,FontSize,distC,true); y+=lh+2; Lbl("V",sV,x,y,FontSize,(cluPct<0.40)?clrLimeGreen:((cluPct>0.60)?clrTomato:clrSilver),false); y+=lh; Lbl("VEL",sVE,x,y,FontSize,(velCur>0)?clrLimeGreen:clrTomato,false); y+=lh; Lbl("ACC",sAC,x,y,FontSize,(accCur>0)?clrLimeGreen:clrTomato,false); y+=lh; Lbl("VOL",sVO,x,y,FontSize,(volPct<0.40)?clrLimeGreen:((volPct>0.60)?clrTomato:clrAqua),false); y+=lh+2; for(int m=0;m<7;m++) { double v=GetMA(m,1); Lbl("a"+IntegerToString(m),sMA[m],x,y,FontSize-1,(v>0)?gCol[m]:clrGray,false); y+=lh-2; } y+=2; Lbl("H",sH,x,y,FontSize-1,clrGray,false); } //+------------------------------------------------------------------+ // etichetta di testo ancorata a (t,price); se price<=0 la rimuove void Tag(string n,datetime t,double price,string txt,color c) { string o=_pfx2+n; if(price<=0) { ObjectDelete(0,o); return; } if(ObjectFind(0,o)<0) ObjectCreate(0,o,OBJ_TEXT,0,0,0); ObjectSetInteger(0,o,OBJPROP_TIME,t); ObjectSetDouble(0,o,OBJPROP_PRICE,price); ObjectSetString(0,o,OBJPROP_TEXT," "+txt); ObjectSetString(0,o,OBJPROP_FONT,"Consolas"); ObjectSetInteger(0,o,OBJPROP_FONTSIZE,FontSize); ObjectSetInteger(0,o,OBJPROP_COLOR,c); ObjectSetInteger(0,o,OBJPROP_ANCHOR,ANCHOR_LEFT); ObjectSetInteger(0,o,OBJPROP_BACK,false); ObjectSetInteger(0,o,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,o,OBJPROP_HIDDEN,true); } //+------------------------------------------------------------------+ // etichette a fine linea, nel margine vuoto a destra void DrawTags(datetime tlast) { datetime tfut = tlast + 2*PeriodSeconds(_Period); Tag("med",tfut,Buff_Median[0],"Mediana",clrGold); for(int m=0;m<7;m++) { double p = ShowMA ? GetBuf(m) : 0.0; Tag(IntegerToString(m),tfut,p,IntegerToString(gDays[m])+"g",gCol[m]); } } //+------------------------------------------------------------------+ 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?"Consolas Bold":"Consolas"); ObjectSetInteger(0,o,OBJPROP_FONTSIZE,fs); ObjectSetInteger(0,o,OBJPROP_COLOR,c); ObjectSetInteger(0,o,OBJPROP_BACK,false); ObjectSetInteger(0,o,OBJPROP_ZORDER,1); // sopra il box di sfondo ObjectSetInteger(0,o,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,o,OBJPROP_HIDDEN,true); } //+------------------------------------------------------------------+ // riquadro di sfondo del pannello (dietro alle scritte) void PanelBox(int px,int py,int w,int h) { string o=_pfx+"BOX"; if(!ShowPanel) { ObjectDelete(0,o); return; } if(ObjectFind(0,o)<0) ObjectCreate(0,o,OBJ_RECTANGLE_LABEL,0,0,0); ObjectSetInteger(0,o,OBJPROP_CORNER,CORNER_LEFT_UPPER); ObjectSetInteger(0,o,OBJPROP_XDISTANCE,px); ObjectSetInteger(0,o,OBJPROP_YDISTANCE,py); ObjectSetInteger(0,o,OBJPROP_XSIZE,w); ObjectSetInteger(0,o,OBJPROP_YSIZE,h); ObjectSetInteger(0,o,OBJPROP_BGCOLOR,PanelBg); ObjectSetInteger(0,o,OBJPROP_BORDER_TYPE,BORDER_FLAT); ObjectSetInteger(0,o,OBJPROP_COLOR,clrDimGray); // bordo ObjectSetInteger(0,o,OBJPROP_BACK,false); ObjectSetInteger(0,o,OBJPROP_ZORDER,0); // dietro alle scritte ObjectSetInteger(0,o,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,o,OBJPROP_HIDDEN,true); } //+------------------------------------------------------------------+