价差叠加指标解析

2019-05-14

回到程序本身, 这样的叠加图运用了许多 MT4 语言里的冷门功能:

1. 取图表里的最左边 K 棒是采用 WindowFirstVisibleBar(), 而在图表里可视全部的 K 棒是 WindowBarsPerChart(), 但是因为图表有自动向左平移的功能 auto-shift, 所以在最新的图里, 当 iWindowBarsPerChart 大于 iWindowFirstVisibleBar, 要特别作相等的动作, 不然会取不到最新 index = 0 的 K 棒.

2. 主要商品图里的 K 棒 index 不能直接用来取第二商品的 K 棒, 因为两个商品可能因为开市时间不同或有漏 K 棒情形时, 如果单纯以主图 K 棒 index 来对照第二商品的 K 棒 index, 会出现时间不对应的状况.  这时需要先取出当个主图 K 棒的 open 时间 iTime(Symbol(),Period(),i); 在利用这个 open 时间来找到相对应的第二商品 K 棒 iBarShift(SymbolSpread,Period(),dtCurrentBar,true); 这个比较特别的处理.

3. 因为叠加图里的第二个商品换算后的价格可能大于或小于主图的最大或最小价格坐标, 所以程序里把两个商品在当前画面的最大和最小值得到后, 用来强制设定主图新的最大和最小的价格坐标, 这也就是 fix scale 的功能, 不再利用图表默认的 auto scale.

4. 另外指标里标准的 OnCalculate(...) 或是之前 MT4 指标里用的 Start() 是用主图价格变动来驱动指标更新, 当使用者用键盘左右键或PageUp,PageDown键作主图图表移动时, 再价格还没更新前会看到未被刷新的叠加图, 所以需要多利用 MT4 新的 event 处理功能 OnChartEvent(...), 把这几个键盘的 keycodes 放在程序里处理, 只要按下这几个键就强制刷新叠加图, 不用等到价格跳动, 当然价格跳动时也会再刷新一次.

还有一个比较特别的地方, 当使用者在几个"全屏"图表中切换时, 再切回这个叠加图, 其实图表是从"小"图从新切回成全屏图, 因为这个指标已经把价格坐标作为 fix scale, 这样的瞬间切换, 会造成价格坐标还是停留在"小"图的坐标, 需要等到有价格跳动才会从新刷新, 这样会造成视觉上的不美观, 所以在 OnChartEvent(...) 里有个小处理, 当图表大小改变时, 强制刷新, 不用等到有价格跳动.

这些小细节都是在设计这个叠加图指标时发现的, 也更加深入了解 MT4 许多图表的特殊处理方式, 还好 MT4 有提供这些相当冷门的功能可以调用.

5. 叠加的商品价格 K 棒是利用四组 DRAW_HISTOGRAM 各画出 OHCL, 绘制方式是参考 MT4 提供的 Heiken Ashi 指标的写法. 在所有 MT4 默认的指标里也只有这个指标是同一图表上画出另一组 K 棒, 自然是参考这个指标的写法来绘制.  那个写法还可以作到上涨和下跌是不同颜色, 因为这是叠加图, 只是在看第二商品与主商品的关系, 所以第二商品的价格 K 棒并没有作两种颜色处理. 

6. 在最下面红蓝横条变化里, 对于 index = 1 与 index = 2 当颜色不一样时, 也就是两个商品的强弱改变时, 程序里有作一个简单的提醒功能, 可以发出提醒窗口或声音或 email, 各有其对应的 boolean 值可以在输入里设定. 

相对来说, 制作价差子窗口指标就简单多了,只要考虑两种商品在第(2)种状况把相同时间的 K 棒找到作价差计算即可, 使用时指标只需要输入第二种商品名称和价差比 SpreadFactor 两个参数即可, 不需要考虑叠加图的复杂坐标调整或最左价格点的对齐等等, 可以一次性把主要商品的所有 K 棒作完计算, 接下来只要有新价格 K 棒作部分更新即可. 图中淡蓝色线代表价差上升, 红色代表下降.  价差子窗口指标也可以单独使用, 不需要开叠加图, 如果只需要看两个商品的价差变化.

*** SpreadChart.mq4
#property indicator_chart_window
#property indicator_buffers 6
/// keycodes for manual chart movement
#define VK_LEFT 0x25
#define VK_RIGHT 0x27
#define VK_UP 0x26
#define VK_DOWN 0x28
#define VK_PRIOR 0x21
#define VK_NEXT 0x22  
extern string SymbolSpread = "";
extern double SpreadFactor = 1.0;
extern color ColorSpread = Aqua;
extern int IndicatorWidthThick = 2;
extern int IndicatorWidthThin = 1;
extern bool PriceLabel = true;
extern color ColorUp = Blue;
extern color ColorDown = Tomato;
extern bool AlertPopup = false;
extern bool Sound = false;
extern bool EmailAlert = true; 
//--- buffers
double SpreadLowHighBuffer[];
double SpreadHighLowBuffer[];
double SpreadOpenBuffer[];
double SpreadCloseBuffer[];
double SpreadUpBuffer[];
double SpreadDnBuffer[];
int Fontsize = 20;
datetime dtLasttime = 0;
double dChartMax = 0.0, dChartMin = 0.0;
int OnInit()
  {
//--- indicator buffers mapping
   /// color down; lower value; open value
   /// color up; higher value; close value
   ChartSetInteger(0,CHART_SCALEFIX,0);
   IndicatorShortName("SpreadChart");
   IndicatorDigits(Digits);
SetIndexStyle(0,DRAW_HISTOGRAM,0,IndicatorWidthThin,ColorSpread);
   SetIndexBuffer(0,SpreadLowHighBuffer);
SetIndexStyle(1,DRAW_HISTOGRAM,0,IndicatorWidthThin,ColorSpread);
   SetIndexBuffer(1,SpreadHighLowBuffer);
SetIndexStyle(2,DRAW_HISTOGRAM,0,IndicatorWidthThick,ColorSpread);
   SetIndexBuffer(2,SpreadOpenBuffer);
  SetIndexStyle(3,DRAW_HISTOGRAM,0,IndicatorWidthThick,ColorSpread);
   SetIndexBuffer(3,SpreadCloseBuffer);
   SetIndexStyle(4,DRAW_ARROW, EMPTY, 1, ColorUp);
   SetIndexArrow(4,174);
   SetIndexBuffer(4,SpreadUpBuffer);
   SetIndexStyle(5,DRAW_ARROW, EMPTY, 1, ColorDown);
   SetIndexArrow(5,174);
   SetIndexBuffer(5,SpreadDnBuffer);
   return(INIT_SUCCEEDED);
  }
void OnDeinit(const int reason)
{
   ObjectDelete("QuoteMain");
   ObjectDelete("SymbolMain");
   ObjectDelete("QuoteSpread");
   ObjectDelete("SymbolSpread");
   ObjectDelete("Time");
   ObjectDelete("SpreadLabel");
   ObjectDelete("SpreadValue");
}  
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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 (PriceLabel) DisplaySpotPrice();   
      if ( SymbolSpread=="" || iClose(SymbolSpread,Period(),0)==0 ) return(0);
      DrawSpreadChart();   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // Event identifier  
                  const long& lparam,   // Event parameter of long type
                  const double& dparam, // Event parameter of double type
                  const string& sparam) // Event parameter of string type
{
   if(id==CHARTEVENT_KEYDOWN)
     {
      switch(int(lparam))
        {
         case VK_LEFT: D
         case VK_RIGHT: 
         case VK_PRIOR: 
         case VK_NEXT:  
         case VK_UP: 
         case VK_DOWN:  DrawSpreadChart(); break;
         default:          break;
        }
     }
     if (id==CHARTEVENT_CHART_CHANGE && (dChartMax!=WindowPriceMax(0) || dChartMin!=WindowPriceMin(0) ))
            DrawSpreadChart();         
  }

void DrawSpreadChart()
{
      int iWindowFirstVisibleBar = WindowFirstVisibleBar();
      int iWindowBarsPerChart = WindowBarsPerChart();
      if (iWindowBarsPerChart>iWindowFirstVisibleBar) iWindowBarsPerChart = iWindowFirstVisibleBar;
      int i=0, iSymbolSpreadShift=0;
      double dClose, dOpen, dHigh, dLow, dMaxMax=0.0, dMinMin=999999.0;
      double dFixedSpreadFactor=0.0;
      datetime dtCurrentBar=0;
      if (SymbolSpread=="") return;
      ArrayInitialize(SpreadCloseBuffer,0.0);
      ArrayInitialize(SpreadHighLowBuffer,0.0);
      ArrayInitialize(SpreadLowHighBuffer,0.0);
      ArrayInitialize(SpreadOpenBuffer,0.0);
      ArrayInitialize(SpreadUpBuffer,0.0);
      ArrayInitialize(SpreadDnBuffer,0.0);
      for (i=iWindowFirstVisibleBar;i>=iWindowFirstVisibleBar-iWindowBarsPerChart;i--)
      {
      dtCurrentBar = iTime(Symbol(),Period(),i);
      iSymbolSpreadShift = iBarShift(SymbolSpread,Period(),dtCurrentBar,true);
      if (iSymbolSpreadShift!=-1)
      {
         dClose = iClose(SymbolSpread,Period(),iSymbolSpreadShift);
         dHigh = iHigh(SymbolSpread,Period(),iSymbolSpreadShift);
         dLow = iLow(SymbolSpread,Period(),iSymbolSpreadShift);
         dOpen = iOpen(SymbolSpread,Period(),iSymbolSpreadShift);
         if ((i==iWindowFirstVisibleBar || dFixedSpreadFactor == 0.0) && dClose!=0.0 )
         {
            dFixedSpreadFactor = iClose(Symbol(),Period(),i) / dClose;
         }
         if (iHigh(Symbol(),Period(),i)>dMaxMax) dMaxMax = iHigh(Symbol(),Period(),i);
         if (iLow(Symbol(),Period(),i)
         dLow *= dFixedSpreadFactor;
         dHigh *= dFixedSpreadFactor;
         dClose *= dFixedSpreadFactor;
         dOpen *=  dFixedSpreadFactor;
         SpreadLowHighBuffer[i] = dLow;
         SpreadHighLowBuffer[i] = dHigh;
         SpreadCloseBuffer[i] = dClose;
         SpreadOpenBuffer[i] = dOpen;
         if (dHigh>dMaxMax) dMaxMax = dHigh;
         if (dLow
      }}
            double dScale = (dMaxMax-dMinMin)/dMinMin;
            dChartMax = dMaxMax*(1+dScale*0.05);
            dChartMin = dMinMin*(1-dScale*0.05);
            ChartSetInteger(0,CHART_SCALEFIX,true);
            ChartSetDouble(0,CHART_FIXED_MAX,dChartMax);
            ChartSetDouble(0,CHART_FIXED_MIN,dChartMin);
          for (i=iWindowFirstVisibleBar;i>=iWindowFirstVisibleBar-iWindowBarsPerChart;i--)
         {  
            if (SpreadCloseBuffer[i]>=iClose(Symbol(),Period(),i)) 
               SpreadUpBuffer[i] = dChartMin;
            if (SpreadCloseBuffer[i]
               SpreadDnBuffer[i] = dChartMin;     
         }         
            if ( (SpreadUpBuffer[1]!=0 || SpreadDnBuffer[1]!=0) &&
                  ( SpreadUpBuffer[1]!=SpreadUpBuffer[2] && SpreadDnBuffer[1]!=SpreadDnBuffer[2] ) 
                  && dtLasttime!=Time[0])
            {
               string strSpreadAlert = "";
               if (SpreadUpBuffer[1]>0.0) strSpreadAlert = ">";
               if (SpreadDnBuffer[1]>0.0) strSpreadAlert = "<";
               if (AlertPopup)
                  Alert("Spread Alert - ",PeriodToString(Period())," ",SymbolSpread," ",strSpreadAlert," ",Symbol());
               if (Sound)
                  PlaySound("alert.wav");
               if (EmailAlert) 
                  SendMail("Spread Alert - " +PeriodToString(Period())+" "+SymbolSpread+" "+strSpreadAlert+" "+Symbol(),
                  SymbolSpread + ": " + DoubleToStr(iClose(SymbolSpread,Period(),1),MarketInfo(SymbolSpread,MODE_DIGITS))+
                  "n"+Symbol()+": "+DoubleToStr(iClose(Symbol(),Period(),1),Digits)+
                  "nServer Time: " + TimeToStr(TimeCurrent(),TIME_DATE|TIME_SECONDS));
               dtLasttime = Time[0];
            }
      ChartRedraw();
}
string PeriodToString (int imin)
{
   string strprd;
   switch (imin)
   {
   case (1):  
   strprd="M1";
   break;
   case (2):  
   strprd="M2"; 
   break;
   case (3):  
   strprd="M3"; 
   break;
   case (5):  
   strprd="M5"; 
   break;
   case (15):  
   strprd="M15"; 
   break;
   case (30):  
   strprd="M30"; 
   break;
   case (60):  
   strprd="H1"; 
   break;
   case (60*4):  
   strprd="H4"; 
   break;
   case (60*24):  
   strprd="D1"; 
   break;
   case (60*24*7):  
   strprd="W1"; 
   break;
   }
   return (strprd);
}
void SetLabel(string nm,string tx,int xd,int yd,string fn,int fs,color ct)
{
   if(ObjectFind(nm)<0)
      ObjectCreate(nm,OBJ_LABEL,0,0,0);  //--- create the Label object
   ObjectSet(nm,OBJPROP_STYLE,STYLE_SOLID);                
   ObjectSet(nm,OBJPROP_XDISTANCE,xd);                 
   ObjectSet(nm,OBJPROP_YDISTANCE,yd);                    
   ObjectSet(nm,OBJPROP_COLOR,ct);               
   ObjectSetText(nm,tx,fs,fn,ct);                    
}
void DisplaySpotPrice()
{
   int x=50;
   int y=50;
   int fz=Fontsize;
   //color cr;
   double dSymbolPrice=0.0, dSymbolSpreadPrice;
   string strTime="";
   string strpd = PeriodToString(Period());
   strTime = TimeToStr(TimeCurrent(), TIME_DATE|TIME_MINUTES|TIME_SECONDS);
   dSymbolPrice = Bid;
   int iSymbolLen = StringLen(Symbol());
   if (iSymbolLen
   SetLabel("Time",strpd+"  "+strTime, x,y,"Arial",fz-5,White);
   SetLabel("SymbolMain",Symbol(), x,y+(Fontsize+2)*1.2,"Arial",fz,White);
SetLabel("QuoteMain",DoubleToStr(dSymbolPrice,Digits),x+iSymbolLen*fz,y+(fz+2)*1.2,"Arial Bold",fz,Lime);
   if (SymbolSpread!="")
   {
      SetLabel("SymbolSpread",SymbolSpread,x,y+(fz+2)*1.2*2,"Arial",fz,White);
      dSymbolSpreadPrice = iClose(SymbolSpread,Period(),0);
SetLabel("QuoteSpread",DoubleToStr(dSymbolSpreadPrice,MarketInfo(SymbolSpread,MODE_DIGITS)),x+iSymbolLen*fz,y+(fz+2)*1.2*2,"Arial Bold",fz,ColorSpread);
      if (SpreadFactor!=0.0){
      SetLabel("SpreadLabel","Spread",x,y+(fz+3)*1.2*3,"Arial",fz*0.9,White);
      SetLabel("SpreadValue",DoubleToStr(dSymbolPrice-dSymbolSpreadPrice*SpreadFactor,Digits),x+iSymbolLen*fz,y+(fz+3)*1.2*3,"Arial Bold",fz*0.9,MintCream);}
    }
}
**** SpreadChartSub.mq4
//---- indicator settings
#property  indicator_separate_window
#property  indicator_buffers 2
//---- indicator parameters
extern string SymbolSpread = "";
extern double SpreadFactor = 1.0;
extern color ColorUp=Aqua;
extern color ColorDown=Tomato; 
//---- indicator buffers
double     BufferUp[];
double     BufferDown[];
int OnInit()
{
   SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,2,ColorUp);
   SetIndexBuffer(0,BufferUp);
   SetIndexStyle(1,DRAW_LINE,STYLE_SOLID,2,ColorDown);
   SetIndexBuffer(1,BufferDown);
   IndicatorDigits(Digits+1);
   return (0);
}
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 (SymbolSpread=="" || SpreadFactor==0.0) return(0);
      int limit;
      int counted_bars=IndicatorCounted();
      //---- last counted bar will be recounted
      if(counted_bars>0) counted_bars--;
      limit=Bars-counted_bars;
      int i=0, iSymbolSpreadShift = 0;
      double dSpreadValue=0.0, dLastSpreadValue=0.0;
      double dSymbolPrice = 0.0, dSymbolSpreadPrice = 0.0;
      datetime dtSymbolBar = 0;
       for (i=limit-1;i>=0;i--)
      {
         dtSymbolBar = iTime(Symbol(),Period(),i);
         iSymbolSpreadShift = iBarShift(SymbolSpread,Period(),dtSymbolBar,true);
         if (iSymbolSpreadShift!=-1)
         {
            dSymbolPrice = iClose(Symbol(),Period(),i);
            dSymbolSpreadPrice = iClose(SymbolSpread,Period(),iSymbolSpreadShift);
            dSpreadValue = dSymbolPrice - dSymbolSpreadPrice * SpreadFactor;         
            if (dSpreadValue*dLastSpreadValue!=0.0 && dSpreadValue>=dLastSpreadValue)
                          {  BufferUp[i] = dSpreadValue; if (dLastSpreadValue!=0.0) BufferUp[i+1] = dLastSpreadValue; } 
            if (dSpreadValue*dLastSpreadValue!=0.0 && dSpreadValue
                          {  BufferDown[i] = dSpreadValue; if (dLastSpreadValue!=0.0) BufferDown[i+1] = dLastSpreadValue; }                           
         }         
         dLastSpreadValue = dSpreadValue;    
      }  
      return(rates_total);
  }


微信公众号:天泓评测


本博客所有文章如无特别注明均为原创。作者:天泓评测
分享到:更多

相关推荐

发表评论

路人甲 表情
Ctrl+Enter快速提交

网友评论(0)