欢迎访问

# 机构化平衡信号 — 设计规范(MT4,优先支持 XAUUSD_2

author emer | 54 人阅读 | 0 人评论 |

// InstitutionalBalanceSignal.mq4
// Institutional-style MT4 indicator (XAUUSD-first) using value-area logic without traditional indicators.
// - Data: O/H/L/C, tick volume, spread, time only
// - Non-repainting: stamp signals on bar close (index 1)
// - Pre-warn on current bar without stamping
// - Multi-timeframe gate (default M15)
// - Event blackout list

property indicator_chart_window

property indicator_buffers 8

property indicator_plots 8

property indicator_label1 "LongSignal"

property indicator_type1 DRAW_ARROW

property indicator_color1 clrLime

property indicator_width1 1

property indicator_label2 "ShortSignal"

property indicator_type2 DRAW_ARROW

property indicator_color2 clrTomato

property indicator_width2 1

property indicator_label3 "VAH_fast"

property indicator_type3 DRAW_LINE

property indicator_color3 clrDeepSkyBlue

property indicator_style3 STYLE_DOT

property indicator_label4 "VAL_fast"

property indicator_type4 DRAW_LINE

property indicator_color4 clrDeepSkyBlue

property indicator_style4 STYLE_DOT

property indicator_label5 "POC_fast"

property indicator_type5 DRAW_LINE

property indicator_color5 clrOrange

property indicator_style5 STYLE_DASH

property indicator_label6 "VAH_slow"

property indicator_type6 DRAW_LINE

property indicator_color6 clrSteelBlue

property indicator_style6 STYLE_DASHDOT

property indicator_label7 "VAL_slow"

property indicator_type7 DRAW_LINE

property indicator_color7 clrSteelBlue

property indicator_style7 STYLE_DASHDOT

property indicator_label8 "POC_slow"

property indicator_type8 DRAW_LINE

property indicator_color8 clrSandyBrown

property indicator_style8 STYLE_DASH

extern int LookbackMinutes = 480;
extern double ValueAreaPercent = 70.0;

extern bool AutoBin = true;
extern double BinSpreadFactor = 0.5;
extern double BinRealizedFactor = 0.15;
extern double MinBinUSD = 0.20;

extern bool AutoBuffer = true;
extern double BufferSpreadFactor = 1.2;
extern double MinBufferUSD = 0.10;

extern int ConfirmBars = 5;
extern double BreakoutSpeedZ = 1.4;
extern double ReversionSpeedZ = 0.8;
extern double ImbalanceThreshold = 0.20;
extern double SpreadWidenFactor_Breakout = 1.8;
extern double SpreadNormalize_Reversion = 1.3;

extern int SessionStartHour = 7;
extern int SessionEndHour = 22;
extern int NoTradeMinutesAroundHour = 1;
extern string EventBlackoutCsv = ""; // yyyy.mm.dd HH:MM|M;...

extern bool UseMTFFilter = true;
extern int SlowTF = PERIOD_M15;

extern double ExtremeZFactor = 3.0;
extern double ExtremeSpreadFactor = 3.0;
extern double MinVAWidthUSD = 1.0;
extern double MinVAWidthSpreadMult = 15.0;
extern int CooldownBars = 10;

extern bool EnableBreakout = true;
extern bool EnableReversion = true;
extern bool EnablePrewarn = true;

double BufLong[];
double BufShort[];
double VAH_fast[];
double VAL_fast[];
double POC_fast[];
double VAH_slow[];
double VAL_slow[];
double POC_slow[];

datetime lastLong=0, lastShort=0;

int PeriodSecondsTF(int tf){
switch(tf){
case PERIOD_M1: return 60;
case PERIOD_M5: return 300;
case PERIOD_M15: return 900;
case PERIOD_M30: return 1800;
case PERIOD_H1: return 3600;
case PERIOD_H4: return 14400;
case PERIOD_D1: return 86400;
default: return PeriodSeconds();
}
}

double Clamp(double v,double lo,double hi){
if(v<lo) return lo;
if(v>hi) return hi;
return v;
}

double Median(double &arr[], int n){
if(n<=0) return 0.0;
double tmp[];
ArrayResize(tmp,n);
for(int i=0;i<n;i++) tmp[i]=arr[i];
ArraySort(tmp,WHOLE_ARRAY,0,MODE_ASCEND);
if((n%2)==1) return tmp[n/2];
return 0.5*(tmp[n/2-1]+tmp[n/2]);
}

double MedianOfIntArr(const int &arr[], int start, int count){
if(count<=0) return 0.0;
double tmp[];
ArrayResize(tmp,count);
int k=0;
int end=start+count-1;
if(end>=Bars) end=Bars-1;
for(int i=start;i<=end;i++) tmp[k++]=(double)arr[i];
ArraySort(tmp,WHOLE_ARRAY,0,MODE_ASCEND);
if((k%2)==1) return tmp[k/2];
return 0.5*(tmp[k/2-1]+tmp[k/2]);
}

double MADCloseDiff(const double &close[], int start, int count){
int end=start+count-1;
if(end>=Bars-1) end=Bars-2;
int n=end-start+1;
if(n<3) return 0.0;
double d[];
ArrayResize(d,n-1);
int k=0;
for(int i=start;i<start+n-1;i++) d[k++]=MathAbs(close[i]-close[i+1]);
double med=Median(d,k);
if(med<=0) return 0.0;
for(int j=0;j<k;j++) d[j]=MathAbs(d[j]-med);
double mad=Median(d,k);
return (mad>0? mad : med);
}

double TickImbalance(const double &open[], const double &close[], const long &tvol[], int start, int len){
int end=start+len-1;
if(end>=Bars) end=Bars-1;
long b=0,s=0;
for(int i=start;i<=end;i++){
if(close[i]>open[i]) b+=tvol[i];
else if(close[i]<open[i]) s+=tvol[i];
else {
b+=tvol[i]/2;
s+=tvol[i]/2;
}
}
double tot=(double)(b+s);
if(tot<=0) return 0.0;
return (double)(b-s)/tot;
}

bool InSession(datetime t){
int hr=TimeHour(t), mn=TimeMinute(t);
bool in=(hr>=SessionStartHour && hr<=SessionEndHour);
bool near=(mn<=NoTradeMinutesAroundHour || mn>=60-NoTradeMinutesAroundHour);
if(!in) return false;
if(NoTradeMinutesAroundHour>0 && near) return false;
return true;
}

bool InEventBlackout(datetime t){
if(StringLen(EventBlackoutCsv)<=0) return false;
string segs[];
int parts=StringSplit(EventBlackoutCsv,';',segs);
if(parts<=0) return false;
for(int i=0;i<parts;i++){
string seg=Trim(segs[i]);
if(StringLen(seg)==0) continue;
int p=StringFind(seg,"|");
if(p<=0) continue;
string ts=StringSubstr(seg,0,p);
string ms=StringSubstr(seg,p+1);
datetime c=StringToTime(ts);
int rad=(int)StringToInteger(ms);
if(c==0 || rad<=0) continue;
if(MathAbs((int)(t-c))<=rad*60) return true;
}
return false;
}

bool BuildValueAreaFast(const double &high[], const double &low[], const long &tvol[], int startIndex, int barsN,
double pct, double binUsd, double &VAH, double &VAL, double &POC){
if(barsN<10) return false;
int start=startIndex;
int end=startIndex+barsN-1;
if(end>=Bars) end=Bars-1;
double lo=1e100, hi=-1e100;
for(int i=start;i<=end;i++){
if(low[i]<lo) lo=low[i];
if(high[i]>hi) hi=high[i];
}
if(hi<=lo) return false;
int bins=(int)MathCeil((hi-lo)/binUsd);
bins=MathMax(10,MathMin(bins,2000));
double hist[];
ArrayResize(hist,bins);
ArrayInitialize(hist,0.0);
for(int j=start;j<=end;j++){
double px=0.5(high[j]+low[j]);
int binIdx=(int)MathFloor((px-lo)/binUsd);
binIdx=MathMax(0,MathMin(binIdx,bins-1));
double w=(double)tvol[j];
if(w<=0) w=1.0;
hist[binIdx]+=w;
}
double tot=0.0;
int poc=0;
double pocMass=-1.0;
for(int bin=0;bin<bins;bin++){
tot+=hist[bin];
if(hist[bin]>pocMass){
pocMass=hist[bin];
poc=bin;
}
}
if(tot<=0.0) return false;
double target=tot
Clamp(pct/100.0,0.10,0.95);
int L=poc,R=poc;
double acc=hist[poc];
while(acc<target && (L>0 || R<bins-1)){
double LM=(L>0)?hist[L-1]:-1.0, RM=(R<bins-1)?hist[R+1]:-1.0;
if(RM>LM){
if(R<bins-1){
R++;
acc+=hist[R];
} else if(L>0){
L--;
acc+=hist[L];
} else break;
}
else{
if(L>0){
L--;
acc+=hist[L];
} else if(R<bins-1){
R++;
acc+=hist[R];
} else break;
}
}
VAL=lo+LbinUsd;
VAH=lo+(R+1)
binUsd;
POC=lo+(poc+0.5)*binUsd;
return true;
}

bool BuildValueAreaSlow(int minutes, int tf, double pct, double binUsd, double &VAH, double &VAL, double &POC){
int sec=PeriodSecondsTF(tf);
int need=MathMax(10, minutes60/sec);
int total=iBars(Symbol(),tf);
if(total<=need+1) return false;
int start=1;
int end=start+need-1;
double lo=1e100, hi=-1e100;
for(int i=start;i<=end;i++){
double l=iLow(Symbol(),tf,i), h=iHigh(Symbol(),tf,i);
if(l<lo) lo=l;
if(h>hi) hi=h;
}
if(hi<=lo) return false;
int bins=(int)MathCeil((hi-lo)/binUsd);
bins=MathMax(10,MathMin(bins,2000));
double hist[];
ArrayResize(hist,bins);
ArrayInitialize(hist,0.0);
for(int j=start;j<=end;j++){
double px=0.5
(iHigh(Symbol(),tf,j)+iLow(Symbol(),tf,j));
int binIdx=(int)MathFloor((px-lo)/binUsd);
binIdx=MathMax(0,MathMin(binIdx,bins-1));
double w=(double)iVolume(Symbol(),tf,j);
if(w<=0) w=1.0;
hist[binIdx]+=w;
}
double tot=0.0;
int poc=0;
double pocMass=-1.0;
for(int bin=0;bin<bins;bin++){
tot+=hist[bin];
if(hist[bin]>pocMass){
pocMass=hist[bin];
poc=bin;
}
}
if(tot<=0.0) return false;
double target=totClamp(pct/100.0,0.10,0.95);
int L=poc,R=poc;
double acc=hist[poc];
while(acc<target && (L>0 || R<bins-1)){
double LM=(L>0)?hist[L-1]:-1.0, RM=(R<bins-1)?hist[R+1]:-1.0;
if(RM>LM){
if(R<bins-1){
R++;
acc+=hist[R];
} else if(L>0){
L--;
acc+=hist[L];
} else break;
}
else{
if(L>0){
L--;
acc+=hist[L];
} else if(R<bins-1){
R++;
acc+=hist[R];
} else break;
}
}
VAL=lo+L
binUsd;
VAH=lo+(R+1)binUsd;
POC=lo+(poc+0.5)
binUsd;
return true;
}

string Trim(const string s){
string t=s;
StringTrimLeft(t);
StringTrimRight(t);
return t;
}

int OnInit(){
IndicatorDigits(Digits);
SetIndexBuffer(0,BufLong); SetIndexEmptyValue(0,EMPTY_VALUE); SetIndexArrow(0,233);
SetIndexBuffer(1,BufShort); SetIndexEmptyValue(1,EMPTY_VALUE); SetIndexArrow(1,234);
SetIndexBuffer(2,VAH_fast); SetIndexEmptyValue(2,EMPTY_VALUE);
SetIndexBuffer(3,VAL_fast); SetIndexEmptyValue(3,EMPTY_VALUE);
SetIndexBuffer(4,POC_fast); SetIndexEmptyValue(4,EMPTY_VALUE);
SetIndexBuffer(5,VAH_slow); SetIndexEmptyValue(5,EMPTY_VALUE);
SetIndexBuffer(6,VAL_slow); SetIndexEmptyValue(6,EMPTY_VALUE);
SetIndexBuffer(7,POC_slow); SetIndexEmptyValue(7,EMPTY_VALUE);
ArraySetAsSeries(BufLong,true); ArraySetAsSeries(BufShort,true);
ArraySetAsSeries(VAH_fast,true); ArraySetAsSeries(VAL_fast,true); ArraySetAsSeries(POC_fast,true);
ArraySetAsSeries(VAH_slow,true); ArraySetAsSeries(VAL_slow,true); ArraySetAsSeries(POC_slow,true);
// Initialize buffers with EMPTY_VALUE to avoid spurious drawings at 0.0
int total = Bars;
ArrayInitialize(BufLong, EMPTY_VALUE);
ArrayInitialize(BufShort, EMPTY_VALUE);
ArrayInitialize(VAH_fast, EMPTY_VALUE);
ArrayInitialize(VAL_fast, EMPTY_VALUE);
ArrayInitialize(POC_fast, EMPTY_VALUE);
ArrayInitialize(VAH_slow, EMPTY_VALUE);
ArrayInitialize(VAL_slow, EMPTY_VALUE);
ArrayInitialize(POC_slow, EMPTY_VALUE);

return(INIT_SUCCEEDED);
}

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<200) return 0;

int sec=PeriodSeconds();
int need=MathMax(10, LookbackMinutes*60/sec);
if(need>rates_total-2) need=rates_total-2;
double spArr[];
ArrayResize(spArr, MathMin(200, rates_total-2));
int k=0;
for(int i=1;i<1+ArraySize(spArr);i++) spArr[k++]=(double)spread[i];
double medSpread=Median(spArr,k);
if(medSpread<=0) medSpread=(double)spread[1];

double trArr[];
int trN=MathMin(120, rates_total-1);
ArrayResize(trArr,trN);
k=0;
for(int t=1;t<1+trN;t++) trArr[k++]=high[t]-low[t];
double medTR=Median(trArr,k);
if(medTR<=0) medTR=high[1]-low[1];

double binUsd=(AutoBin? MathMax(MathMax(BinSpreadFactormedSpreadPoint, BinRealizedFactormedTR), MinBinUSD) : MinBinUSD);
double bufferUsd=(AutoBuffer? MathMax(BufferSpreadFactor
medSpread*Point, MinBufferUSD) : MinBufferUSD);

double fVAH=0,fVAL=0,fPOC=0;
bool okFast=BuildValueAreaFast(high,low,tick_volume,1,need,ValueAreaPercent,binUsd,fVAH,fVAL,fPOC);
if(okFast){
VAH_fast[1]=fVAH;
VAL_fast[1]=fVAL;
POC_fast[1]=fPOC;
} else {
VAH_fast[1]=EMPTY_VALUE;
VAL_fast[1]=EMPTY_VALUE;
POC_fast[1]=EMPTY_VALUE;
}

double sVAH=0,sVAL=0,sPOC=0;
bool okSlow=false;
if(UseMTFFilter) okSlow=BuildValueAreaSlow(LookbackMinutes,SlowTF,ValueAreaPercent,binUsd,sVAH,sVAL,sPOC);
if(okSlow){
VAH_slow[1]=sVAH;
VAL_slow[1]=sVAL;
POC_slow[1]=sPOC;
} else {
VAH_slow[1]=EMPTY_VALUE;
VAL_slow[1]=EMPTY_VALUE;
POC_slow[1]=EMPTY_VALUE;
}

BufLong[1]=EMPTY_VALUE;
BufShort[1]=EMPTY_VALUE;
bool inSess=InSession(time[1]);
bool inBlk=InEventBlackout(time[1]);
if(!inSess || inBlk) return rates_total;

double mad=MADCloseDiff(close,1,MathMin(200,rates_total-1));
if(mad<=0) mad=2Point;
int back=MathMin(ConfirmBars,rates_total-2);
double z=(close[1]-close[1+back])/mad;
bool extreme=(MathAbs(z)>=ExtremeZFactor) || ((double)spread[1] >= medSpread
ExtremeSpreadFactor);
if(!okFast || extreme) return rates_total;
double fWidth=fVAH-fVAL;
bool tooNarrow=(fWidth < MathMax(MinVAWidthUSD, MinVAWidthSpreadMultmedSpreadPoint));
if(tooNarrow) return rates_total;

double imb=TickImbalance(open,close,tick_volume,1,MathMax(3,ConfirmBars));
bool mtfLong=true, mtfShort=true;
if(UseMTFFilter && okSlow){
mtfLong=(close[1]>=sPOC);
mtfShort=(close[1]<=sPOC);
}
bool cdLong=(Time[1]-lastLong >= CooldownBarssec);
bool cdShort=(Time[1]-lastShort >= CooldownBars
sec);

if(EnableBreakout){
bool spWide=((double)spread[1] >= medSpreadSpreadWidenFactor_Breakout);
if(close[1] > fVAH + bufferUsd && z>BreakoutSpeedZ && imb>ImbalanceThreshold && spWide && mtfLong && cdLong){
BufLong[1]=low[1] - 0.2
(high[1]-low[1]);
lastLong=Time[1];
}
if(close[1] < fVAL - bufferUsd && z<-BreakoutSpeedZ && imb<-ImbalanceThreshold && spWide && mtfShort && cdShort){
BufShort[1]=high[1] + 0.2*(high[1]-low[1]);
lastShort=Time[1];
}
}

if(EnableReversion){
bool spNorm=((double)spread[1] <= medSpreadSpreadNormalize_Reversion);
bool wasAbove=(close[2]>fVAH), backInFromAbove=(close[1]<fVAH && close[1]>fVAL);
bool wasBelow=(close[2]<fVAL), backInFromBelow=(close[1]>fVAL && close[1]<fVAH);
if(wasAbove && backInFromAbove && z<-ReversionSpeedZ && imb<=0.0 && spNorm && mtfShort && cdShort){
BufShort[1]=high[1] + 0.2
(high[1]-low[1]);
lastShort=Time[1];
}
if(wasBelow && backInFromBelow && z> ReversionSpeedZ && imb>=0.0 && spNorm && mtfLong && cdLong){
BufLong[1]=low[1] - 0.2*(high[1]-low[1]);
lastLong=Time[1];
}
}

if(EnablePrewarn && okFast){
string warn="";
double z0=(close[0]-close[MathMin(ConfirmBars, rates_total-2)])/mad;
double imb0=TickImbalance(open,close,tick_volume,0,MathMax(3,ConfirmBars));
bool spWide0=((double)spread[0] >= medSpreadSpreadWidenFactor_Breakout);
bool up=(close[0] > fVAH + 0.8
bufferUsd);
bool dn=(close[0] < fVAL - 0.8bufferUsd);
if(up && z0>0.8
BreakoutSpeedZ && imb0>0.8ImbalanceThreshold && spWide0 && (!UseMTFFilter || (okSlow && close[0]>=sPOC)) && InSession(time[0]) && !InEventBlackout(time[0]))
warn="PREWARN: Breakout LONG";
if(dn && z0<-0.8
BreakoutSpeedZ && imb0<-0.8*ImbalanceThreshold && spWide0 && (!UseMTFFilter || (okSlow && close[0]<=sPOC)) && InSession(time[0]) && !InEventBlackout(time[0]))
warn=(StringLen(warn)>0? warn+" | ":"")+"PREWARN: Breakout SHORT";
string obj="IBS_PREWARN";
if(StringLen(warn)>0){
if(ObjectFind(0,obj) < 0){
ObjectCreate(0,obj,OBJ_LABEL,0,0,0);
ObjectSetInteger(0,obj,OBJPROP_CORNER,CORNER_LEFT_UPPER);
ObjectSetInteger(0,obj,OBJPROP_XDISTANCE,8);
ObjectSetInteger(0,obj,OBJPROP_YDISTANCE,8);
ObjectSetInteger(0,obj,OBJPROP_COLOR,clrGoldenrod);
ObjectSetInteger(0,obj,OBJPROP_FONTSIZE,10);
}
ObjectSetString(0,obj,OBJPROP_TEXT,warn);
} else {
if(ObjectFind(0,obj) >= 0) ObjectDelete(0,obj);
}
}

return rates_total;
}

还没有人打赏,支持一下