下载MetaTrader 5

无缝图表

15 一月 2016, 11:47
Andrey Khatimlianskii
0
923

1.   动机

在MT4中, 只有当价格发生改变时, 才会画出柱形. 如果在一分钟之内价格都没有变化, 在一分钟图表上就会出现一个柱形空缺.

开发人员是有意选择这种绘制图表的方式的, 因为使用他们产品的大多数交易者都希望图表上只包含实际存在的价格. 然而, 也还是有人更喜欢连续的图表. 他们希望的是, 即使开盘价等于前一柱的收盘价, 还是会画出柱形. 这样, 在任何时间尺度上图表上都不会有空缺, 在1分钟图表上的100个柱永远对应着100分钟. 而在现在的实现中, 这些数据可能是不同的. 例如, 100分钟有可能只"配合"了98个柱, 因为有两分钟没有任何报价进来.

幸运的是, 在MQL 4 中已经有了帮助我们独立绘制此类图表的必要工具.

 

2.   实现

首先, 让我们把这个任务分成两步:

  • 处理历史数据

  • 更新最新柱形

在第一步, 我们在交易品种名称之前使用"ALL"作为前缀("ALL" 表示全部柱形), 创建一个新的历史文件, 并且把历史中加入的柱形写入其中.

在随MT4客户终端一起发布的"period_converter"脚本程序中, 解决了一个类似的问题. 此脚本在一个非标准时段中生成了图表. 我们将使用这个例子来学习怎样操作历史文件.

在创建程序之前, 我们必须决定它的形式: 是一个脚本程序, 还是指标, 亦或是EA交易?指标用于显示数组的内容. 我们在此不需要它. 而对于脚本程序和EA交易, 它们之间的唯一区别就是脚本程序运行结束后会立即在图表上被删除. 在此阶段是符合我们需要的, 所以我们这一步将创建一个脚本程序.

      这就是我们得到的结果 (AllMinutes_Step1.mq4):

#property show_inputs

//---- 允许/禁止在节假日绘制柱形
//---- 如果 == true, 节假日不会被填充
//---- 如果 == false, 节假日会被柱形填充 O=H=L=C
extern bool  SkipWeekEnd=true;

int start()
  {
   int HistoryHandle=-1,pre_time,now_time,_PeriodSec;
   double  now_close,now_open,now_low,now_high,now_volume,pre_close;

   int    _GetLastError=0,cnt_copy=0,cnt_add=0;
   int    temp[13];

//---- 记住图表的交易品种和周期
   string _Symbol=Symbol();
   int _Period= Period();
   _PeriodSec = _Period * 60;

//---- 打开我们将要写入历史的文件
   string file_name=StringConcatenate("ALL",_Symbol,_Period,".hst");
   HistoryHandle=FileOpenHistory(file_name,FILE_BIN|FILE_WRITE);
   if(HistoryHandle<0)
     {
      _GetLastError=GetLastError();
      Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - 错误 #",_GetLastError);
      return(-1);
     }

//---- 写文件头
   FileWriteInteger(HistoryHandle,400,LONG_VALUE);
   FileWriteString(HistoryHandle,"Copyright © 2006, komposter",64);
   FileWriteString(HistoryHandle,"ALL"+_Symbol,12);
   FileWriteInteger(HistoryHandle,_Period,LONG_VALUE);
   FileWriteInteger(HistoryHandle,Digits,LONG_VALUE);
   FileWriteInteger(HistoryHandle,0,LONG_VALUE);       //timesign
   FileWriteInteger(HistoryHandle,0,LONG_VALUE);       //last_sync
   FileWriteArray(HistoryHandle,temp,0,13);

//+-----------------------------------------------------------------+
//| 处理历史
//+-----------------------------------------------------------------+
   int bars=Bars;
   pre_time=Time[bars-1];
   for(int i=bars-1; i>=0; i--)
     {
      //---- 记住柱参数
      now_open=Open[i];
      now_high=High[i];
      now_low=Low[i];
      now_close=Close[i];
      now_volume=Volume[i];
      now_time=Time[i]/_PeriodSec;
      now_time*=_PeriodSec;

      //---- 如果有跳空的柱,
      while(now_time>pre_time+_PeriodSec)
        {
         pre_time+=_PeriodSec;
         pre_time    /= _PeriodSec;
         pre_time    *= _PeriodSec;

         //---- 如果不是周末,
         if(SkipWeekEnd)
           {
            if(TimeDayOfWeek(pre_time)<=0 || 
               TimeDayOfWeek(pre_time)>5)
              {
               continue;
              }
            if(TimeDayOfWeek(pre_time)==5)
              {
               if( TimeHour(pre_time) == 23 ||
                  TimeHour(pre_time + _PeriodSec) == 23 )
                 {
                  continue;
                 }
              }
           }

         //---- 把跳空的柱写入文件
         FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,0,DOUBLE_VALUE);
         FileFlush(HistoryHandle);
         cnt_add++;
        }

      //---- 把新柱写入文件
      FileWriteInteger(HistoryHandle,now_time,LONG_VALUE);
      FileWriteDouble(HistoryHandle,now_open,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_low,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_high,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_volume,DOUBLE_VALUE);
      FileFlush(HistoryHandle);
      cnt_copy++;

      //---- 记住所记录柱的时间值 
      //---- 和收盘价
      pre_close=now_close;
      pre_time=now_time/_PeriodSec;
      pre_time*=_PeriodSec;
     }

//---- 关闭文件
   FileClose(HistoryHandle);

//---- 显示统计信息
   Print("< - - - ",_Symbol,_Period,": 共有 ",cnt_copy,
         " 个柱, 增加了 ",cnt_add," 个柱 - - - >");
   Print("< - - - 如需检视结果, 请打开图表 \"ALL",
         _Symbol,_Period,"\" - - - >");
   return(0);
  }


这里需要注意一下SkipWeekEnd 变量. 如果此变量是false, 周末将会以 O=H=L=C (等号)的柱形进行填充.

让我们把脚本附加到GBPUSD一分钟图表上, 简单看看它工作得如何:


  

现在, 让我们在离线模式下打开 ALLGBPUSD1 图表并把它与前面的图表做比较:

 

你可以看到, 一些跳空的分钟柱已经增加到图表上了. 它们用红色圈出来了. 这正是我们想要的, 不是吗?

我们已经有了一个无缝的图表, 我们可以更新它. 新的报价会显示出来, 但是新的缝隙又出现了.

可以再次运行"period_converter"脚本程序. 它也可以解决图表的更新问题. 我们只会做一个改变: 增加填充跳空柱的代码块. 因为图表必须在每个订单时刻更新, 让我们把代码转移到EA交易中. 它会在每次有新报价时运行. 让我们把代码放到init() 函数的第一部分, 因为这部分一定只运行一次, 整段新的代码将放在start() 函数中, 因为它在每个订单时刻使用. 另外, 文件的关闭代码放在deinit()函数中, 这是它的正确位置.

      这样, EA交易的代码(AllMinutes_Step2.mq4)如下显示:

#include <WinUser32.mqh>

//---- 允许/禁止在节假日绘制柱形
//---- 如果它 == true, 节假日会保持不被填充
//---- 如果它 == false, 节假日会使用 O=H=L=C 柱形填充
extern bool  SkipWeekEnd=true;

int  HistoryHandle=-1,hwnd=0,last_fpos=0,pre_time,now_time;
int  _Period,_PeriodSec;
double  now_close,now_open,now_low,now_high,now_volume;
double  pre_close,pre_open,pre_low,pre_high,pre_volume;
string  _Symbol;

int init()
  {
   int    _GetLastError=0,cnt_copy=0,cnt_add=0;
   int    temp[13];

//---- 记住图表的交易品种和周期
   _Symbol=Symbol();
   _Period=Period();
   _PeriodSec=_Period*60;
   hwnd=0;

//---- 打开写入历史的文件
   string file_name=StringConcatenate("ALL",_Symbol,_Period,".hst");
   HistoryHandle=FileOpenHistory(file_name,FILE_BIN|FILE_WRITE);
   if(HistoryHandle<0)
     {
      _GetLastError=GetLastError();
      Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - 错误 #",_GetLastError);
      return(-1);
     }

//---- 写文件头
   FileWriteInteger(HistoryHandle,400,LONG_VALUE);
   FileWriteString(HistoryHandle,"Copyright © 2006, komposter",64);
   FileWriteString(HistoryHandle,StringConcatenate("ALL",_Symbol),
                   12);
   FileWriteInteger(HistoryHandle,_Period,LONG_VALUE);
   FileWriteInteger(HistoryHandle,Digits,LONG_VALUE);
   FileWriteInteger(HistoryHandle,0,LONG_VALUE);       //timesign
   FileWriteInteger(HistoryHandle,0,LONG_VALUE);       //last_sync
   FileWriteArray(HistoryHandle,temp,0,13);

//+-----------------------------------------------------------------+
//| 处理历史
//+-----------------------------------------------------------------+
   int bars=Bars;
   pre_time=Time[bars-1];
   for(int i=bars-1; i>=1; i--)
     {
      //---- 记住柱参数
      now_open=Open[i];
      now_high=High[i];
      now_low=Low[i];
      now_close=Close[i];
      now_volume=Volume[i];
      now_time=Time[i]/_PeriodSec;
      now_time*=_PeriodSec;

      //---- 如果有跳空的柱,
      while(now_time>pre_time+_PeriodSec)
        {
         pre_time+=_PeriodSec;
         pre_time    /= _PeriodSec;
         pre_time    *= _PeriodSec;

         //---- 如果这不是周末,
         if(SkipWeekEnd)
           {
            if(TimeDayOfWeek(pre_time)<=0 || TimeDayOfWeek(pre_time)>5)
              {
               continue;
              }
            if(TimeDayOfWeek(pre_time)==5)
              {
               if( TimeHour(pre_time) == 23 ||
                  TimeHour(pre_time + _PeriodSec) == 23 )
                 {
                  continue;
                 }
              }
           }

         //---- 把获得的柱形写入文件
         FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,0,DOUBLE_VALUE);
         FileFlush(HistoryHandle);
         cnt_add++;
        }

      //---- 把新柱写入文件
      FileWriteInteger(HistoryHandle,now_time,LONG_VALUE);
      FileWriteDouble(HistoryHandle,now_open,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_low,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_high,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_volume,DOUBLE_VALUE);
      FileFlush(HistoryHandle);
      cnt_copy++;

      //---- 记住所记录柱的时间值和 
      //---- 收盘价
      pre_close=now_close;
      pre_time=now_time/_PeriodSec;
      pre_time*=_PeriodSec;
     }

   last_fpos=FileTell(HistoryHandle);

//---- 显示统计信息
   Print("< - - - ",_Symbol,_Period,": 共有 ",cnt_copy," 个柱, 增加了 ",cnt_add," 个柱 - - - >");
   Print("< - - - 如需检视结果, 请打开图表 \"ALL",
         _Symbol,_Period,"\" - - - >");

//---- 调用start函数从第0柱开始立即开始绘制
   start();

   return(0);
  }
//----
int start()
  {
//+---------------------------------------------------------------+
//| 处理进来的订单
//+---------------------------------------------------------------+

//---- 把"光标"放到最新柱之前
//---- (除了第一次运行之外每次都要这样做)
   FileSeek(HistoryHandle,last_fpos,SEEK_SET);

//---- 记住柱的参数
   now_open=Open[0];
   now_high=High[0];
   now_low=Low[0];
   now_close=Close[0];
   now_volume=Volume[0];
   now_time=Time[0]/_PeriodSec;
   now_time*=_PeriodSec;

//---- 如果已经生成柱形, 
   if(now_time>=pre_time+_PeriodSec)
     {
      //---- 写下新生成的柱形
      FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE);
      FileWriteDouble(HistoryHandle,pre_open,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_low,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_high,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_volume,DOUBLE_VALUE);
      FileFlush(HistoryHandle);

      //---- 在写入第0个柱之前记住文件的位置
      last_fpos=FileTell(HistoryHandle);
     }

//---- 如果跳过的柱出现,
   while(now_time>pre_time+_PeriodSec)
     {
      pre_time+=_PeriodSec;
      pre_time /= _PeriodSec;
      pre_time *= _PeriodSec;

      //---- 如果不是周末,
      if(SkipWeekEnd)
        {
         if(TimeDayOfWeek(pre_time)<=0 || 
            TimeDayOfWeek(pre_time)>5)
           {
            continue;
           }
         if(TimeDayOfWeek(pre_time)==5)
           {
            if(TimeHour(pre_time)==23 || 
               TimeHour(pre_time+_PeriodSec)==23)
              {
               continue;
              }
           }
        }

      //---- 把跳空的柱写入文件
      FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE);
      FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,0,DOUBLE_VALUE);
      FileFlush(HistoryHandle);

      //---- 在写入第0个柱之前记住文件的位置
      last_fpos=FileTell(HistoryHandle);
     }

//---- 写入当前柱
   FileWriteInteger(HistoryHandle,now_time,LONG_VALUE);
   FileWriteDouble(HistoryHandle,now_open,DOUBLE_VALUE);
   FileWriteDouble(HistoryHandle,now_low,DOUBLE_VALUE);
   FileWriteDouble(HistoryHandle,now_high,DOUBLE_VALUE);
   FileWriteDouble(HistoryHandle,now_close,DOUBLE_VALUE);
   FileWriteDouble(HistoryHandle,now_volume,DOUBLE_VALUE);
   FileFlush(HistoryHandle);

//---- 记住所记录柱的参数
   pre_open=now_open;
   pre_high=now_high;
   pre_low=now_low;
   pre_close=now_close;
   pre_volume=now_volume;
   pre_time=now_time/_PeriodSec;
   pre_time*=_PeriodSec;

//---- 找到新报价"发送"的窗口
   if(hwnd==0)
     {
      hwnd = WindowHandle( StringConcatenate( "ALL", _Symbol ), _Period );
      if( hwnd != 0 )
        {
         Print("< - - - Chart ",
               "ALL"+_Symbol,_Period," 已被找到!- - - >");
        }
     }
//---- 如果找到就做更新
   if(hwnd!=0)
     {
      PostMessageA(hwnd,WM_COMMAND,33324,0);
     }
  }
//----
int deinit()
  {
   if(HistoryHandle>=0)
     {
      //---- 关闭文件
      FileClose(HistoryHandle);
      HistoryHandle=-1;
     }
   return(0);
  }
   

    有一点要记住: 更新图表是较耗费处理器资源的, 因为终端需要从文件中载入所有的柱. 如果文件中有很多柱形, 终端可能变得非常缓慢. 这主要依赖于安装MT4客户终端的计算机的配置. 无论如何, 资源总不是取之不尽的. 我们将使用简单方法解决这个问题: 只要把显示于图表的柱数减少到10000("工具" - "选项" - "图表", "图表的最大柱数"参数). 现在让我们重启终端并附加EA交易:


  

EA交易已经迅速"修补"了历史并等待直到新的订单时刻出现. 两分钟后, 相同的图表显示如下:

   

你可以看到, 一分钟柱添加到了上面的图表, 而跳过的柱增加到下面的图表中.

这说明我们已经影响了想要的结果!


3.   规模

当然, 一个图表已经可以了, 但是如果我们需要打开10个没有跳空柱形的图表呢?为每一个正常图表开额外一个新的, "临时"图表不会是最好的方案. 这需要消耗额外的资源, 另外工作也不方便.

让我们创建一个EA交易来处理任意数量的图表. 这将是一个合适并且节约的解决方案.

这样, 我们必须修改我们的代码来使它可以同时处理多个图表:

  • 增加一个外部变量用于修改图表的列表,
  • 把所有的变量替换成数组, 数组元素数量等于需要被处理的图表的数量,
  • 把需要搜索图表部分代码放到循环里面, 并且
  • 把更新模块放到无限循环中, 通过这种方式, 它的更新将不依赖于新报价的到来. 如果列表中有不同的交易品种, 它们的更新时间也可能不同.

这就是我们要的结果 (AllMinutes.mq4):

#include <WinUser32.mqh>

//---- 需要处理的图表列表, 使用逗号 (",")分隔
extern string    ChartList="EURUSD1,GBPUSD1";
//---- 允许/禁止在周末绘制柱形
//---- 如果它 == true, 周末会保持不被填充
//---- 如果它 == false, 周末会使用 O=H=L=C 的柱形填充
extern bool     SkipWeekEnd=true;
//---- 图表更新的频率, 单位是毫秒
//---- 此数值越高, EA交易使用的资源就 
//---- 越少.
extern int   RefreshLuft=1000;

int init()
  {
   start();
   return(0);
  }

int start()
  {
   int   _GetLastError=0,cnt_copy=0,cnt_add=0,temp[13];
   int   Charts=0,pos=0,curchar=0,len=StringLen(ChartList);
   string    cur_symbol="",cur_period="",file_name="";

   string    _Symbol[100]; int _Period[100],_PeriodSec[],_Bars[];
   int HistoryHandle[],hwnd[],last_fpos[],pre_time[],now_time[];
   double   now_close[],now_open[],now_low[],now_high[],now_volume[];
   double   pre_close[],pre_open[],pre_low[],pre_high[],pre_volume[];

//---- 需要处理的图表计数
   while(pos<=len)
     {
      curchar=StringGetChar(ChartList,pos);
      if(curchar>47 && curchar<58)
        {
         cur_period=cur_period+CharToStr(curchar);
        }
      else
        {
         if(curchar==',' || pos==len)
           {
            MarketInfo(cur_symbol,MODE_BID);
            if(GetLastError()==4106)
              {
               Alert("未知交易品种 ",cur_symbol,"!!!");
               return(-1);
              }
            if(iClose(cur_symbol,StrToInteger(cur_period),0)<=0)
              {
               Alert("未知周期 ",cur_period,"!!!");
               return(-1);
              }

            _Symbol[Charts]=cur_symbol;
            _Period[Charts]=StrToInteger(cur_period);
            cur_symbol=""; cur_period="";

            Charts++;
           }
         else
           {
            cur_symbol=cur_symbol+CharToStr(curchar);
           }
        }
      pos++;
     }
   Print("< - - - 已找到 ",Charts," correct charts. - - - >");
   ArrayResize(_Symbol,Charts);
   ArrayResize(_Period,Charts);
   ArrayResize(HistoryHandle,Charts);
   ArrayResize(hwnd,Charts);
   ArrayResize(last_fpos,Charts);
   ArrayResize(pre_time,Charts);
   ArrayResize(now_time,Charts);
   ArrayResize(now_close,Charts);
   ArrayResize(now_open,Charts);
   ArrayResize(now_low,Charts);
   ArrayResize(now_high,Charts);
   ArrayResize(now_volume,Charts);
   ArrayResize(pre_close,Charts);
   ArrayResize(pre_open,Charts);
   ArrayResize(pre_low,Charts);
   ArrayResize(pre_high,Charts);
   ArrayResize(pre_volume,Charts);
   ArrayResize(_PeriodSec,Charts);
   ArrayResize(_Bars,Charts);

   for(int curChart=0; curChart<Charts; curChart++)
     {
      _PeriodSec[curChart]=_Period[curChart] *60;

      //---- 打开将要写入历史的文件
      file_name=StringConcatenate("ALL",_Symbol[curChart],
                                  _Period[curChart],".hst");
      HistoryHandle[curChart]=FileOpenHistory(file_name,
                                              FILE_BIN|FILE_WRITE);
      if(HistoryHandle[curChart]<0)
        {
         _GetLastError=GetLastError();
         Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - 错误 #",_GetLastError);
         continue;
        }

      //---- 写文件头
      FileWriteInteger(HistoryHandle[curChart],400,LONG_VALUE);
      FileWriteString(HistoryHandle[curChart],
                      "Copyright © 2006, komposter",64);
      FileWriteString(HistoryHandle[curChart],
                      StringConcatenate("ALL",
                      _Symbol[curChart]),12);
      FileWriteInteger(HistoryHandle[curChart],_Period[curChart],
                       LONG_VALUE);
      FileWriteInteger(HistoryHandle[curChart],
                       MarketInfo(_Symbol[curChart],
                       MODE_DIGITS),LONG_VALUE);
      FileWriteInteger(HistoryHandle[curChart],0,
                       LONG_VALUE);       //timesign
      FileWriteInteger(HistoryHandle[curChart],0,
                       LONG_VALUE);       //last_sync
      FileWriteArray(HistoryHandle[curChart],temp,0,13);

      //+-----------------------------------------------------------+
      //| 处理历史
      //+-----------------------------------------------------------+
      _Bars[curChart]=iBars(_Symbol[curChart],_Period[curChart]);
      pre_time[curChart]=iTime(_Symbol[curChart],
                               _Period[curChart],_Bars[curChart]-1);
      for(int i=_Bars[curChart]-1; i>=1; i--)
        {
         //---- 记住柱参数
         now_open[curChart]=iOpen(_Symbol[curChart],
                                  _Period[curChart],i);
         now_high[curChart]=iHigh(_Symbol[curChart],
                                  _Period[curChart],i);
         now_low[curChart]=iLow(_Symbol[curChart],
                                _Period[curChart],i);
         now_close[curChart]=iClose(_Symbol[curChart],
                                    _Period[curChart],i);
         now_volume[curChart]=iVolume(_Symbol[curChart],
                                      _Period[curChart],i);
         now_time[curChart]=iTime(_Symbol[curChart],
                                  _Period[curChart],i)
         /_PeriodSec[curChart];
         now_time[curChart]*=_PeriodSec[curChart];

         //---- 如果有跳空的柱,
         while(now_time[curChart]>pre_time[curChart]+
               _PeriodSec[curChart])
           {
            pre_time[curChart]+=_PeriodSec[curChart];
            pre_time[curChart] /= _PeriodSec[curChart];
            pre_time[curChart] *= _PeriodSec[curChart];

            //---- 如果不是周末,
            if(SkipWeekEnd)
              {
               if(TimeDayOfWeek(pre_time[curChart])<=0 || 
                  TimeDayOfWeek(pre_time[curChart])>5)
                 {
                  continue;
                 }
               if(TimeDayOfWeek(pre_time[curChart])==5)
                 {
                  if(TimeHour(pre_time[curChart])==23 || 
                     TimeHour(pre_time[curChart]+_PeriodSec[curChart])==23)
                    {
                     continue;
                    }
                 }
              }

            //---- 把跳空的柱写入文件
            FileWriteInteger(HistoryHandle[curChart],pre_time[curChart],
                             LONG_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],0,
                            DOUBLE_VALUE);
            FileFlush(HistoryHandle[curChart]);
            cnt_add++;
           }

         //---- 把新柱写入文件
         FileWriteInteger(HistoryHandle[curChart],now_time[curChart],
                          LONG_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_open[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_low[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_high[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_close[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_volume[curChart],DOUBLE_VALUE);
         FileFlush(HistoryHandle[curChart]);
         cnt_copy++;

         //---- 记住所记录柱的时间值 
         //---- 和收盘价
         pre_close[curChart]=now_close[curChart];
         pre_time[curChart]=now_time[curChart]/
                            _PeriodSec[curChart];
         pre_time[curChart]*=_PeriodSec[curChart];
        }

      last_fpos[curChart]=FileTell(HistoryHandle[curChart]);

      //---- 显示统计数据
      Print("< - - - ",_Symbol[curChart],_Period[curChart],": there were ",
            cnt_copy," 个柱, 增加了 ",cnt_add," 个柱 - - - >");
      Print("< - - - 如需查看结果, 打开图表 \"ALL",
            _Symbol[curChart],_Period[curChart],"\" - - - >");

     }

//+---------------------------------------------------------------+
//| 处理进来的订单
//+---------------------------------------------------------------+
   while(!IsStopped())
     {
      RefreshRates();
      for(curChart=0; curChart<Charts; curChart++)
        {
         //---- 在最新柱之前放入 "光标" 
         //---- (除了第一次运行之外都要做)
         FileSeek(HistoryHandle[curChart],last_fpos[curChart],
                  SEEK_SET);

         //---- 记住柱参数
         now_open[curChart]=iOpen(_Symbol[curChart],
                                  _Period[curChart],0);
         now_high[curChart]=iHigh(_Symbol[curChart],
                                  _Period[curChart],0);
         now_low[curChart]=iLow(_Symbol[curChart],
                                _Period[curChart],0);
         now_close[curChart]=iClose(_Symbol[curChart],
                                    _Period[curChart],0);
         now_volume[curChart]=iVolume(_Symbol[curChart],
                                      _Period[curChart],0);
         now_time[curChart]=iTime(_Symbol[curChart],
                                  _Period[curChart],0)
         /_PeriodSec[curChart];
         now_time[curChart]*=_PeriodSec[curChart];

         //---- 如果柱形已经生成, 
         if(now_time[curChart]>=pre_time[curChart]+
            _PeriodSec[curChart])
           {
            //---- 写下生成的柱形
            FileWriteInteger(HistoryHandle[curChart],pre_time[curChart],
                             LONG_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_open[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_low[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_high[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_volume[curChart],DOUBLE_VALUE);
            FileFlush(HistoryHandle[curChart]);

            //---- 在文件记录第0柱前记住位置
            last_fpos[curChart]=FileTell(HistoryHandle[curChart]);
           }

         //---- 如果出现了跳空柱,
         while(now_time[curChart]>pre_time[curChart]+_PeriodSec[curChart])
           {
            pre_time[curChart] += _PeriodSec[curChart];
            pre_time[curChart] /= _PeriodSec[curChart];
            pre_time[curChart] *= _PeriodSec[curChart];

            //---- 如果不是周末,
            if(SkipWeekEnd)
              {
               if(TimeDayOfWeek(pre_time[curChart])<=0 || 
                  TimeDayOfWeek(pre_time[curChart])>5)
                 {
                  continue;
                 }
               if(TimeDayOfWeek(pre_time[curChart])==5)
                 {
                  if(TimeHour(pre_time[curChart])==23 || 
                     TimeHour(pre_time[curChart]+_PeriodSec[curChart])==23)
                    {
                     continue;

                    }
                 }
              }

            //---- 把跳空的柱写入文件
            FileWriteInteger(HistoryHandle[curChart],pre_time[curChart],
                             LONG_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],0,
                            DOUBLE_VALUE);
            FileFlush(HistoryHandle[curChart]);

            //---- 在文件记录第0柱前记住位置
            last_fpos[curChart]=FileTell(HistoryHandle[curChart]);
           }

         //---- 写当前柱
         FileWriteInteger(HistoryHandle[curChart],now_time[curChart],
                          LONG_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_open[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_low[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_high[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_close[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_volume[curChart],DOUBLE_VALUE);
         FileFlush(HistoryHandle[curChart]);

         //---- 记住写入柱的参数
         pre_open[curChart]        = now_open[curChart];
         pre_high[curChart]        = now_high[curChart];
         pre_low[curChart]=now_low[curChart];
         pre_close[curChart]=now_close[curChart];
         pre_volume[curChart]=now_volume[curChart];
         pre_time[curChart]=now_time[curChart]/
                            _PeriodSec[curChart];
         pre_time[curChart]*=_PeriodSec[curChart];

         //---- 找到需要"发送"新报价的窗口
         if(hwnd[curChart]==0)
           {
            hwnd[curChart]=WindowHandle(StringConcatenate("ALL",
                                        _Symbol[curChart]),
                                        _Period[curChart]);
            if(hwnd[curChart]!=0)
              {
               Print("< - - - Chart ","ALL"+_Symbol[curChart],
                     _Period[curChart]," found! - - - >"); 
              }
           }
         //---- 并且, 如果找到就更新它
         if(hwnd[curChart]!=0)
           {
            PostMessageA(hwnd[curChart],WM_COMMAND,33324,0); 
           }
        }
      Sleep(RefreshLuft);
     }

   for(curChart=0; curChart<Charts; curChart++)
     {
      if(HistoryHandle[curChart]>=0)
        {
         //---- 关闭文件
         FileClose(HistoryHandle[curChart]);
         HistoryHandle[curChart]=-1;
        }
     }
   return(0);
  }
 

现在让我们在EURUSD5分钟图表上载入EA交易, ChartList参数等于"EURUSD1,GBPUSD1,EURGBP1", 并且以离线模式打开全部三个图表:

 



看起来一切正常: 全部三个图表都同时更新, 而且如果图表上出现缝隙会被"修补".

本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/1407

附加的文件 |
AllMinutes.mq4 (12.72 KB)
MetaTrader 4 中的事件 MetaTrader 4 中的事件

本文介绍的是使用编程方法追踪MetaTrader 4客户终端中的事件, 它的目标读者是对终端的操作和MQL4编程具有基本知识和技能的人员.

文件操作. 一个重要市场时间可视化的实例 文件操作. 一个重要市场时间可视化的实例

本文展示并展望了使用MQL4在外汇交易市场上做出更加高效的工作.

创建自定义指标的特性 创建自定义指标的特性

在MetaTrader交易系统中创建自定义指标有一些特性.

EA交易, 脚本程序和指标的同步 EA交易, 脚本程序和指标的同步

本文介绍了开发捆绑程序, 即可能同时包含EA交易, 脚本程序和指标的程序集合的必要性以及通用原则.