下载MetaTrader 5

一个用于通过 Google Chart API 构建图表的库

10 十二月 2013, 07:41
Евгений
0
1 060

简介

Google Chart 允许创建 11 类不同的图表。包括:
  1. 线条图
  2. 柱形图
  3. 散点图
  4. 雷达图
  5. 烛形图
  6. 维恩图
  7. QR 码
  8. 地图
  9. 公式
  10. 图形图
  11. 饼图 

很多文章都详细介绍了相当足够的工作集 - 要获得一个准备就绪的图表,您只需要向 Google 服务器发送一个特别构建的查询。在“使用标准库类和 Google Chart API 创建信息板”一文中实施了一个这样的例子,但这只是此服务能够提供的十分之一。自然地,为了正确构建一个请求,我们需要检查其结构和关键字,因此,在本文中,我们将尝试创建一个类库,使用该类库能让用户快速创建需要的图表,并将其放在图表上,以及依据构建的图表动态更新数据。

必须立即指出,在本文中没有大量的库代码计算,但是,有一份通过 Doxygen 创建的附带证书(详情见“MQL5 代码自动生成文档”一文)。在该文中,您将能够找到库类的公共方法的说明以及有关枚举和在创建图表过程中出现的异常的说明文档。

1. 对获取和显示图表方式的说明

让我们从后面开始。假定我们已经正确构建了请求。现在,您需要将其发送到服务器,将回应记录到一个文件,然后将该文件附加到图表中的一个图形对象以显示图表。为了使用互联网,我们使用在“使用 WinInet.dll 通过互联网在客户端之间交换数据”一文中说明的函数。

这里也有一个需要注意的地方:设计用于显示图像的组件(Picture 和 Graphic 标记)仅采用 BMP 格式的图像,而 Google Chart 仅返回 PNG 或 GIF。因此,您需要转换图像。这通过函数 Convert_PNG () 来进行。获取图表的函数代码看起来如下所示:

bool CDiagram::GetChart()
{
   if(!ObjExist()) {SetUserError(DIAGRAM_ERR_OBJ_NOT_EXIST); return false;}
   string request=CreateRequest();
   //打印(请求);
   if(StringLen(request)>2048) {SetUserError(DIAGRAM_ERR_TOO_LARGE_REQUEST); return false;}
   //尝试建立一个连接
   int rv=InternetAttemptConnect(0);
   if(rv!=0) {SetUserError(DIAGRAM_ERR_INTERNET_CONNECT_FAILED); return false;}
   //初始化结构
   int hInternetSession=InternetOpenW("Microsoft Internet Explorer", 0, "", "", 0);
   if(hInternetSession<=0) {SetUserError(DIAGRAM_ERR_INTERNET_CONNECT_FAILED); return false;}
   //发送请求
   int hURL=InternetOpenUrlW(hInternetSession, request, "", 0, 0, 0);
   if(hURL<=0) SetUserError(DIAGRAM_ERR_INTERNET_CONNECT_FAILED);
   //准备转换路径 
   CString src;
   src.Assign(TerminalInfoString(TERMINAL_PATH));
   src.Append("\MQL5\Files\\"+name+".png");
   src.Replace("\\","\\\\");
   CString dst;
   dst.Assign(TerminalInfoString(TERMINAL_PATH));
   dst.Append("\MQL5\Images\\"+name+".bmp");
   dst.Replace("\\","\\\\");
   DeleteFileW(dst.Str());
   DeleteFileW(src.Str());
   CFileBin chart_file;//记录结果的文件
                       //创建它
   chart_file.Open(name+".png",FILE_BIN|FILE_WRITE);
   //****************************   
   int dwBytesRead[1];// 已记录文档的数目
   char readed[1000];//实际的数据
                    //读取数据, 发送请求后从服务器收到
   while(InternetReadFile(hURL,readed,1000,dwBytesRead))
     {
      if(dwBytesRead[0]<=0) break;//没有数据 - 退出
      chart_file.WriteCharArray(readed,0,dwBytesRead[0]);//把数据记录到文件中
     }
   InternetCloseHandle(hInternetSession);//终止连接
   chart_file.Close();//和文件
   //转换文件
   if(!Convert_PNG(src.Str(), dst.Str())) SetUserError(DIAGRAM_ERR_IMAGE_CONVERSION_FAILED);
   //把文件附加到图形对象
   switch (obj_type)
   {
      case OBJ_BITMAP: 
      {
         ObjectSetString(chart_ID, name, OBJPROP_BMPFILE, name+".bmp"); 
         return true;
      }
      case OBJ_BITMAP_LABEL:
      {
         ObjectSetString(chart_ID, name, OBJPROP_BMPFILE, 0, name+".bmp"); 
         ObjectSetString(chart_ID, name, OBJPROP_BMPFILE, 1, name+".bmp"); 
         return true;
      }
      default: return false;
   }
   //重画图表   
   ChartRedraw();
}
值得指出,在创建 Google Charts 库期间,我大量使用标准库中的类,非常感谢其开发人员。

2. Google Charts 库组件概览

到服务器的请求应如下开头:http://chart.apis.google.com/chart?cht = ,您需要进一步指出图表的类型,之后才是其他参数。除了报头以外,请求由命令及其参数构成。命令之间用符号 "&" 分隔。例如,命令 & Chtt = Title 将图表的标题设置为 "Title"。

那么,让我们开始吧。 

2.1 线条图

这或许是最常用的图表,具有最多的属性。图表的构建通过 CLineXYChart 类来进行。例如:

CLineXYChart chart;
chart.Attach(0, "test diagram");
chart.SetSize(200,200);
double Y[10]={-50, -40, -25, -35, 10, 50, 70, 40, 15, 80};
chart.AddLine(Y, Red, 0, "Test line");
chart.GetChart();

结果,在名为 "Test diagram" 的组件中,您将看到以下图像:

  图 1. 一个构建线条图的简单例子

记住,可以在附加的证书中找到类方法的证书,以下是其使用的一些例子。

通过添加其他轴线和网格线,使图表变得复杂:

//建立一个类的实例
CLineXYChart chart;
//把它附加到之前建立的对象中
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(200,200);
//准备数据
double Y2[10]={-70, -5, 6, 8, 10, 20, 100, 130, 90, 60};
double Y[10]={-50, -40, -25, -35, 10, 50, 70, 40, 15, 80};
//增加线
chart.AddLine(Y, Red, 0, "Test line");
chart.AddLine(Y2, Blue, 0, "Test line 2");
//轴
chart.SetAxis(DIAGRAM_AXIS_BOTTOM|DIAGRAM_AXIS_LEFT, -1, 0, 0, 10, 0);
//和网格
chart.SetGrid();
//获取图表
chart.GetChart();

现在,图表与下图相同。值得指出一个重要的特点 - 应在调用其他方法之后才调用 GetChart () 方法,因为它是接收图表的方法。

  图 2. 一个复杂的线条图例子

然而,这还不够。添加标记、标题、图例和内容:

//建立一个类的实例
CLineXYChart chart;
//把它附加到之前创建的对象中
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(500,300);
//准备数据
double Y2[10]={-70, -5, 6, 8, 10, 20, 100, 130, 90, 60};
double Y[10]={-50, -40, -25, -35, 10, 50, 70, 40, 15, 80};
//增加线
int first_line=chart.AddLine(Y, Red, 0, "Test line");
int second_line=chart.AddLine(Y2, Blue, 0, "Test line 2");
//轴
chart.SetAxis(DIAGRAM_AXIS_BOTTOM|DIAGRAM_AXIS_LEFT, -1, 0, 0, 10, 0);
//网格
chart.SetGrid();
//图例
chart.ShowLegend();
//标题
chart.SetTitle("My Chart", Green, 15);
//填充
chart.SetFill(Linen);
//标注
chart.SetLineMarker(first_line, DIAGRAM_LINE_MARKERS_DIAMOND, BlueViolet, 10);
chart.SetLineMarker(second_line, DIAGRAM_LINE_MARKERS_CROSS, YellowGreen, 15);
//获取图表
chart.GetChart();

对于具体线条特有的属性的配置,我们使用标识符(first_line 和 second_line),之后这些标识符被传递到相应的方法。 

图 3. 一个更加复杂的线条图例子

但即使这样,也还没有结束。可以向线条添加标记,在线条下方以及线条之间的区域添加内容,缩放线条,向副轴添加线条:

//建立一个类的实例
CLineXYChart chart;
//把它附加到之前创建的对象中
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(700,400);
//准备数据
double Y3[10]={1000, 1200, 1800, 1700, 1300, 900, 700, 750, 800, 600};
double X3[10]={2, 4, 5, 6, 10, 15, 17, 20, 21, 23};
double Y2[10]={-70, -5, 6, 8, 10, 20, 100, 130, 90, 60};
double Y[10]={-50, -40, -25, -35, 10, 50, 70, 40, 15, 80};
//增加线
int first_line=chart.AddLine(Y, Red, 0, "Test line");
int second_line=chart.AddLine(Y2, Blue, 0, "Test line 2");
int third_line=chart.AddLine(Y3, X3, Green, 0, "Test line 3");
//主轴
chart.SetAxis(DIAGRAM_AXIS_BOTTOM|DIAGRAM_AXIS_LEFT, -1, 0, 0, 10, 0);
//次要轴 
chart.SetAxis(DIAGRAM_AXIS_RIGHT|DIAGRAM_AXIS_TOP, third_line, 0, Red, 15, 2);
//网格
chart.SetGrid();
//图例
chart.ShowLegend(DIAGRAM_LEGEND_POSITION_BOTTOM_HORIZONTAL);
//标题
chart.SetTitle("My Chart", Green, 15);
//在图表上填充
chart.SetFill(Linen, Silver);
//标注
chart.SetLineMarker(first_line, DIAGRAM_LINE_MARKERS_DIAMOND, BlueViolet, 10);
chart.SetLineMarker(second_line, DIAGRAM_LINE_MARKERS_CROSS, YellowGreen, 15);
//在两线之间填充
chart.SetLineFilling(first_line, Lime , second_line);
//增加标注
chart.AddLabel(first_line, DIAGRAM_LABEL_TYPE_FLAG, 5, "Flag", Red, 15); 
chart.AddLabel(second_line, DIAGRAM_LABELS_TYPE_ANNOTATION, 3, "annotation", Blue, 25);
//稍微压缩一下线 (压缩 20%)
chart.SetLineScaling(second_line, false, 20);
//把第三条线附加到次要轴
chart.SetLineScaling(third_line, true, 20);
//获取图表
chart.GetChart();

  图 4. 线条图的全部特征

如果您要动态更新图表的数据,则可以使用方法 SetLineData (),或者使用 DeleteLine () 完全删除含有旧数据的线条,然后创建新的线条。然而,第一种方法是首选。

2.2 柱形图

这也被称为柱状图。CBarChart 类负责构建。这与 CLineXYChart 不同,因为缺少标记,不需要为 X 轴指定一组数据,没有某些此类对象特有的特征。在其他方面都与 CLineXYChart 相同。

例如:

CBarChart chart;
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(700,400);
//准备数据
double Y3[10]={100, 120, 18, 17, 13, 9, 70, 75, 80, 60};
double Y2[10]={70, 5, 6, 8, 10, 20, 100, 130, 90, 60};
double Y[10]={50, 40, 25, 35, 10, 50, 70, 40, 15, 80};
//增加线
int first_line=chart.AddLine(Y, Red, 0, "Test bar 1");
int second_line=chart.AddLine(Y2, Blue, 0, "Test bar 2");
int third_line=chart.AddLine(Y3, Green, 0, "Test bar 3");
//主轴
chart.SetAxis(DIAGRAM_AXIS_BOTTOM|DIAGRAM_AXIS_LEFT, -1, 0, 0, 10, 0);
//网格
chart.SetGrid();
//图例
chart.ShowLegend();
//标题
chart.SetTitle("My Chart", Green, 15);
//填充图表
chart.SetFill(Linen, Silver);
//获取图表
chart.GetChart(); 

 

图 5. 一个柱形图例子

请注意,我们有三个数据集,并且一根柱位于另一根柱的上方以优化显示。然后,还有其他的方式来排列柱。使用方法 SetGrouped () 来设置:

 // 设置分组  chart.SetGrouped (true);

  图 6. 用另一种方式排列柱的柱形图例子

如您所见,现在柱并不是上下排列,而是按它们的创建顺序排列。

2.3. 饼图

类 CPieChart 构建一个饼图。您可以创建两维和三维图表:

CPieChart chart;
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(300,200);
//增加部分
chart.AddPieSlice(10, 0, Red, 0, "Test slice 1");
chart.AddPieSlice(20, 0, Green, 0, "Test slice 2");
chart.AddPieSlice(30, 0, Blue, 0, "Test slice 3");
//图例
chart.ShowLegend();
//标题
chart.SetTitle("My Chart", Green, 15);
//显示模式 - 2D
chart.SetPieType(true);
//获取图表
chart.GetChart();  

图 7. 两维和三维饼图

图 7. 两维和三维饼图

通过调用 SetPieType () 方法来建立显示类型(2D 或 3D)。另一个有用的功能是能够设置多个环,然而在这种情况下只能使用 2D 模式。要设置第二个环,将 AddPieSlice () 方法的维度参数设置为一个不等于 0 的值:

//增加第二个环
chart.AddPieSlice(50, 1, YellowGreen, 0, "Test ring 1");
chart.AddPieSlice(20, 1, Magenta, 0, "Test ring 2");
chart.AddPieSlice(70, 1, Maroon, 0, "Test ring 3");

  图 8. 同心饼图

注意,上图还包含远程扇形标记。它们通过 SetPieLabels () 方法设定,可以代替图例。但是有一个缺点 - 标记的大小不会自动适应图表的大小,这样可能导致标记溢出图表的边界。在此情况下,您需要增大图表的宽度。此图表类型不提供网格、轴、标记和比例的设置。可以通过 DeleteLine () 方法删除扇形。

2.4. 雷达图

CRadarChart 类构建雷达图。它与 CLineXYChart 类没有区别。其所有方法都可在 CRadarChart 中使用:

CRadarChart chart;
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(300,300);
//增加线
double Y3[10]={100, 120, 18, 17, 13, 9, 70, 75, 80, 60};
double Y2[10]={70, 5, 6, 8, 10, 20, 100, 130, 90, 60};
double Y[10]={50, 40, 25, 35, 10, 50, 70, 40, 15, 80};
int first_line=chart.AddLine(Y, Red, 0, "Test line");
int second_line=chart.AddLine(Y2, Blue, 0, "Test line 2");
int third_line=chart.AddLine(Y3, Green, 0, "Test line 3");
//在两线之间填充
chart.SetLineFilling(first_line, Lime , second_line);
//标注
chart.SetLineMarker(first_line, DIAGRAM_LINE_MARKERS_CIRCLE, BlueViolet, 10);
chart.SetLineMarker(second_line, DIAGRAM_LINE_MARKERS_DIAMOND, YellowGreen, 15);
//标题
chart.SetTitle("My Chart", Green, 15);
//网格
chart.SetGrid();
//图例
chart.ShowLegend(DIAGRAM_LEGEND_POSITION_BOTTOM_HORIZONTAL);
//获取图表
chart.GetChart();

图 9. 雷达图

2.5. 烛形图

CCandleChart 类构建烛形图。可以通过 AddCandles () 方法添加烛形。

CCandleChart chart;
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(300,300);
//在当前图表上增加10个烛形
double Open[10], Close[10], High[10], Low[10];
CopyOpen(Symbol(), PERIOD_CURRENT, 0, 10, Open);
CopyClose(Symbol(), PERIOD_CURRENT, 0, 10, Close);
CopyHigh(Symbol(), PERIOD_CURRENT, 0, 10, High);
CopyLow(Symbol(), PERIOD_CURRENT, 0, 10, Low);
chart.AddCandles(Open, Close, High, Low);
//标题
chart.SetTitle(Symbol(), Green, 15);
//网格
chart.SetGrid();
//主轴
chart.SetAxis(DIAGRAM_AXIS_LEFT, -1, 0, 0, 10, 4);
//获取图表
chart.GetChart();

  图 10. 烛形图

此类图表没有标记、图例和标签。 

2.6 公式

CFormulaChart 类允许您创建公式。公式以 TeX 语言通过行的形式传递给 SetFormulaString () 方法:

CFormulaChart chart;
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(300,75);
//添加公式
chart.SetFormulaString("x=-b\pm\sqrt{b^2-4ac}\over(2a)");
//颜色
chart.SetFormulaColor(Blue);
//标题
chart.GetChart();

图 11. 公式

还可以通过 SetFill () 方法设置内容。不支持其他功能。

2.7 图形图

CGraphChart 类构建该图表。AddNode () 和 AddEdge () 方法向图表添加节点和连接线:

CGraphChart chart;
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(300,220);
//增加节点和边界
int A=chart.AddNode("A");
int B=chart.AddNode("B");
int C=chart.AddNode("C");
int D=chart.AddNode("D");
chart.AddEdge(A,B);
chart.AddEdge(B,C);
chart.AddEdge(C,D);
chart.AddEdge(A,C);
//设置引擎
chart.SetEngine(DIAGRAM_GRAPH_ENGINE_NEATO);
//箭头
chart.SetGraphType(false);
//标题
chart.GetChart();

  

图 11. 图形

SetEngine () 方法设置在图形构建中使用的图形引擎的具体类型。您可以自己试验。DeleteNode () 和 DeleteEdge () 方法从图表删除节点和连接线。 

2.8 维恩图

CVennChart 类构建维恩图。

SetCircleSizes () 方法设置集合的大小,SetCircleColors () 方法设置它们的颜色,SetVennLegend () 方法设置它们的图例,SetIntersections () 设置相交的尺寸:

CVennChart chart;
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(300,220);
//增加集合(设置它们的尺寸)
chart.SetCircleSizes(100, 90, 80);
//颜色
chart.SetCircleColors(Yellow, Lime, Maroon);
//签名
chart.SetVennLegend("EURUSD", "USDJPY", "EURJPY");
//交叉的尺寸
chart.SetIntersections(30,30,30,10);
//图例
chart.ShowLegend();
//标题
chart.SetTitle("Venn", Green, 15);
//标题
chart.GetChart();

图 11. 维恩图

2.9 QR 码 

CQRCode 类允许您创建 QR 码:

CQRCode chart;
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(300,220);
//增加数据
chart.SetData("test data");
//设置编码时的错误更正级别
chart.SetErrCorrection(DIAGRAM_QRCODE_ERROR_CORRECTION_LOW);
//增加编码
chart.SetEncoding(DIAGRAM_QRCODE_ENCODING_UTF_8);
//标题
chart.GetChart();

 图 11. QR 码  

SetData () 方法创建据其创建代码的数据。SetErrCorrection () 和 SetEncoding () 方法分别设置编码期间的错误纠正和编码。

2.10 地图

CMapChart 类将创建一份世界地图或大陆地图,并且能够选择必要的国家或地区:

CMapChart chart;
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(440,220);
//设置区域
chart.SetZoomArea(DIAGRAM_MAP_AREA_AFRICA);
//国家
chart.SetCountries("DZEGMGAOBWNGCFKECGCVSNDJTZGHMZZM");
//颜色
chart.SetColors(White, Red, Blue);
//海蓝色 (第二个参数)
chart.SetFill(Gray, Blue);
//标题
chart.GetChart();

图 11. 非洲地图

必要国家的代码以 ISO 3166-1-alpha-2 格式传递给 SetCountries () 方法。SetZoomArea() 设置地图的大洲,SetColors() 设置国家的颜色。

2.11 散点图

CScatterChart 类构建散点图。与 CLineXYChart 的唯一区别是数据的指定方式。

在这里,为了指定数据,我们使用 AddLineScatter () 方法,在该方法中传递点的坐标和它们的维度:

//建立一个类的实例
CScatterChart chart;
//把它附加到之前创建的对象中
chart.Attach(0, "test diagram");
//设置尺寸
chart.SetSize(300,300);
//准备数据
double Y2[10]={70, 5, 6, 8, 10, 20, 100, 130, 90, 60};
double X2[10]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
double Z2[10]={90, 80, 75, 90, 10, 700, 80, 90, 90, 88};
double Y[10]={50, 40, 25, 35, 10, 50, 70, 40, 105, 80};
double X[10]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
double Z[10]={60, 90, 90, 80, 70, 90, 73, 80, 77, 100};
//增加分散
int first_line=chart.AddLineScatter(Y, X, Z, Red, 0, "scatters 1");
int second_line=chart.AddLineScatter(Y2, X2, Z2, Blue, 0, "scatters 2");
//主轴
chart.SetAxis(DIAGRAM_AXIS_BOTTOM|DIAGRAM_AXIS_LEFT, -1, 0, 0, 10, 0);
//网格
chart.SetGrid();
//图例
chart.ShowLegend(DIAGRAM_LEGEND_POSITION_BOTTOM_HORIZONTAL);
//标题
chart.SetTitle("My Chart", Green, 15);
//填充图表
chart.SetFill(Linen, Silver);
//获取图表
chart.GetChart();

 

 图 11. 散点图

总结

亲爱的读者,我希望这个库能够减少您作为交易者所面临的困难。我愿意补充这一点:使用面向对象的编程将显著简化大型项目的创建,使它们具有更加灵活,更加用户友好的特性。

祝您好运!

附件:


文件名
说明
1
 google_charts.mqh 库,放在 MQL5 \\ Include 内
2
 google_charts_test.mq5 测试脚本,放在 MQL5 \\ Script 内
3
 google_charts_help.zip 含有 Doxygen 生成的库类说明文档的压缩档案
4 Libraries.zip 含有库及其源代码的压缩档案,解压到 MQL5\\Libraries 内(库采用 C++ Builder 编写)

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

附加的文件 |
libraries.rar (159.62 KB)
google_charts.mqh (221.1 KB)
New Bar (新柱)事件处理程序 New Bar (新柱)事件处理程序

MQL5 编程语言处理问题的能力已达到一个全新的水平。即便是那些已有此类解决方案的任务,也因为面向对象编程而进阶到一个更高的水平。本文中,我们会举一个检查图表中新柱的特别简单的例子,而且,它已经转化成为一种相当强大且用途多样的工具。什么工具?到文中找答案吧。

查找错误和记录 查找错误和记录

MetaEditor 5 具备调试功能。但是在编写 MQL5 程序时,您通常都希望不要显示个别的值,而是测试与在线工作期间出现的所有信息。如果日志文件内容庞大,所需信息快速便捷检索自动化的重要性就显而易见了。本文中,我们会研究 MQL5 程序中查找错误的方式以及记录方法。我们也会简单地记录到文件中,并了解一款方便日志查看的简单程序 - LogMon。

生长型神经气:MQL5 中的实施 生长型神经气:MQL5 中的实施

本文会举例说明如何开发一个可以实施名为“生长型神经气” (GNG) 自适应聚类算法的 MQL5 程序。本文针对已研究过语言文档、且已具备一定编程能力和神经信息学基础知识的用户。

MQL5 向导:无需编程即可创建 EA 交易程序 MQL5 向导:无需编程即可创建 EA 交易程序

您想试试不用浪费时间来编程的交易策略吗?利用 MQL5 向导,您只需要选择交易信号的类型,添加追踪仓位和资金管理模块,您的工作就完成了!创建自己的模块实现,或是通过“任务”服务订购 - 再将您的新模块合并到现有模块。