简介
元交易者5可能是一个完全自给自足的产品,没有额外的扩展。MetaTrader 5提供了到经纪人的链接,显示报价,允许我们使用各种指标进行市场分析,当然,也给了交易员进行交易操作的机会。显然,由于MetaTrader 5主要集中于简单的交易,因此它不能——也不应该在技术上是一个绝对通用的工具,用于研究、分析数学方法和创建多媒体内容。
此外,软件产品的过度通用性最终会导致其效率、可靠性和安全性的降低。另一方面,在某些情况下,用户可能需要一些额外的功能,特别是在各个领域具有专业知识和教育背景的交易员。因此,任何额外的功能都可能增强交易平台的吸引力,当然,如果它们以一种非常简单的方式实现,而不牺牲它们的可靠性和安全性。
在本文中,我们将考虑一个这样的补充,它提供了根据从客户那里获得的数据创建和显示图表的机会。
每个程序都必须尽其所能。如果我们遵守这一原则,让我们让MetaTrader 5负责处理经纪人、收集和处理收到的信息,并使用另一个程序以图形方式显示这些信息。
网络浏览器
现在,很难找到没有安装Web浏览器的计算机。长期以来,浏览器不断发展和改进。现代浏览器可靠、稳定,最重要的是免费。考虑到Web浏览器实际上是访问Internet的基本工具,大多数用户都熟悉它,并且在使用它时几乎不会遇到任何困难。
现代浏览器被广泛使用,以至于我们习惯于通过网络浏览器观看视频、听音乐、玩游戏和进行许多其他活动。因此,今天的Web浏览器是以各种格式显示不同类型信息的完美工具。
必须指出,有几种非常流行的Web浏览器:Internet Explorer、Mozilla Firefox、Google Chrome和Opera。在软件实现和用户界面方面,这些浏览器可能存在显著差异。但是,从理论上讲,它们应该完全支持网络中采用的信息交换的基本标准,主要涉及HTML语言标准。
在实践中,尽管开发人员做出了努力,浏览器在实现某些协议或技术方面仍然具有一定的个性。如果我们确定某个浏览器因其特性而不适合我们,我们可以通过在计算机上安装一个或多个其他Web浏览器来轻松解决这个问题。即使是像火狐这样的浏览器爱好者,他们的系统中也至少安装了Internet Explorer。
虽然Web浏览器是作为客户端开发的,以提供与远程服务器的交互,但它们也可以用于显示存储在计算机上的本地信息。例如,查看以前保存在计算机上的网页。浏览器可以显示本地页面而不必访问Internet。
因此,在离线模式下运行的Web浏览器对于扩展MetaTrader 5客户机图形功能的应用程序来说是非常有吸引力的候选者。要使用它,你不需要购买昂贵的浏览器,麻烦和冗长的安装,或学习如何使用新的软件产品。
因此,在本文的后面,我们将讨论使用Web浏览器根据在MetaTrader 5中获得的数据构建图表。
HTML和JavaScript
通过选择Web浏览器作为扩展,让我们为自己设置一个我们将严格遵守的基本原则——创建的HTML页面必须在没有本地或远程Web服务器的情况下显示。也就是说,我们不会在我们的计算机上安装任何服务器软件,并且显示我们的页面不需要访问网络。我们创建的HTML页面只能通过Web浏览器显示,并且应该位于我们的计算机上。这一原则将最小化与访问外部网络可能导致的安全性能下降相关的风险。
使用仅用于信息显示的HTML4,我们可以创建带有表格、格式化文本和图像的网页,但这些机会并不完全令人满意,因为我们的目标是基于从MetaTrader 5收到的数据创建完美的图表。
在大多数情况下,当我们访问不同的网站时,我们在浏览器中看到的内容是使用HTML扩展创建的。一般来说,这些扩展是在服务器端执行的,因此不符合我们的目的。在浏览器端不需要服务器软件的技术,例如Macromedia Flash、JavaScript和Java,可能会引起我们的兴趣。
为了在浏览器端执行Macromedia Flash和Java应用程序,我们需要至少安装额外的插件,而用JavaScript编写的用户程序直接由浏览器执行。所有常见的Web浏览器都有自己的内置JavaScript解释程序。为了避免安装任何其他软件或插件,我们选择javascript。
因此,在下面的文章中,我们将只使用支持MQL5的MetaTrader 5和支持HTML和JavaScript的Web浏览器。不需要其他软件。应该注意,HTML页面只是文本文件。因此,要创建HTML文档,我们可以使用任何文本编辑器。例如,我们可以在metaeditor 5中创建和编辑HTML代码。在撰写本文时,HTML代码编辑是在opera@usb v10.63浏览器中完成的,它允许您编辑页面内容、保存修改过的页面以及预览显示结果。
不熟悉HTML和JavaScript语言的人可能会担心在掌握这些语言时会遇到一些困难。为了方便我们的任务,避免进一步学习HTML和JavaScript,我们将尝试使用基于此技术的现成解决方案。因为,在本文的范围内,我们的目标是只构建图表,我们将使用专门为此目的编写的现成的JavaScript库。
emprise javascript图表是一个相当高级的图形库。也许读者有兴趣通过提供链接来更好地理解它,但是图形库并不是完全免费的。所以让我们来看看免费的库,比如DygraphsJavaScript可视化库和海图图表库。Dygraphs由于其简单而具有吸引力,而HighCharts库包含许多函数,看起来更通用。虽然HighCharts库大约有75KB,并且需要一个额外的jQuery库,大约有70KB,但是我们仍然选择它作为我们的库。
您可以在我们的网站https://www.highcharts.com/上的“演示库”部分熟悉海图库。对于每个示例,您可以通过单击查看选项来查看其javascript源代码。有关库的详细说明,请参阅“文档/选项参考”部分,在这里您还可以找到许多使用不同选项的示例。由于JavaScript代码的丰富性和MQL程序员的不同语法,这个库的使用看起来相当复杂。但事实并非如此。考虑使用库显示图形的简单HTML文件的第一个示例。
例如,让我们创建一个名为test_01的文本文件。在记事本编辑器中,使用库复制以下简单示例。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "https://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Example</title> <!-- - --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" type="text/javascript"></script> <script src="/js/highcharts.js" type="text/javascript"></script> <!-- - --> <script type="text/javascript"> var chart1; $(document).ready(function(){ chart1 = new Highcharts.Chart({ chart: {renderTo: 'container1'}, series: [{data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]}] }); }); </script> <!-- - --> </head> <body> <div id="container1" style="width: 700px; height: 400px "></div> </body> </html>
示例代码由四部分注释。
在第一部分中,代码顶部包含普通的HTML页面标记。目前,我们对这部分代码并不特别感兴趣。
接下来是另一部分,其中包含两个<;script>;标记。在第一个标签中,我们向浏览器发出一条指令来下载库代码jquery。来自Ajax网站的Min.js。谷歌。通用域名格式。第二个标记假定在服务器端,目录/js/包含库HighCharts。浏览器必须下载的JS。以前,已经决定在显示页面时不应连接到任何外部资源,因此我们必须更改这部分代码。
变更后,本节代码如下
<script src="jquery.min.js" type="text/javascript"></script> <script src="highcharts.js" type="text/javascript"></script>
在本例中,我们发出指令,从HTML文件所在的目录(即当前目录)下载这两个库。为了让浏览器下载库,必须首先分别从ajax.googleapis.com和https://www.highcharts.com下载库,然后复制到HTML文件所在的同一目录。这两个库也可以在本文末尾的附录中找到。
海图的对象。图表类是在代码的下一部分中创建的。参数“renderto:’container1’”表示图表将显示在名为“container1”的HTML元素中,参数“data”定义将显示图表中的数据。正如我们在这个例子中看到的,数据的定义方式与参数相同——在创建海图对象的过程中。图表类。通过简单的更改,我们将显示数据的定义放在一个单独的代码部分中,这允许我们在需要显示多个图时对其数据进行分组。
在示例的最后一部分中,标记<;div>;声明了一个名为“container1”的HTML元素,并指示其大小。如前所述,这是将用于构建图表的HTML元素,其大小由标记<;div>中指定的元素“container1”的大小决定。
考虑到所做的更改,我们的示例代码如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "https://www.w3.org/TR/html4/strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Example</title> <!-- - --> <script src="jquery.min.js" type="text/javascript"></script> <script src="highcharts.js" type="text/javascript"></script> <!-- - --> <script type="text/javascript"> var dat1 = [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]; </script> <!-- - --> <script type="text/javascript"> var chart1; $(document).ready(function(){ chart1 = new Highcharts.Chart({ chart: {renderTo: 'container1'}, series: [{data: dat1}] }); }); </script> <!-- - --> </head> <body> <div id="container1" style="width: 700px; height: 400px "></div> </body> </html>
这个测试用例和所有库都可以从文章末尾的附件中复制。test_01.htm示例文件和库文件位于同一/test文件夹中,因此我们只需双击html文件test_01.htm即可查看我们的工作。
必须记住,要正确显示此测试页,应该允许在Web浏览器中执行javascript。因为出于安全目的,浏览器允许您禁用此选项,此选项可能已关闭。这样,我们可以看到下图:
图1。Test01HTM&NBSP;
这是我们的第一个测试图,尽管技术看起来很复杂,但创建它并不需要很长时间。
我们应该指出以这种方式创建的显示图表的一些特性。在重复的目录中,打开文件test_01.htm,如果Web浏览器允许您放大,您会发现即使您放大多次,图表的质量也不会下降。
这是因为此图表不是静态图像(如PNG或JPEG文件),在放大或缩小为其绘图分配的区域后,它将被重新绘制。因此,这样的图像不能像我们通常希望保存的那样保存到磁盘上。因为图表是用javascript创建的,所以我们必须指出,不同的浏览器都有自己的语言内置解释程序,并且可能并不总是以相同的方式执行程序。
在不同浏览器中查看时,使用javascript创建的图表可能有所不同。在大多数情况下,这些差异在Internet Explorer中比在其他浏览器中更常见。
但是我们希望JavaScript库的创建者能够考虑让他们的代码尽可能与最流行的Web浏览器兼容。
MetaTrader 5和MQL 5
在上面的示例中,要在图表中显示的数据是在创建HTML页面期间手动设置的。为了安排数据从MetaTrader 5传输到创建的图形,我们使用最简单的方法。让MetaTrader 5将数据记录到单独的文件中,然后在显示图表时从该文件将数据加载到浏览器中。让我们编写一个包含HTML页面的示例,该页面通过从文件下载数据和在MQL5中创建文件的脚本来显示图表。
对于HTML文件,我们将使用以前创建的文件test_01.htm(先进行一些小的更改)。我们将修改后的文件称为示例1。HTM。由于以下代码行,所做的更改将减少:
<script type="text/javascript"> var dat1 = [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]; </script>
将被替换为
<script type="text/javascript"> var dat1=[0]; </script> <script src="exdat.txt" type="text/javascript"></script>
现在,当浏览器下载HTML页面时,它们还需要下载exdat。TXT文本文件,其中要在图表中显示的值被赋予一个DAT1数组。这个文件应该包含一段javascript代码。您可以使用相应的脚本在MetaTrader 5中轻松创建此文件。
下面提供了这样一个脚本的示例。
//+------------------------------------------------------------------+ //| Example1.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { int i,n,fhandle; double gr[25]; string str; n=ArraySize(gr); for(i=0;i<n;i++) { gr[i]=NormalizeDouble(MathSin(i*3*2*M_PI/n),4); } str=DoubleToString(gr[0],4); for(i=1;i<n;i++) { str+=","+DoubleToString(gr[i],4); } ResetLastError(); fhandle=FileOpen("exdat.txt",FILE_WRITE|FILE_TXT|FILE_ANSI); if(fhandle<0){Print("File open failed, error ",GetLastError());return;} FileWriteString(fhandle,"dat1=["+str+"];/n"); FileClose(fhandle); } //+------------------------------------------------------------------+
要存储显示的数据,此脚本使用包含25个元素的gr[]数组。例如,这个数组中填充了正弦函数的值,并四舍五入到小数点后四位。当然,这个数组也可以用任何其他更有用的数据填充。
此外,数据被格式化并组合成一个文本字符串。为了减小生成的文本文件的大小,字符串中只会精确地将gr[]数组元素的值放置到四位十进制数字。为此,我们使用doubletoString()函数。
组合文本字符串str后,它将存储在exdat.txt文件中。如果成功执行此脚本,将在客户机的/mql5/file s子文件夹中创建一个texdat.txt文本文件;如果该文件已存在,则将覆盖该文件。
本文末尾的附件部分提供jquery.min.js、highcharts.js、example1.mq5、example1.htm和exdat.txt文件。这五个文件位于目录/example1中。要简单地查看结果,只需复制示例并打开文件example1。目录/example1中的HTM。图表将基于exdat.txt文件中的数据创建。
和NBSP;
图2。例1。HTM
当然,要运行example1.mq5脚本,它应该位于客户机的/mql5/script s文件夹中并进行编译。
如前所述,在启动脚本之后,exdat。txt文件将在/mql5/files文件夹中创建,但在我们的示例中,HTML文件、库中的文件和数据文件都必须位于同一文件夹中。因此,我们必须复制jquery文件。最小JS,高图表。JS和示例1。HTM到/mql5/files文件夹或exdat。将txt文件保存到这些文件所在的文件夹中。
在这种情况下,HTML页面和清单存储在不同的文件中。在设计阶段,将项目的不同部分放在不同的文件中可能很有用。例如,这有助于避免在编辑HTML文件时随机更改库代码。但是,在编辑了HTML页面并且没有做进一步的更改之后,库将直接集成到HTML代码文件中。
这是可能的,因为javascript库是简单的文本文件。如果我们打开jquery。最小JS或高图表。JS有了文本编辑器,我们将看不到任何可以理解的内容,因为库的源代码被尽可能压缩。
压缩是通过删除服务符号来完成的,例如换行符或一系列空格。在这种压缩之后,任何格式都会丢失,但文本仍然是文本,因为文件类型没有更改。因此,无论浏览器是从扩展名为.js的外部文件连接库代码,还是从当前HTML文件读取文本格式的库代码,两者都没有区别。
要组合文件,请执行示例1中的代码行。HTM
<script src="jquery.min.js" type="text/javascript"></script> <script src="highcharts.js" type="text/javascript"></script>
替换为
<script type="text/javascript"> </script>
接下来,使用文本编辑器(如记事本)在库jquery中打开该文件。min.js,然后通过选择命令“全选”(完全选择)来复制文件的内容。接下来,打开example1.htm文件,在标记<;script type=/“text/javascript/”>;和<;/script>;之间粘贴复制库文本。将获取的文件保存为示例2。HTM。以同样的方式,复制库HighCharts的内容。JS到该文件,并将其放在以前复制的库文本和标记<;/script>;之间。
由于复制,HTML文件的大小会增加,但现在我们不需要分离库文件来正确显示它们。在文件夹/example2中包含exdat.txt数据文件就足够了。这个文件夹包含example2.htm文件,exdat.txt在本文末尾的附件部分提供。
图形格式的事务历史记录报告
为了更完整地描述显示图形信息的建议方法,我们将创建一个报告,以指定的间隔显示交易账户历史记录。在MetaTrader 5的History选项卡的上下文菜单中选择Report命令时创建的基于HTML的报告将用作原型。此报表包含大量不同的功能,汇总在表中。假设这些功能在图形格式中更直观,那么让我们使用海图。显示它们的JS图形库。
在上面的示例中,为了构建图表,我们使用了这个版本的HighCharts中设置的默认显示参数。JS库。
出于实际目的,此选项将不会成功,因为在每种情况下,我们都必须调整图表的显示以满足个别的特定要求。为了这个目的,海图。JS库提供了各种各样的机会,有各种各样的选项可以应用于图表。如前所述,可在https://www.highcharts.com上找到选项列表及其详细说明和示例。
我们将毫不犹豫地详细描述图形库选项及其应用程序细节,因为本文的主要目的是建议并演示使用Web浏览器显示从MetaTrader 5收到的信息的能力。特别是因为其他的javascript库也可以根据创建网页的具体要求来使用。读者可以自主选择最适合的图书馆,并根据实际需要尽可能深入地研究。
为了显示交易账户的历史记录,我们创建了profitreport。HTM文件。文件可以在附件中找到。/报告文件夹包含data.txt文件,其中包含要显示的数据。文件数据。TXT作为一个例子放在这个文件夹中。
当我们复制/report文件夹并打开profitreport时。HTM,我们将以图形格式看到为这个示例创建的测试帐户的事务特征。
图3。利润报告。HTM
当我们创建profitreport时。首先,我们对页面进行了总体布局,并确定了可能定位的信息类型。
然后我们将带有默认选项的图表放在页面上。
创建此模板后,我们分别为每个图表选择最合适的选项。编辑后,我们只需将库文本复制到页面。如前所述,为了正确显示页面,它应该与文件数据位于同一目录中。包含要显示的数据的TXT。
文件data.txt是使用profitreport.mq5脚本在metatrader 5中创建的。如果成功执行此脚本,将在/mql5/files文件夹中创建包含当前活动帐户的事务特征的data.txt文件。
必须记住,脚本应放在/mql5/scripts文件夹中并进行编译。
//----------------------------------------------------------------------------------- // ProfitReport.mq5 // Copyright 2011, MetaQuotes Software Corp. // https://www.mql5.com //----------------------------------------------------------------------------------- #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs #include <Arrays/ArrayLong.mqh> #include <Arrays/ArrayDouble.mqh> #include <Arrays/ArrayString.mqh> #include <Arrays/ArrayInt.mqh> //--- input parameters input int nD=30; // Number of days //--- global double balabce_cur=0; // balance double initbalance_cur=0; // Initial balance (not including deposits to the account) int days_num; // number of days in the report (including the current day) datetime tfrom_tim; // Date from datetime tend_tim; // Date to double netprofit_cur=0; // Total Net Profit double grossprofit_cur=0; // Gross Profit double grossloss_cur=0; // Gross Loss int totaltrades_num=0; // Total Trades int longtrades_num=0; // Number of Long Trades double longtrades_perc=0; // % of Long Trades int shorttrades_num=0; // Number of Short Trades double shorttrades_perc=0; // % of Short Trades int proftrad_num=0; // Number of All Profit Trades double proftrad_perc=0; // % of All Profit Trades int losstrad_num=0; // Number of All Loss Trades double losstrad_perc=0; // % of All Loss Trades int shortprof_num=0; // Number of Short Profit Trades double shortprof_perc=0; // % of Short Profit Trades double shortloss_perc=0; // % of Short Loss Trades int longprof_num=0; // Number of Long Profit Trades double longprof_perc=0; // % of Long Profit Trades double longloss_perc=0; // % of Long Loss Trades int maxconswins_num=0; // Number of Maximum consecutive wins double maxconswins_cur=0; // Maximum consecutive wins ($) int maxconsloss_num=0; // Number of Maximum consecutive losses double maxconsloss_cur=0; // Maximum consecutive losses ($) int aveconswins_num=0; // Number of Average consecutive wins double aveconswins_cur=0; // Average consecutive wins ($) int aveconsloss_num=0; // Number of Average consecutive losses double aveconsloss_cur=0; // Average consecutive losses ($) double largproftrad_cur=0; // Largest profit trade double averproftrad_cur=0; // Average profit trade double larglosstrad_cur=0; // Largest loss trade double averlosstrad_cur=0; // Average loss trade double profitfactor=0; // Profit Factor double expectpayoff=0; // Expected Payoff double recovfactor=0; // Recovery Factor double sharperatio=0; // Sharpe Ratio double ddownabs_cur=0; // Balance Drawdown Absolute double ddownmax_cur=0; // Balance Drawdown Maximal double ddownmax_perc=0; // % of Balance Drawdown Maximal int symbols_num=0; // Numbre of Symbols string Band=""; double Probab[33],Normal[33]; CArrayLong TimTrad; CArrayDouble ValTrad; CArrayString SymNam; CArrayInt nSymb; //----------------------------------------------------------------------------------- // Script program start function //----------------------------------------------------------------------------------- void OnStart() { int i,n,m,k,nwins=0,nloss=0,naverw=0,naverl=0,nw=0,nl=0; double bal,sum,val,p,stdev,vwins=0,vloss=0,averwin=0,averlos=0,pmax=0; MqlDateTime dt; datetime ttmp,it; string symb,br; ulong ticket; long dtype,entry; if(!TerminalInfoInteger(TERMINAL_CONNECTED)){printf("Terminal not connected.");return;} days_num=nD; if(days_num<1)days_num=1; // number of days in the report (including the current day) tend_tim=TimeCurrent(); // date to tfrom_tim=tend_tim-(days_num-1)*86400; TimeToStruct(tfrom_tim,dt); dt.sec=0; dt.min=0; dt.hour=0; tfrom_tim=StructToTime(dt); // date from //---------------------------------------- Bands ttmp=tfrom_tim; br=""; if(dt.day_of_week==6||dt.day_of_week==0) { Band+=(string)(ulong)(ttmp*1000)+","; br=",";ttmp+=86400; } for(it=ttmp;it<tend_tim;it+=86400) { TimeToStruct(it,dt); if(dt.day_of_week==6){Band+=br+(string)(ulong)(it*1000)+","; br=",";} if(dt.day_of_week==1&&br==",") Band+=(string)(ulong)(it*1000); } if(dt.day_of_week==6||dt.day_of_week==0) Band+=(string)(ulong)(tend_tim*1000); //---------------------------------------- balabce_cur=AccountInfoDouble(ACCOUNT_BALANCE); // Balance if(!HistorySelect(tfrom_tim,tend_tim)){Print("HistorySelect failed");return;} n=HistoryDealsTotal(); // Number of Deals for(i=0;i<n;i++) { ticket=HistoryDealGetTicket(i); entry=HistoryDealGetInteger(ticket,DEAL_ENTRY); if(ticket>=0&&(entry==DEAL_ENTRY_OUT||entry==DEAL_ENTRY_INOUT)) { dtype=HistoryDealGetInteger(ticket,DEAL_TYPE); if(dtype==DEAL_TYPE_BUY||dtype==DEAL_TYPE_SELL) { totaltrades_num++; // Total Trades val=HistoryDealGetDouble(ticket,DEAL_PROFIT); val+=HistoryDealGetDouble(ticket,DEAL_COMMISSION); val+=HistoryDealGetDouble(ticket,DEAL_SWAP); netprofit_cur+=val; // Total Net Profit if(-netprofit_cur>ddownabs_cur)ddownabs_cur=-netprofit_cur; // Balance Drawdown Absolute if(netprofit_cur>pmax)pmax=netprofit_cur; p=pmax-netprofit_cur; if(p>ddownmax_cur) { ddownmax_cur=p; // Balance Drawdown Maximal ddownmax_perc=pmax; } if(val>=0) //win { grossprofit_cur+=val; // Gross Profit proftrad_num++; // Number of Profit Trades if(val>largproftrad_cur)largproftrad_cur=val; // Largest profit trade nwins++;vwins+=val; if(nwins>=maxconswins_num) { maxconswins_num=nwins; if(vwins>maxconswins_cur)maxconswins_cur=vwins; } if(vloss>0){averlos+=vloss; nl+=nloss; naverl++;} nloss=0;vloss=0; } else //loss { grossloss_cur-=val; // Gross Loss if(-val>larglosstrad_cur)larglosstrad_cur=-val; // Largest loss trade nloss++;vloss-=val; if(nloss>=maxconsloss_num) { maxconsloss_num=nloss; if(vloss>maxconsloss_cur)maxconsloss_cur=vloss; } if(vwins>0){averwin+=vwins; nw+=nwins; naverw++;} nwins=0;vwins=0; } if(dtype==DEAL_TYPE_SELL) { longtrades_num++; // Number of Long Trades if(val>=0)longprof_num++; // Number of Long Profit Trades } else if(val>=0)shortprof_num++; // Number of Short Profit Trades symb=HistoryDealGetString(ticket,DEAL_SYMBOL); // Symbols k=1; for(m=0;m<SymNam.Total();m++) { if(SymNam.At(m)==symb) { k=0; nSymb.Update(m,nSymb.At(m)+1); } } if(k==1) { SymNam.Add(symb); nSymb.Add(1); } ValTrad.Add(val); TimTrad.Add(HistoryDealGetInteger(ticket,DEAL_TIME)); } } } if(vloss>0){averlos+=vloss; nl+=nloss; naverl++;} if(vwins>0){averwin+=vwins; nw+=nwins; naverw++;} initbalance_cur=balabce_cur-netprofit_cur; if(totaltrades_num>0) { longtrades_perc=NormalizeDouble((double)longtrades_num/totaltrades_num*100,1); // % of Long Trades shorttrades_num=totaltrades_num-longtrades_num; // Number of Short Trades shorttrades_perc=100-longtrades_perc; // % of Short Trades proftrad_perc=NormalizeDouble((double)proftrad_num/totaltrades_num*100,1); // % of Profit Trades losstrad_num=totaltrades_num-proftrad_num; // Number of Loss Trades losstrad_perc=100-proftrad_perc; // % of All Loss Trades if(shorttrades_num>0) { shortprof_perc=NormalizeDouble((double)shortprof_num/shorttrades_num*100,1); // % of Short Profit Trades shortloss_perc=100-shortprof_perc; // % of Short Loss Trades } if(longtrades_num>0) { longprof_perc=NormalizeDouble((double)longprof_num/longtrades_num*100,1); // % of Long Profit Trades longloss_perc=100-longprof_perc; // % of Long Loss Trades } if(grossloss_cur>0)profitfactor=NormalizeDouble(grossprofit_cur/grossloss_cur,2); // Profit Factor if(proftrad_num>0)averproftrad_cur=NormalizeDouble(grossprofit_cur/proftrad_num,2);// Average profit trade if(losstrad_num>0)averlosstrad_cur=NormalizeDouble(grossloss_cur/losstrad_num,2); // Average loss trade if(naverw>0) { aveconswins_num=(int)NormalizeDouble((double)nw/naverw,0); aveconswins_cur=NormalizeDouble(averwin/naverw,2); } if(naverl>0) { aveconsloss_num=(int)NormalizeDouble((double)nl/naverl,0); aveconsloss_cur=NormalizeDouble(averlos/naverl,2); } p=initbalance_cur+ddownmax_perc; if(p!=0) { ddownmax_perc=NormalizeDouble(ddownmax_cur/p*100,1); // % of Balance Drawdown Maximal } if(ddownmax_cur>0)recovfactor=NormalizeDouble(netprofit_cur/ddownmax_cur,2); // Recovery Factor expectpayoff=netprofit_cur/totaltrades_num; // Expected Payoff sum=0; val=balabce_cur; for(m=ValTrad.Total()-1;m>=0;m--) { bal=val-ValTrad.At(m); p=val/bal; sum+=p; val=bal; } sum=sum/ValTrad.Total(); stdev=0; val=balabce_cur; for(m=ValTrad.Total()-1;m>=0;m--) { bal=val-ValTrad.At(m); p=val/bal-sum; stdev+=p*p; val=bal; } stdev=MathSqrt(stdev/ValTrad.Total()); if(stdev>0)sharperatio=NormalizeDouble((sum-1)/stdev,2); // Sharpe Ratio stdev=0; for(m=0;m<ValTrad.Total();m++) { p=ValTrad.At(m)-expectpayoff; stdev+=p*p; } stdev=MathSqrt(stdev/ValTrad.Total()); // Standard deviation if(stdev>0) { ArrayInitialize(Probab,0.0); for(m=0;m<ValTrad.Total();m++) // Histogram { i=16+(int)NormalizeDouble((ValTrad.At(m)-expectpayoff)/stdev,0); if(i>=0 && i<ArraySize(Probab))Probab[i]++; } for(m=0;m<ArraySize(Probab);m++) Probab[m]=NormalizeDouble(Probab[m]/totaltrades_num,5); } expectpayoff=NormalizeDouble(expectpayoff,2); // Expected Payoff k=0; symbols_num=SymNam.Total(); // Symbols for(m=0;m<(6-symbols_num);m++) { if(k==0) { k=1; SymNam.Insert("",0); nSymb.Insert(0,0); } else { k=1; SymNam.Add(""); nSymb.Add(0); } } } p=1.0/MathSqrt(2*M_PI)/4.0; for(m=0;m<ArraySize(Normal);m++) // Normal distribution { val=(double)m/4.0-4; Normal[m]=NormalizeDouble(p*MathExp(-val*val/2),5); } filesave(); } //----------------------------------------------------------------------------------- // Save file //----------------------------------------------------------------------------------- void filesave() { int n,fhandle; string loginame,str="",br=""; double sum; ResetLastError(); fhandle=FileOpen("data.txt",FILE_WRITE|FILE_TXT|FILE_ANSI); if(fhandle<0){Print("File open failed, error ",GetLastError());return;} loginame="/""+(string)AccountInfoInteger(ACCOUNT_LOGIN)+", "+ TerminalInfoString(TERMINAL_COMPANY)+"/""; str+="var PName="+loginame+";/n"; str+="var Currency=/""+AccountInfoString(ACCOUNT_CURRENCY)+"/";/n"; str+="var Balance="+(string)balabce_cur+";/n"; str+="var IniBalance="+(string)initbalance_cur+";/n"; str+="var nDays="+(string)days_num+";/n"; str+="var T1="+(string)(ulong)(tfrom_tim*1000)+";/n"; str+="var T2="+(string)(ulong)(tend_tim*1000)+";/n"; str+="var NetProf="+DoubleToString(netprofit_cur,2)+";/n"; str+="var GrossProf="+DoubleToString(grossprofit_cur,2)+";/n"; str+="var GrossLoss="+DoubleToString(grossloss_cur,2)+";/n"; str+="var TotalTrad="+(string)totaltrades_num+";/n"; str+="var NProfTrad="+(string)proftrad_num+";/n"; str+="var ProfTrad="+DoubleToString(proftrad_perc,1)+";/n"; str+="var NLossTrad="+(string)losstrad_num+";/n"; str+="var LossTrad="+DoubleToString(losstrad_perc,1)+";/n"; str+="var NLongTrad="+(string)longtrades_num+";/n"; str+="var LongTrad="+DoubleToString(longtrades_perc,1)+";/n"; str+="var NShortTrad="+(string)shorttrades_num+";/n"; str+="var ShortTrad="+DoubleToString(shorttrades_perc,1)+";/n"; str+="var ProfLong ="+DoubleToString(longprof_perc,1)+";/n"; str+="var LossLong ="+DoubleToString(longloss_perc,1)+";/n"; FileWriteString(fhandle,str); str=""; str+="var ProfShort="+DoubleToString(shortprof_perc,1)+";/n"; str+="var LossShort="+DoubleToString(shortloss_perc,1)+";/n"; str+="var ProfFact="+DoubleToString(profitfactor,2)+";/n"; str+="var LargProfTrad="+DoubleToString(largproftrad_cur,2)+";/n"; str+="var AverProfTrad="+DoubleToString(averproftrad_cur,2)+";/n"; str+="var LargLosTrad="+DoubleToString(larglosstrad_cur,2)+";/n"; str+="var AverLosTrad="+DoubleToString(averlosstrad_cur,2)+";/n"; str+="var NMaxConsWin="+(string)maxconswins_num+";/n"; str+="var MaxConsWin="+DoubleToString(maxconswins_cur,2)+";/n"; str+="var NMaxConsLos="+(string)maxconsloss_num+";/n"; str+="var MaxConsLos="+DoubleToString(maxconsloss_cur,2)+";/n"; str+="var NAveConsWin="+(string)aveconswins_num+";/n"; str+="var AveConsWin="+DoubleToString(aveconswins_cur,2)+";/n"; str+="var NAveConsLos="+(string)aveconsloss_num+";/n"; str+="var AveConsLos="+DoubleToString(aveconsloss_cur,2)+";/n"; str+="var ExpPayoff="+DoubleToString(expectpayoff,2)+";/n"; str+="var AbsDD="+DoubleToString(ddownabs_cur,2)+";/n"; str+="var MaxDD="+DoubleToString(ddownmax_cur,2)+";/n"; str+="var RelDD="+DoubleToString(ddownmax_perc,1)+";/n"; str+="var RecFact="+DoubleToString(recovfactor,2)+";/n"; str+="var Sharpe="+DoubleToString(sharperatio,2)+";/n"; str+="var nSymbols="+(string)symbols_num+";/n"; FileWriteString(fhandle,str); str="";br=""; for(n=0;n<ArraySize(Normal);n++) { str+=br+"["+DoubleToString(((double)n-16)/4.0,2)+","+DoubleToString(Normal[n],5)+"]"; br=","; } FileWriteString(fhandle,"var Normal=["+str+"];/n"); str=""; str="[-4.25,0]"; for(n=0;n<ArraySize(Probab);n++) { if(Probab[n]>0) { str+=",["+DoubleToString(((double)n-16)/4.0,2)+","+DoubleToString(Probab[n],5)+"]"; } } str+=",[4.25,0]"; FileWriteString(fhandle,"var Probab=["+str+"];/n"); str=""; sum=0; if(ValTrad.Total()>0) { sum+=ValTrad.At(0); str+="["+(string)(ulong)(TimTrad.At(0)*1000)+","+DoubleToString(sum,2)+"]"; for(n=1;n<ValTrad.Total();n++) { sum+=ValTrad.At(n); str+=",["+(string)(ulong)(TimTrad.At(n)*1000)+","+DoubleToString(sum,2)+"]"; } } FileWriteString(fhandle,"var Prof=["+str+"];/n"); FileWriteString(fhandle,"var Band=["+Band+"];/n"); str="";br=""; for(n=0;n<SymNam.Total();n++) { str+=br+"{name:/'"+SymNam.At(n)+"/',data:["+(string)nSymb.At(n)+"]}"; br=","; } FileWriteString(fhandle,"var Sym=["+str+"];/n"); FileClose(fhandle); }
如我们所见,脚本代码有点麻烦,但不是因为任务的复杂性,而是因为需要确定其值的大量事务特征。为了存储这些值,脚本首先声明全局变量并提供相应的注释。
onStart()函数验证客户端是否已连接到事务服务器,如果没有,脚本将结束其工作。当没有连接到服务器时,我们无法定义活动帐户并获取相关信息。
下一步是计算日期,报表将包含从该日期算起的当前活动帐户的交易数据。对于结束日期,我们使用当前日期和执行脚本时的当前时间的值。可以通过在加载脚本时更改输入参数“天数”来设置报告中包含的天数。此参数的默认值为30天。一旦定义了报告的开始和结束时间,我们就在字符串变量带中定义了一对与周末对应的时间值。该信息用于资产负债表,周六和周日对应的时间间隔可以标为黄色。
接下来,我们使用HistorySelect()函数以指定的时间间隔获取事务和订单历史记录,并通过调用HistoryDealStotal()函数来确定历史记录中的事务数。然后,根据事务数安排一个周期,收集计算事务特征所需的统计数据,并在周期结束时确定其值。
当我们创建脚本时,我们的任务是根据MetaTrader 5中生成的报告保留事务特征的含义。假定脚本计算的特性必须与客户机帮助文件中给出的描述一致。
有关访问帐户历史记录和交易功能计算的信息,请参阅以下文章:
- 元交易员5的订单、头寸和交易;
- 使用标准库类和谷歌图表API创建信息板;
- 专家测试报告中数字的含义是什么?
- 交易数学:如何估计交易结果。
大多数特征计算都很简单,因此本文不考虑与每个特征计算相关的操作,只考虑与标准报表和补充报表存在的差异。
在客户生成的报告中,余额表是通过依次显示每个更改的值来构建的,X比例反映了这些更改的数量。在我们的示例中,我们使用时间刻度来构建图表。
因此,利润表与客户生成的图表非常不同。为了实时显示关闭时间,我们选择此图表构建选项。因此,我们可以看到报告期内交易活动何时会增加或减少。
在构建图表时,必须记住,从1970年1月1日起,MQL5以秒为单位作为日期值运行,图像库要求从1970年1月1日起以毫秒为单位表示。因此,脚本中收到的日期值必须乘以1000才能正确显示。
为了在关闭仓库时存储利润和时间值,脚本使用标准库中的array double和array long类。每次在循环中检测到事务时,请使用add()方法将其信息添加到元素中,然后将元素添加到数组的末尾。这使我们能够避免预先确定所需元素的数量。数组的大小随事务历史记录中找到的事务数的增加而增加。
对于每个事务,将检查要执行的事务类型,同时保留事务类型的名称和对其执行的事务数。与损益表一样,在查看历史记录时,通过将其记录在要添加到数组末尾的元素中,可以累积这些数据。为了存储事务的名称和数量,我们在标准库中使用carraystring和arrayint类。
如果对各种事务执行事务,则图表中的列太宽。为了避免这种情况,数据数组始终至少包含七个元素。图中没有显示未使用的元素,因为它们的值为零,这不会使列太宽。为了确保当交易的项目数量很小时,列可以位于x轴的中间,数组中不相关的元素按顺序插入到数组的开头或结尾。
与标准报告的下一个区别是,尝试为每个交易的利润值序列构建概率分布图。
图4。概率密度
在大多数情况下,这样的图用柱状图表示。在我们的示例中,我们基于这些柱状图的现有列值构造一条样条曲线,以创建概率分布图。计算出的概率密度值补充了图表两边的零值。这是必要的,这样基于样条曲线的图的构造就不会在最后一个已知值处中断,并且会在图之外继续到零。
为了进行比较,在概率密度图中,正态分布以灰色突出显示,并通过读取等于1的和进行归一化,就像用直方图值构造的图一样。在提供的样本报告中,交易数量不足以提供对盈利交易价值概率分布的某种可靠估计。我们可以假设,当历史上有很多交易时,这个图表看起来更可信。
计算完所有事务特征后,在脚本末尾调用filesave()函数。此函数打开数据。以文本格式存储变量名和值的TXT文件。这些变量的值对应于计算的参数,它们的名称对应于将参数传输到图形库中的函数期间HTML文件中使用的名称。
为了减少写入文件时的磁盘访问次数,短代码行在被记录到文件中之前被合并为长代码行。作为metatrader 5中的约定,data.txt文件在mql5/files目录中创建;如果它已经存在,它将被覆盖。为了方便起见,您可以复制profitreport。HTM文件到该目录并从该位置运行。
在MetaTrader 5客户机中,当报告以HTML格式保存时,报告将自动在注册为默认值的浏览器中打开。本文提供的示例中没有实现这种可能性。
要添加自动播放,请在profitreport.mq5脚本的开头插入以下行。
#import "shell32.dll" int ShellExecuteW(int hwnd,string lpOperation,string lpFile,string lpParameters, string lpDirectory,int nShowCmd); #import
最后,在调用filesave()函数后,添加
string path=TerminalInfoString(TERMINAL_DATA_PATH)+"//MQL5//Files//ProfitReport.htm"; ShellExecuteW(NULL,"open",path,NULL,NULL,1);
如果文件profitreport。HTM存在于指定的变量路径中,当调用函数ShellExecutew()时,浏览器将打开该文件。函数shellexecutew()位于shell32.dll系统库中,此函数的声明将添加到文件的开头以提供访问权限。
总结
Web浏览器的使用允许我们同时显示许多不同的信息,这对于组织直观地控制在客户机中运行的EA事务的各个模块的内部状态非常有用。
方便同时显示资金管理数据、交易信号、跟踪止损等模块数据。如果需要显示大量信息,可以使用多页HTML报告。
应该注意的是,JavaScript语言比绘图功能强大得多。使用这种语言,我们可以创建真正的交互式网页。您可以在Internet上的网页中找到许多现成的javascript代码,以及使用该语言的各种示例。
例如,如果在客户端和浏览器之间组织了双向数据事务,则可以直接从浏览器窗口管理客户端。
我们希望本文中描述的方法会有所帮助。
文件
js lib.zip和jquery.min.js
libriestest.zip-highcharts.js、jquery.min.js和test_01.htm
example1.zip-highcharts.js、jquery.min.js和test_01.htm
example1.zip-highcharts.js、jquery.min.js、example1.htm、example1.mq5和exdat.txt
example2.zip-example2.htm和exdat.txT
report.zip-profitreport.htm和data.txt
profitreport.mq5-用于收集统计数据和创建data.txt文件的脚本
本文由MetaQuotes Software Corp.翻译自俄语原文
,网址为https://www.mql5.com/ru/articles/244。
MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!
免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。