English Русский Español Deutsch 日本語 Português
preview
时间序列挖掘的数据标签(第1部分):通过EA操作图制作具有趋势标记的数据集

时间序列挖掘的数据标签(第1部分):通过EA操作图制作具有趋势标记的数据集

MetaTrader 5EA交易 | 8 三月 2024, 14:30
567 0
Yuqiang Pan
Yuqiang Pan

汇总

当我们设计人工智能模型时,我们通常需要首先准备数据。良好的数据质量将使我们在模型训练和验证方面事半功倍。但我们的外汇或股票数据是特殊的,其中包含复杂的市场信息和时间信息,数据标注很困难,但我们可以很容易地在图表上分析历史数据的趋势。

 

本节介绍了一种通过EA操作图制作带有趋势标记的数据集的方法,您可以根据自己的想法直观地操作数据,当然您也可以使用相同的方法来扩展和自定义自己的数据集!

目录:

  1. 定义标签数据格式
  2. 初始化图表和文件
  3. 设计和标记操作逻辑
  4. 组织数据并写入文件
  5. 附件:完整的EA代码示例


定义标签数据格式

当我们从客户那里获得外汇或股票数据时(本文不讨论从文件中读取或从其他网站下载的外部数据),一般情况是:

Time(时间) Open(开盘价) High(最高价) Low(最低价) Close(收盘价) Tick_volume(分时交易量)
2021-12-10 01:15:00
1775.94
1775.96
1775.58
1775.58
173
2021-12-10 01:30:00
1775.58
1776.11
1775.48
1775.88
210
2021-12-10 01:45:00
1775.88
1776.22
1775.68
1776.22
212
2021-12-10 02:00:00
1776.22
1777.57
1775.98
1777.02
392
2021-12-10 02:15:00
1776.99
1777.72
1776.89
1777.72
264

以上是5个时间序列数据的样子。它们的收盘价(Close)和开盘价(Open)自始至终都是相互联系的,连贯性很强。假设我们认为前两个是上升趋势,其他是下降趋势(以上5个数据为例)。一般标记方法将数据分为两部分:

Time(时间)

Open(开盘价) High(最高价)   Low(最低价)   Close(收盘价) Tick_volume(分时交易量)
2021-12-10 01:15:00
1775.94
1775.96
1775.58
1775.58
173
2021-12-10 01:30:00
1775.58
1776.11
1775.48
1775.88
210

Time(时间) Open(开盘价) High(最高价)   Low(最低价) Close(收盘价) Tick_volume(分时交易量)
2021-12-10 01:45:00
1775.88
1776.22
1775.68
1776.22
212
2021-12-10 02:00:00
1776.22
1777.57
1775.98
1777.02
392
2021-12-10 02:15:00
1776.99
1777.72
1776.89
1777.72
264

然后告诉我们的模型哪个部分是上升趋势,哪个部分是下降趋势,但这忽略了它们的整体属性,会破坏数据的完整性,那么我们如何解决这个问题呢?

一种可行的方法是在我们的时间序列中添加趋势分组,如下所示(以上述5条数据为例,或遵循上述假设):

Time(时间) Open(开盘价) High(最高价) Low(最低价) Close(收盘价) Tick_volume(分时交易量) Trend_group(趋势组)
2021-12-10 01:15:00
1775.94
1775.96
1775.58
1775.58
173 0
2021-12-10 01:30:00
1775.58
1776.11
1775.48
1775.88
210 0
2021-12-10 01:45:00
1775.88
1776.22
1775.68
1776.22
212 1
2021-12-10 02:00:00
1776.22
1777.57
1775.98
1777.02
392 1
2021-12-10 02:15:00
1776.99
1777.72
1776.89
1777.72
264 1

但如果我们想在模型中进行趋势发展分析,比如当前趋势发展到什么程度(例如波动理论告诉我们,一般趋势一般包括趋势阶段和调整阶段,趋势阶段有5个波动阶段,调整阶段有3个波动调整等),我们需要进一步标注数据,我们可以通过添加另一个索引列来实现这一点,该列表示数据中趋势的发展(假设以下10个数据中的前2个数据为上升趋势,后5个数据为上涨趋势,中间的其余数据为下降趋势),如下所示:

Time(时间) Open(开盘价) High(最高价) Low(最低价) Close(收盘价) Tick_volume(分时交易量) Trend_group(趋势组) Trend_index(趋势索引)
2021-12-10 03:15:00 1776.38 1777.94 1775.47 1777.71 565 0 0
2021-12-10 03:30:00 1777.75 1778.93 1777.68 1778.61 406 0 1
2021-12-10 03:45:00 1778.58 1778.78 1777.65 1778.16 388 1 0
2021-12-10 04:00:00 1778.14 1779.42 1778.06 1779.14 393 1 1
2021-12-10 04:15:00 1779.16 1779.49 1778.42 1779.31 451 1 2
2021-12-10 04:30:00 1779.22 1779.42 1778.36 1778.37 306 0 0
2021-12-10 04:45:00 1778.42 1778.51 1777.60 1777.78 411 0 1
2021-12-10 05:00:00 1777.81 1778.68 1777.61 1778.57 372 0 2
2021-12-10 05:15:00 1778.54 1779.29 1778.42 1779.02 413 0 3
2021-12-10 05:30:00 1778.97 1779.49 1778.48 1778.50 278 0 4

注意:

1. 定义上升趋势的Trend_group为0

2. 定义下降趋势的Trend_group为1

 接下来,我们将开始在客户端操作图表,根据我们想要的模式标记数据。


初始化图表和文件

    图表初始化

          因为我们需要看图表来标记数据,所以图表不能随意滚动,必须根据我们的手动操作进行滚动,因此我们需要禁用 CHART_AUTOSCROLL 和 CHART_SHIFT:

           ChartSetInteger (0, CHART_AUTOSCROLL, false);
          
            ChartSetInteger (0, CHART_SHIFT, true);
          
            ChartSetInteger (0, CHART_MOUSE_SCROLL ,1);
          注意:代码的绿色部分旨在允许我们使用鼠标滚轮控制图表
            文件初始化

                文件的初始化应首先检查是否存在现有标签文件,如果存在历史文件,则将文件名保存到变量“reName”中:

                 do
                     {
                       //---Find if there are files that match the chart
                       if (StringFind(name, Symbol())!=-1 && StringFind(name,".csv")!=-1)
                         reName=name;
                     }
                
                   while (FileFindNext(hd,name));
                注意:这里应该注意的是,我们使用的是“do-while”循环,它与“while”循环的不同之处在于,它首先执行运算符,然后计算表达式。但是名称的初始化是个问题,我们可以这样做
                int hd= FileFindFirst("*",name,0);


                  如果有一个原始的标记文件,打开该文件并使用read_csv()函数获取标记的最后时间:

                    read_csv(file_handle,a);
                    然后将图表滚动到最后标记的时间:
                    shift = - iBarShift(Symbol(),PERIOD_CURRENT,(datetime)a[i-8]);
                    ChartNavigate(0, CHART_END ,shift);


                    如果没有历史记录文件,就创建一个文件:

                    file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t), FILE_WRITE | FILE_CSV | FILE_READ);
                    然后将图表滚动到全局变量“start_t”指定的位置
                     shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)start_t);
                      ChartNavigate(0,CHART_END,shift);
                    添加一条垂直红线以标记起始列:
                     ObjectCreate (0,"Start",OBJ_VLINE,0,(datetime)start_t,0)
                    这部分的逻辑是这样组织的:
                     if (FileIsExist(reName))
                         {
                          file_handle = FileOpen(reName, FILE_WRITE | FILE_CSV | FILE_READ );
                           string a[];
                           int i= 0 ;
                          read_csv(file_handle,a);
                          i = ArraySize (a);
                          shift = -iBarShift(Symbol(), PERIOD_CURRENT,(datetime)a[i-8]);
                           ChartNavigate(0,CHART_END,shift);
                         }
                       else
                         {
                          file_handle = FileOpen (StringFormat ("%s%d-%d.csv", Symbol(), Period(),start_t), FILE_WRITE | FILE_CSV | FILE_READ );
                           Print ("There is no history file,create file:" , StringFormat ( "%s%d-%d",Symbol(), Period(),start_t));
                           shift = - iBarShift (Symbol(), PERIOD_CURRENT ,(datetime)start_t);
                           ChartNavigate (0, CHART_END ,shift);
                           ObjectCreate (0,"Start", OBJ_VLINE,0,(datetime)start_t,0);
                         }
                    注意:由于我们想将图表向左移动,我们必须在“iBarShift()”函数之前添加“-”
                    shift = -iBarShift(Symbol(), PERIOD_CURRENT ,(datetime)start_t);
                    当然,它也可以在ChartNavigate()函数中实现,例如:
                    ChartNavigate(0,CHART_END,-shift);
                    本文中的代码仍然是根据第一种方法实现的。
                      这些初始化操作将在我们的OnInit()中实现,包括定义我们需要的变量。最重要的是明确我们希望图表移动的位置并开始标记。这主要由变量“shift”和“start_t”控制。我们将反映在最终代码中:
                        int OnInit()
                          {
                        //---initial
                           string name;
                           string reName="1";
                           int hd=FileFindFirst("*",name,0);
                           int shift;
                        
                           ChartSetInteger(0,CHART_AUTOSCROLL,false);
                           ChartSetInteger(0,CHART_SHIFT,false);
                           ChartSetInteger(0,CHART_MOUSE_SCROLL,1);
                        
                        
                           do
                             {
                              //---check File
                              if(StringFind(name,Symbol())!=-1 && StringFind(name,".csv")!=-1)
                                 reName=name;
                             }
                           while(FileFindNext(hd,name));
                        
                           if(FileIsExist(reName))
                             {
                              file_handle = FileOpen(reName,FILE_WRITE|FILE_CSV|FILE_READ);
                              string a[];
                              int i=0;
                              read_csv(file_handle,a);
                              i = ArraySize(a);
                              shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)a[i-8]);
                              ChartNavigate(0,CHART_END,shift);
                             }
                           else
                             {
                              file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t),FILE_WRITE|FILE_CSV|FILE_READ);
                              Print(FileTell(file_handle));
                              Print("No history file,create file:",StringFormat("%s%d-%d",Symbol(),Period(),start_t));
                              shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)start_t);
                              ChartNavigate(0,CHART_END,shift);
                              ObjectCreate(0,"Start",OBJ_VLINE,0,(datetime)start_t,0);
                             }
                           return(INIT_SUCCEEDED);
                          }

                          注意:

                          1. start_t变量-指定开始的时间范围;

                          2. shift 变量-指定要移位的列数,代码示例通过转换指定的时间显示要移位的列数;

                          3. read_csv()函数将在稍后定义。

                            read_csv()函数的定义:
                               void read_csv(int hd,
                                             string &arry[])
                                {
                                 int i= 0;
                                 while(!FileIsEnding(hd))
                                   {
                                    ArrayResize(arry,i+1);
                                    arry[i]= FileReadString(hd);
                                    i++;
                                   }
                                }

                              注意:我们使用“while”循环来查找历史注释文件的结束行,获取文件中的最后一行数据,并查找我们最后一次注释的结束时间。此注释将图表滚动到此柱状图,以便我们可以从此处继续进行注释。


                              设计和标记操作逻辑

                                操作图表:可以从客户端的帮助主题轻松查询此部分。
                                  • Home —移动到图表的最后一个柱;
                                  • End — 移动到图表的第一个柱;
                                  • Page Up — 将图表向后移动一个窗口的距离;
                                  • Page Down — 将图表向前移动一个窗口的距离;
                                  • Ctrl+I — 打开一个包含指标列表的窗口;
                                  • Ctrl+B — 打开一个包含对象列表的窗口;
                                  • Alt+1—图表显示为一系列柱形;
                                  • Alt+2 — 该图表被显示为日式蜡烛的序列;
                                  • Alt+3— 图表显示为连接收盘价格的线;
                                  • Ctrl+G — 在图表窗口中显示/隐藏网格;
                                  • "+"— 放大图表;
                                  • "-" - 缩小图表;
                                  • F12 — 逐步滚动图表(逐条滚动);
                                  • F8 — 打开属性窗口;
                                  • Backspace — 从图表中删除最后添加的对象;
                                  • Delete — 删除所有选定的对象;
                                  • Ctrl+Z — 取消删除最后一个对象。
                                    控制逻辑:
                                      1. 按键告诉EA下一步标记的数据将是什么样的趋势
                                        定义'b'键,'s'键,由虚拟键值定义:
                                           #define KEY_B     66
                                           #define KEY_S     83
                                            按‘b’然后按‘s’表示上升趋势,按‘s’然后按‘b’表示下降趋势,以上升趋势为例:
                                              1)此时按‘b’表示上升趋势。我们设置“typ”变量为0,“tp”变量为“start”,箭头颜色为“clrBlue”,标签计数“Num”增加1。需要注意的是,我们只需要在数据段的开头增加变量,并指定再次按下按钮将通过先反转来执行标记数据段的“end”部分
                                              b_press
                                              2) 按's'标记上升趋势结束,“typ”变量仍然为0,“tp”变量设置为“end”,箭头颜色仍然为“clrBlue”,标签计数“Num”保持不变。需要注意的是,我们只需要在数据段的开头增加变量,而‘first’的反转用于指定再次按下按钮将执行标记数据段的“start”部分。s_press
                                              3) 执行switch语句后,调用函数ChartRedraw()来重新绘制图表。
                                              if(id==CHARTEVENT_KEYDOWN)
                                                   {
                                                    switch(lparam)
                                                      {
                                                       case KEY_B:
                                                          if(first)
                                                            {
                                                             col=clrBlue ;
                                                             typ =0;
                                                             Num+=1;
                                                             tp = "start";
                                                            }
                                                          else
                                                            {
                                                             col=clrRed ;
                                                             typ = 1;
                                                             tp = "end";
                                                            }
                                                          ob =OBJ_ARROW_BUY;
                                                          first = !first;
                                                          Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                          break;
                                                       case KEY_S:
                                                          if(first)
                                                            {
                                                             col=clrRed ;
                                                             typ =1;
                                                             Num+=1;
                                                             tp = "start";
                                                            }
                                                          else
                                                            {
                                                             col=clrBlue ;
                                                             typ = 0;
                                                             tp = "end";
                                                            }
                                                          ob =OBJ_ARROW_SELL;
                                                          first = !first;
                                                          Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                          break;
                                              
                                                       default:
                                                          Print("You pressed:"+lparam+" key, do nothing!");
                                                      }
                                                    ChartRedraw(0);
                                                   }

                                              注意:

                                              1. “typ”变量 - 0表示上升趋势,1表示下降趋势;

                                              2. “Num”变量 —标记计数,会直观地显示在图表上;

                                              3. “first”变量 - 控制我们的标签总是成对的,确保每个组都是“b”和“s”或“s”和“b”,而不会混淆;

                                              4. “tp”变量 - 用于确定数据段的开始或结束。

                                              2. 在图表上单击鼠标左键以确定标记的位置

                                              if(id==CHARTEVENT_CLICK)
                                                   {
                                                    //--- definition
                                                    int x=(int)lparam;
                                                    int y=(int)dparam;
                                                    datetime dt    =0;
                                                    double   price =0;
                                                    int      window=0;
                                                    if(ChartXYToTimePrice(0,x,y,window,dt,price))
                                                      {
                                                       ObjectCreate(0,Name,ob,window,dt,price);
                                                       ObjectSetInteger(0,Name,OBJPROP_COLOR,col);
                                                       //Print("time:",dt,"shift:",iBarShift(Symbol(),PERIOD_CURRENT,dt));
                                                       if(tp=="start")
                                                          Start=dt;
                                                       else
                                                         {
                                                          if(file_handle)
                                                             file_write(Start,dt);
                                                         }
                                                       ChartRedraw(0);
                                                      }
                                                    else
                                                       Print("ChartXYToTimePrice return error code: ",GetLastError());
                                                   }
                                              //--- object delete
                                                 if(id==CHARTEVENT_OBJECT_DELETE)
                                                   {
                                                    Print("The object with name ",sparam," has been deleted");
                                                   }
                                              //--- object create
                                                 if(id==CHARTEVENT_OBJECT_CREATE)
                                                   {
                                                    Print("The object with name ",sparam," has been created!");
                                                   }

                                              注意:

                                              1. ChartXYToTimePrice()函数主要用于获取我们鼠标点击位置的柱的属性,包括当前时间和价格。我们使用全局变量“dt”来取得当前时间;

                                              2. 当我们点击鼠标时,我们还需要判断当前动作是数据段的开始还是结束。我们使用全局变量“tp”来判断。 

                                              3. 具体操作流程


                                                如果要标记上升趋势,请先按“b”键,在图表上开始标记的列上单击鼠标左键,然后按“s”键,然后在图标上的列末尾单击鼠标左按钮,完成标记。图表上显示成对的蓝色箭头,如下图所示:

                                                  向上


                                                      如果要标记下降趋势,请先按‘s’键,在图表上开始标记的列上单击鼠标左键,然后按‘b’键,然后在图表上的列末尾单击鼠标左按钮。标记完成后,将显示成对的红色箭头,如下图所示:

                                                        向下


                                                          贴标输出栏会随时显示贴标动作,非常直观地监控贴标过程,如图所示:

                                                            输出注意:这个部分实际上可以更好地优化,比如增加撤销最后一个动作的功能,然后你可以随时调整标记的位置,也可以避免错误的操作,但我是个懒人,所以…(^o^)


                                                            组织数据并写入文件

                                                              定义变量“Start”和“MqlRates rates[]”以保存趋势的开始时间和数据序列:
                                                                datetime Start;
                                                                MqlRates rates[];
                                                                ArraySetAsSeries(rates, false);
                                                                  注意:1. 这里我们不需要定义结束时间,因为从图表中获得的最后一个时间就是结束时间; 2. “ArraySetAsSeries(rates,false)”函数中的标志被指定为“false”,以确保时间段按顺序连接。
                                                                    当tp=“end”时,我们将数据段写入文件(代码的绿色部分):
                                                                         if(id==CHARTEVENT_CLICK)
                                                                           {
                                                                            //--- definition
                                                                            int x=(int)lparam;
                                                                            int y=(int)dparam;
                                                                            datetime dt    =0;
                                                                            double   price =0;
                                                                            int      window=0;
                                                                            if(ChartXYToTimePrice(0,x,y,window,dt,price))
                                                                              {
                                                                               ObjectCreate(0,Name,ob,window,dt,price);
                                                                               ObjectSetInteger(0,Name,OBJPROP_COLOR,col);
                                                                               //Print("time:",dt,"shift:",iBarShift(Symbol(),PERIOD_CURRENT,dt));
                                                                               if(tp=="start")
                                                                                  Start=dt;
                                                                               else
                                                                                 {
                                                                                  if(file_handle)
                                                                                     file_write(Start,dt);
                                                                                 }
                                                                               ChartRedraw(0);
                                                                              }
                                                                            else
                                                                               Print("ChartXYToTimePrice return error code: ",GetLastError());
                                                                           }

                                                                        通过“CopyRates()”函数获取分段数据,并通过遍历“rates[]”中包含的每条数据添加“trend_group”和“trend_index”列,我们需要在“file_write()”中实现这些功能:
                                                                          void file_write(datetime start,
                                                                                          datetime end)
                                                                            {
                                                                             MqlRates rates[];
                                                                             ArraySetAsSeries(rates,false);
                                                                             int n_cp=CopyRates(Symbol(),PERIOD_CURRENT,start,end,rates);
                                                                             if(n_cp>0)
                                                                               {
                                                                                if(FileTell(file_handle)==2)
                                                                                  {
                                                                                   FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index");
                                                                                   for(int i=0; i<n_cp; i++)
                                                                                     {
                                                                                      FileWrite(file_handle,
                                                                                                rates[i].time,
                                                                                                rates[i].open,
                                                                                                rates[i].high,
                                                                                                rates[i].low,
                                                                                                rates[i].close,
                                                                                                rates[i].tick_volume,
                                                                                                typ,
                                                                                                i);
                                                                                     }
                                                                                  }
                                                                                else
                                                                                  {
                                                                                   for(int i=0; i<n_cp; i++)
                                                                                     {
                                                                                      FileWrite(file_handle,
                                                                                                rates[i].time,
                                                                                                rates[i].open,
                                                                                                rates[i].high,
                                                                                                rates[i].low,
                                                                                                rates[i].close,
                                                                                                rates[i].tick_volume,
                                                                                                typ,
                                                                                                i);
                                                                                     }
                                                                                  }
                                                                               }
                                                                             else
                                                                                Print("No data copied!");
                                                                             FileFlush(file_handle);
                                                                             typ=3;
                                                                            }



                                                                          注意:

                                                                          1. 第一次写入文件时,我们需要写入索引头;

                                                                          2. Trend_group实际上是全局变量“typ”;

                                                                          3. 我们没有在此函数中调用FileClose()函数,因为我们的标记尚未完成。我们将在OnDeinit()函数中调用此函数,将最终结果写入文件。

                                                                          4. 应特别注意此处使用的代码的黄色部分

                                                                          if(FileTell(file_handle)==2)
                                                                          要确定文件中是否有数据(当然,也可以使用其他方法,例如在初始化过程中添加变量为其赋值),如果文件中没有数据,则需要添加这样的头:
                                                                          FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index");
                                                                          如果文件中有数据,则无需添加标头,否则数据将被截断,这一点非常重要
                                                                            所写文件的示例:
                                                                              data_0


                                                                                让我们检查不同数据段之间的一致性,发现数据是完美的:

                                                                                  data_1


                                                                                  附件:完整的EA代码示例

                                                                                  1. 全局变量和常量的定义。参数“start_t”可以由1970年1月1日起的每秒数据定义。当然,它也可以由标准的“datetime”定义,也可以由输入变量“input int start_t=1403037112;”定义,以便在以后运行EA时随时更改:
                                                                                  #define KEY_B     66
                                                                                  #define KEY_S     83
                                                                                  
                                                                                  
                                                                                  int Num= 0;
                                                                                  int typ= 3;
                                                                                  string Name;
                                                                                  string tp;
                                                                                  color col;
                                                                                  bool first= true;
                                                                                  ENUM_OBJECT ob;
                                                                                  int file_handle=0;
                                                                                  int start_t=1403037112;
                                                                                  datetime Start;

                                                                                  注意:当然,您也可以根据个人喜好将按钮定义为输入变量。

                                                                                  input int KEY_B=66;
                                                                                  input int KEY_S=83;

                                                                                  这样做的好处是,如果你觉得按钮不容易使用,你可以在每次执行EA时随意更改按钮,直到你满意为止,我们的代码暂时不会更改。


                                                                                  2. OnInit()函数,在这里我们初始化我们的准备工作:

                                                                                  int OnInit()
                                                                                    {
                                                                                  //---initial
                                                                                     string name;
                                                                                     string reName="1";
                                                                                     int hd=FileFindFirst("*",name,0);
                                                                                     int shift;
                                                                                  
                                                                                     ChartSetInteger(0,CHART_AUTOSCROLL,false);
                                                                                     ChartSetInteger(0,CHART_SHIFT,false);
                                                                                     ChartSetInteger(0,CHART_MOUSE_SCROLL,1);
                                                                                  
                                                                                  
                                                                                     do
                                                                                       {
                                                                                        //---check File
                                                                                        if(StringFind(name,Symbol())!=-1 && StringFind(name,".csv")!=-1)
                                                                                           reName=name;
                                                                                       }
                                                                                     while(FileFindNext(hd,name));
                                                                                  
                                                                                     if(FileIsExist(reName))
                                                                                       {
                                                                                        file_handle = FileOpen(reName,FILE_WRITE|FILE_CSV|FILE_READ);
                                                                                        string a[];
                                                                                        int i=0;
                                                                                        read_csv(file_handle,a);
                                                                                        i = ArraySize(a);
                                                                                        shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)a[i-8]);
                                                                                        ChartNavigate(0,CHART_END,shift);
                                                                                       }
                                                                                     else
                                                                                       {
                                                                                        file_handle = FileOpen(StringFormat("%s%d-%d.csv",Symbol(),Period(),start_t),FILE_WRITE|FILE_CSV|FILE_READ);
                                                                                        Print(FileTell(file_handle));
                                                                                        Print("No history file,create file:",StringFormat("%s%d-%d",Symbol(),Period(),start_t));
                                                                                        shift = -iBarShift(Symbol(),PERIOD_CURRENT,(datetime)start_t);
                                                                                        ChartNavigate(0,CHART_END,shift);
                                                                                        ObjectCreate(0,"Start",OBJ_VLINE,0,(datetime)start_t,0);
                                                                                       }
                                                                                  //---
                                                                                     Print("EA:",MQL5InfoString(MQL5_PROGRAM_NAME),"Working!");
                                                                                  //---
                                                                                     ChartSetInteger(ChartID(),CHART_EVENT_OBJECT_CREATE,true);
                                                                                  //---
                                                                                     ChartSetInteger(ChartID(),CHART_EVENT_OBJECT_DELETE,true);
                                                                                  //---
                                                                                     ChartRedraw(0);
                                                                                  //---
                                                                                     return(INIT_SUCCEEDED);
                                                                                    }


                                                                                  3. 因为我们所有的键盘和鼠标操作都是在图表上完成的,所以我们将主要的逻辑函数放入OnChartEvent()函数中,以实现:

                                                                                  void OnChartEvent(const int id,
                                                                                                    const long &lparam,
                                                                                                    const double &dparam,
                                                                                                    const string &sparam)
                                                                                    {
                                                                                  //Comment(__FUNCTION__,": id=",id," lparam=",lparam," dparam=",dparam," sparam=",sparam);
                                                                                     if(id==CHARTEVENT_KEYDOWN)
                                                                                       {
                                                                                        switch(lparam)
                                                                                          {
                                                                                           case KEY_B:
                                                                                              if(first)
                                                                                                {
                                                                                                 col=clrBlue ;
                                                                                                 typ =0;
                                                                                                 Num+=1;
                                                                                                 tp = "start";
                                                                                                }
                                                                                              else
                                                                                                {
                                                                                                 col=clrRed ;
                                                                                                 typ = 1;
                                                                                                 tp = "end";
                                                                                                }
                                                                                              ob =OBJ_ARROW_BUY;
                                                                                              first = !first;
                                                                                              Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                                                              break;
                                                                                           case KEY_S:
                                                                                              if(first)
                                                                                                {
                                                                                                 col=clrRed ;
                                                                                                 typ =1;
                                                                                                 Num+=1;
                                                                                                 tp = "start";
                                                                                                }
                                                                                              else
                                                                                                {
                                                                                                 col=clrBlue ;
                                                                                                 typ = 0;
                                                                                                 tp = "end";
                                                                                                }
                                                                                              ob =OBJ_ARROW_SELL;
                                                                                              first = !first;
                                                                                              Name = StringFormat("%d-%d-%s",typ,Num,tp);
                                                                                              break;
                                                                                  
                                                                                           default:
                                                                                              Print("You pressed:"+lparam+" key, do nothing!");
                                                                                          }
                                                                                        ChartRedraw(0);
                                                                                       }
                                                                                  //---
                                                                                     if(id==CHARTEVENT_CLICK&&(typ!=3))
                                                                                       {
                                                                                        //--- definition
                                                                                        int x=(int)lparam;
                                                                                        int y=(int)dparam;
                                                                                        datetime dt    =0;
                                                                                        double   price =0;
                                                                                        int      window=0;
                                                                                        if(ChartXYToTimePrice(0,x,y,window,dt,price))
                                                                                          {
                                                                                           ObjectCreate(0,Name,ob,window,dt,price);
                                                                                           ObjectSetInteger(0,Name,OBJPROP_COLOR,col);
                                                                                           //Print("time:",dt,"shift:",iBarShift(Symbol(),PERIOD_CURRENT,dt));
                                                                                           if(tp=="start")
                                                                                              Start=dt;
                                                                                           else
                                                                                             {
                                                                                              if(file_handle)
                                                                                                 file_write(Start,dt);
                                                                                             }
                                                                                           ChartRedraw(0);
                                                                                          }
                                                                                        else
                                                                                           Print("ChartXYToTimePrice return error code: ",GetLastError());
                                                                                       }
                                                                                  //--- object delete
                                                                                     if(id==CHARTEVENT_OBJECT_DELETE)
                                                                                       {
                                                                                        Print("The object with name ",sparam," has been deleted");
                                                                                       }
                                                                                  //--- object create
                                                                                     if(id==CHARTEVENT_OBJECT_CREATE)
                                                                                       {
                                                                                        Print("The object with name ",sparam," has been created!");
                                                                                       }
                                                                                  
                                                                                    }

                                                                                  注意:在实现此函数时,我们更改了上面的代码

                                                                                   if (id==CHARTEVENT_CLICK&&(typ!=3))

                                                                                  我们这样做的原因很简单,我们避免了意外点击鼠标引起的错误操作,并使用“typ”变量来控制鼠标操作是否有效。当我们标记一个趋势时,我们将执行file_write()函数。我们在该函数的末尾添加此行

                                                                                  typ=3;

                                                                                  然后你可以在开始下一段标记之前随意地用鼠标在图表上操作,不需要任何动作,直到你找到合适的位置并准备标记下一个趋势。


                                                                                  4. 写入数据函数 - file_write()的实现:

                                                                                  void file_write(datetime start,
                                                                                                  datetime end)
                                                                                    {
                                                                                     MqlRates rates[];
                                                                                     ArraySetAsSeries(rates,false);
                                                                                     int n_cp=CopyRates(Symbol(),PERIOD_CURRENT,start,end,rates);
                                                                                     if(n_cp>0)
                                                                                       {
                                                                                        if(FileTell(file_handle)==2)
                                                                                          {
                                                                                           FileWrite(file_handle,"time","open","high","low","close","tick_volume","trend_group","trend_index");
                                                                                           for(int i=0; i<n_cp; i++)
                                                                                             {
                                                                                              FileWrite(file_handle,
                                                                                                        rates[i].time,
                                                                                                        rates[i].open,
                                                                                                        rates[i].high,
                                                                                                        rates[i].low,
                                                                                                        rates[i].close,
                                                                                                        rates[i].tick_volume,
                                                                                                        typ,
                                                                                                        i);
                                                                                             }
                                                                                          }
                                                                                        else
                                                                                          {
                                                                                           for(int i=0; i<n_cp; i++)
                                                                                             {
                                                                                              FileWrite(file_handle,
                                                                                                        rates[i].time,
                                                                                                        rates[i].open,
                                                                                                        rates[i].high,
                                                                                                        rates[i].low,
                                                                                                        rates[i].close,
                                                                                                        rates[i].tick_volume,
                                                                                                        typ,
                                                                                                        i);
                                                                                             }
                                                                                          }
                                                                                       }
                                                                                     else
                                                                                        Print("No data copied!");
                                                                                     FileFlush(file_handle);
                                                                                     typ=3;
                                                                                    }

                                                                                  5. 读取文件函数 - read_csv()的实现:
                                                                                  void read_csv(int hd,
                                                                                                string &arry[])
                                                                                    {
                                                                                     int i=0;
                                                                                     while(!FileIsEnding(hd))
                                                                                       {
                                                                                        ArrayResize(arry,i+1);
                                                                                        arry[i]=FileReadString(hd);
                                                                                        i++;
                                                                                       }
                                                                                    }

                                                                                  6. 这里仍然有一个重要的问题没有得到解决,即初始化EA时打开的文件句柄“file_handle”没有被释放。我们在最后一个OnDeinit()函数中释放句柄。当调用函数“FileClose(file_handle)”时,所有数据实际上都会写入csv文件,因此在EA仍在运行时不要试图打开csv文件尤为重要:
                                                                                  void OnDeinit(const int reason)
                                                                                    {
                                                                                     FileClose(file_handle);
                                                                                     Print("Write data!");
                                                                                    }


                                                                                  注意:本文中显示的代码仅用于演示。如果您想在实践中使用它,建议您进一步改进代码。在文章的最后,将提供演示中涉及的CSV文件和最终MQL5文件。本系列的下一篇文章将介绍如何通过与python结合的客户端对数据进行注释。

                                                                                  感谢您的耐心阅读,希望您有所收获,祝您生活愉快,下一章再见!

                                                                                  本文由MetaQuotes Ltd译自英文
                                                                                  原文地址: https://www.mql5.com/en/articles/13225

                                                                                  附加的文件 |
                                                                                  时间序列挖掘的数据标签(第2部分):使用Python制作带有趋势标记的数据集 时间序列挖掘的数据标签(第2部分):使用Python制作带有趋势标记的数据集
                                                                                  本系列文章介绍了几种时间序列标记方法,这些方法可以创建符合大多数人工智能模型的数据,而根据需要进行有针对性的数据标记可以使训练后的人工智能模型更符合预期设计,提高我们模型的准确性,甚至帮助模型实现质的飞跃!
                                                                                  模式搜索的暴力方法(第六部分):循环优化 模式搜索的暴力方法(第六部分):循环优化
                                                                                  在这篇文章中,我将展示改进的第一部分,这些改进不仅使我能够使MetaTrader 4和5交易的整个自动化链闭环,而且还可以做一些更有趣的事情。从现在起,这个解决方案使我能够完全自动化创建EA和优化,并最大限度地降低寻找有效交易配置的劳动力成本。
                                                                                  神经网络变得轻松(第四十九部分):软性扮演者-评价者 神经网络变得轻松(第四十九部分):软性扮演者-评价者
                                                                                  我们继续讨论解决连续动作空间问题的强化学习算法。在本文中,我将讲演软性扮演者-评论者(SAC)算法。SAC 的主要优点是拥有查找最佳策略的能力,不仅令预期回报最大化,而且拥有最大化的动作熵(多样性)。
                                                                                  利用 MQL5 的交互式 GUI 改进您的交易图表(第 II 部分):可移动 GUI(II) 利用 MQL5 的交互式 GUI 改进您的交易图表(第 II 部分):可移动 GUI(II)
                                                                                  依靠我们的以 MQL5 创建可移动 GUI 的深度指南,在您的交易策略和实用程序中解锁动态数据表达的潜力。深入研究面向对象编程的基本原理,并探索如何在同一图表上轻松高效地设计和实现单个或多个可移动 GUI。