小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

利用 HTML 報告分析交易結(jié)果

 Levy_X 2019-02-21

概述

如果交易者試圖吸引投資者,而他們很可能會要檢查他或她的交易成果。 所以交易者應(yīng)該能夠提供交易歷史來展示成果。 MetaTrader 5 允許將交易歷史保存到文件(工具箱 — 交易選項(xiàng)卡 — 關(guān)聯(lián)菜單 — 報告)。 報告可以保存為 XLSX(在 Microsoft Excel 中進(jìn)行分析),和可以在任何瀏覽器中查看的 HTML文件。 第二種選項(xiàng)顯然更受歡迎,因?yàn)橛行┯脩艨赡軟]有 Excel,而所有人都有瀏覽器。 因此,含有交易結(jié)果的 HTML 報告更適合潛在投資者。

除了此類報告中提供的標(biāo)準(zhǔn)衡量值之外,您可能還希望利用從報告中提取的交易數(shù)據(jù)來自行計(jì)算隱秘數(shù)值。 在本文中,我們將研究從 HTML 報告中提取數(shù)據(jù)的方法。 首先,我們將分析報告的結(jié)構(gòu),然后我們將編寫一個函數(shù)來解析它們。 報告的名稱會傳遞給該函數(shù)。 然后該函數(shù)返回包含相關(guān)數(shù)據(jù)的結(jié)構(gòu)。 此結(jié)構(gòu)能夠直接訪問任何報告數(shù)值。 使用此結(jié)構(gòu),您能夠按照任意期望度量值輕松迅速地生成自己的報告。

除交易報告外,MetaTrader 5 還能夠保存智能交易系統(tǒng)的測試和優(yōu)化報告。 與交易歷史類似,測試報告可以按照兩種格式保存:XLSX 和 HTML,而優(yōu)化報告則以 XML 格式保存。

在本文中,我們將研究 HTML 測試報告,XML 優(yōu)化報告和 HTML 交易歷史報告。

HTML 和 XML 文件

HTML 文件實(shí)際上是一個由文本(可顯示數(shù)據(jù))和標(biāo)簽組成的文本文件,它指明數(shù)據(jù)應(yīng)如何顯示(圖例 1)。 所有標(biāo)簽都以字符 '<' 開頭,以 '>' 結(jié)尾。 例如,標(biāo)簽 <br> 意為其隨后的文本應(yīng)以新行開頭,而 <p> 意為文本應(yīng)以新段落開頭(不僅是新行,且在一個空行之后)。 其他屬性可以位于標(biāo)簽內(nèi),例如 <p color='red'> 意為標(biāo)簽后面的文本應(yīng)以新段落開頭,并以紅色書寫。 

記事本中的 HTML 文件
圖例 1 在記事本中打開 HTML 文件的一個片段

為了取消標(biāo)簽動作,使用了一個封閉標(biāo)簽,它與起始標(biāo)簽相似,并且在開頭還有一個斜杠 '/'。 例如,</p> 是段落的封閉標(biāo)簽。 某些標(biāo)簽在使用時無需對應(yīng)的封閉,例如 <br>。 某些標(biāo)簽可以選擇性使用封閉標(biāo)簽。 新段落可以在不封閉前一段落的情況下起始。 然而,如果在標(biāo)簽內(nèi)部使用了顏色屬性,就像上面帶有 <p> 的示例一樣,則必須使用封閉標(biāo)簽來取消未來的文本涂色。 有些標(biāo)簽需要封閉,例如 <table>。 table 應(yīng)始終以封閉標(biāo)簽 </table> 結(jié)尾。 table 由 <tr> 開頭且由封閉標(biāo)簽 </tr> 結(jié)束所指示的行組成。 行內(nèi)的單元格由 <td> 和 </ td> 定義。 有時候單元格會用 <th> 標(biāo)簽(標(biāo)題單元格)。 同一個表格可能二者皆有,即 <td> 標(biāo)簽和 <th> 標(biāo)簽的單元格。 行和單元格封閉標(biāo)簽是必需的。

目前,大多數(shù) html 屬性實(shí)際上都沒用過。 而是用了一個“style(樣式)”屬性,其中描述了元素外觀。 例如:<p style='color: red'> 表示紅色的段落,但這也并非一個非常流行的方法。 最常見的方法是使用“class”屬性,其中只指定樣式類名稱,例如 <p class='small'>,而類本身(樣式描述)位于 HTML 文檔的開頭,或單獨(dú)的 CSS 文件(層疊樣式表)。 

XML 文件 (圖例 2) 與 HTML 非常相似。 主要區(qū)別在于 HTML 支持嚴(yán)格限制的標(biāo)簽集,而 XML 允許擴(kuò)展標(biāo)簽集,并添加自定義標(biāo)簽。

記事本里的 XML 文件
圖例 2 在記事本中打開的 XML 文件的一個片段

HTML 的主要目的是數(shù)據(jù)顯示,這就是為什么它有一組標(biāo)準(zhǔn)的標(biāo)簽。 由于這個 HTML 文檔在不同的瀏覽器中具有幾乎相同的外觀。 XML 的主要目的是保存和傳遞數(shù)據(jù),因此支持任意數(shù)據(jù)結(jié)構(gòu)的可能性很重要,而使用此文件的用戶必須了解這樣做的目的和需要提取的數(shù)據(jù)。

字符串函數(shù)或正則表達(dá)式

通常,正則表達(dá)式用來從文本中提取數(shù)據(jù)。 在 代碼庫 中,您可以找到 RegularExpressions 函數(shù)庫來處理表達(dá)式。 您還可以查看如何使用它們的說明文章:“交易者的正則表達(dá)式”。 當(dāng)然,正則表達(dá)式構(gòu)成了解析文本數(shù)據(jù)的非常強(qiáng)大且方便的工具。 如果您經(jīng)常需要處理數(shù)據(jù)解析,且執(zhí)行不同的任務(wù)時,肯定需要使用正則表達(dá)式。

不過,正則表達(dá)式有一些缺點(diǎn):您需要在使用它們之前正確研究表達(dá)式。 與正則表達(dá)式相關(guān)的整體思想非常廣泛。 您不能指望“在需要時翻查參考”來使用它們。 你必須徹底研究這個理論并掌握實(shí)用技巧。 我認(rèn)為使用正則表達(dá)式所需的思維方式與解決普通編程任務(wù)所需的思維方式大不相同。 即使有能力使用正則表達(dá)式,在切換到使用正則表達(dá)式再返回時也可能出現(xiàn)障礙。 

如果數(shù)據(jù)解析任務(wù)不復(fù)雜且并不頻繁,則可以使用標(biāo)準(zhǔn) 字符串函數(shù)。 甚或,有一種觀點(diǎn)認(rèn)為字符串函數(shù)運(yùn)行得更快。 最可能的是,速度取決于任務(wù)的類型,以及應(yīng)用正則表達(dá)式的條件。 然而,字符串函數(shù)為數(shù)據(jù)解析任務(wù)提供了一個很好的解決方案。

在語言參考中可用的所有字符串函數(shù)中,我們只需要少量幾個:Stringring(),StringSubstr()StringReplace()。 我們可能還需要一些非常簡單的函數(shù),例如 StringLen(),StringTrimLeft(),StringTrimRight()。

測試器報告

在測試報告中包含了很大的數(shù)據(jù)量,因此我們將從頭開始。 這將令我們能夠理解任務(wù),并處理可能出現(xiàn)的所有障礙。 在創(chuàng)建本文時,我使用了默認(rèn)參數(shù)的 ExpertMACD EA 測試報告(報告附在下面)。 在瀏覽器中打開報告以便查看我們正在處理的內(nèi)容(圖例 3)。

HTML 報告部分
圖例 3 HTML 格式的測試報告,在瀏覽器中打開。 紅色行藍(lán)色文本顯示主要報告部分

報告數(shù)據(jù)分為幾個部分:報告名稱,參數(shù),結(jié)果,圖表,訂單成交,總計(jì)。  

每個瀏覽器都有一個查看頁面代碼的命令:右鍵單擊頁面打開關(guān)聯(lián)菜單,然后選擇“查看頁面源碼”或類似命令。 

通過對源代碼的直觀檢查,我們可以發(fā)現(xiàn)所有報告數(shù)據(jù)都排布在表中(<table> 標(biāo)簽)。 測試器報告有兩個表格。 第一個表格包含一般數(shù)據(jù):從報告名稱,直到“Average position holding time(平均持倉時間)”值,即直到訂單部分。 第二個表格包含有關(guān)訂單,成交和最終存款狀態(tài)的數(shù)據(jù)。 使用“colspan”屬性將不同類型的數(shù)據(jù)置于一個表格中,該屬性將若干個單元格合并在一起。 常用參數(shù)的名稱及其數(shù)值位于表格的不同單元格中,有時這些單元格出現(xiàn)在同一行中,有時它們位于不同的行中。

一個重要的時刻是“id”屬性的可用性,它們是標(biāo)簽的唯一標(biāo)識符。 id 值可用于查找包含所需數(shù)據(jù)的單元格。 但是,“id”屬性未使用。 這就是我們通過計(jì)算行和單元格來查找參數(shù)的原因。 例如,“策略測試器報告”名稱位于第一個表格,第一行和第一個單元格中。 

第二個表格中的訂單和成交數(shù)量在所有報告中都會有所不同,因此我們需要查找訂單的結(jié)束和成交的開始。 成交之后跟隨一個單元格的行,然后是標(biāo)題行 — 這是分隔數(shù)據(jù)的指示。 現(xiàn)在我們不會詳述行數(shù)和單元格的數(shù)量。 稍后將介紹一種便潔的方法。

所有數(shù)據(jù)都位于表格單元格中,因此我們在初始階段需要做的就是提取表格、行和單元格。

我們來分析交易歷史和優(yōu)化報告。 歷史報告與測試報告非常相似,除了附加的最終存款狀態(tài)部分(圖例 4)。

HTML 格式的交易歷史記錄報告,在瀏覽器中打開。

圖例 4 HTML 格式的交易歷史記錄報告,在瀏覽器中打開。 紅色行藍(lán)色文本顯示主要報告部分

通過檢查交易歷史 HTML 代碼,我們可以看到 單元格不僅由 <td> 標(biāo)簽定義,還由<th> 定義,它代表標(biāo)題單元格。 此外,所有數(shù)據(jù)都位于一個表格之中。

優(yōu)化報告非常簡單 — 它在一個表格中只有一個數(shù)據(jù)部分。 從它的源代碼中,我們看到它是使用 <Table> 標(biāo)簽創(chuàng)建的,行是用 <Row> 定義的,單元格是用 <Cell> 定義的(所有標(biāo)簽都與相應(yīng)的封閉標(biāo)簽一起使用)。

文件加載

在我們可以使用報告數(shù)據(jù)執(zhí)行任何操作之前,我們需要從文件中讀取它們。 我們使用以下函數(shù),將整個文件內(nèi)容返回到一個字符串變量中:

bool FileGetContent(string aFileName,string & aContent){    int h=FileOpen(aFileName,FILE_READ|FILE_TXT);    if(h==-1)return(false);    aContent='';    while(!FileIsEnding(h)){       aContent=aContent FileReadString(h);    }    FileClose(h);    return(true); }

傳遞給函數(shù)的第一個參數(shù)是報告文件的名稱。 第二個參數(shù)是包含文件內(nèi)容的變量。 該函數(shù)根據(jù)結(jié)果返回 true/false。

提取表格

我們使用兩種結(jié)構(gòu)來定位表格數(shù)據(jù)。 包含一個表格行的結(jié)構(gòu):

struct Str{
   string td[];
};

td[] 數(shù)組的每個元素將包含一個單元格的內(nèi)容。

包含整個表格(所有行)的結(jié)構(gòu):

struct STable{    Str tr[]; };

將從報告中提取數(shù)據(jù)如下:首先,我們要找到表格起始標(biāo)簽的開始位置。 由于標(biāo)簽可能含有屬性,因此我們僅搜索標(biāo)簽開頭:“<table”。 找到起始標(biāo)簽的開頭后,我們繼續(xù)找到它的結(jié)尾,“>”。 然后搜索表格結(jié)束,即封閉標(biāo)簽“</ table>”。 這很容易,因?yàn)閳蟾娌缓短妆砀瘢疵總€起始標(biāo)簽后跟一個封閉標(biāo)簽。

在找到表格之后,我們針對表格行重復(fù)相同動作,這次使用“<tr”作為起始,“</ tr”作為結(jié)束。 然后,在每個表格行中,我們將查找單元格,這次是“<td”起始,并用 </td> 結(jié)束。 表格行的任務(wù)有點(diǎn)復(fù)雜,因?yàn)閱卧窨梢酝瑫r包含 <td> 和 <th> 標(biāo)簽。 出于此原因,我們將使用自定義 TagFind() 函數(shù)來替代 StringFind():

int TagFind(string aContent,string & aTags[],int aStart,string & aTag){
   int rp=-1;
   for(int i=0;i<ArraySize(aTags);i  ){
      int p=StringFind(aContent,'<' aTags[i],aStart);
      if(p!=-1){
         if(rp==-1){
            rp=p;
            aTag=aTags[i];
         }
         else{
            if(p<rp){
               rp=p;
               aTag=aTags[i];
            }
         }      
      }
   }
   return(rp);
}

函數(shù)參數(shù):

  • string aContent — 我們要搜索的字符串;
  • string & aTags[] — 存儲標(biāo)簽的數(shù)組;
  • int aStart — 搜索的起始位置;
  • string & aTag — 找到的標(biāo)簽通過引用返回。

與 StringFind() 不同,是將數(shù)組傳遞給 TagFind() 函數(shù),而非要搜索的字符串。 起始“<”被添加到函數(shù)中的搜索字符串。 該函數(shù)返回最近標(biāo)簽的位置。

使用 TagFind(),我們將始終搜索起始和封閉標(biāo)記。 它們之間發(fā)現(xiàn)的所有內(nèi)容都將被收集到一個數(shù)組中:

int TagsToArray(string aContent,string & aTags[],string & aArray[]){    ArrayResize(aArray,0);    int e,s=0;    string tag;    while((s=TagFind(aContent,aTags,s,tag))!=-1 && !IsStopped()){         s=StringFind(aContent,'>',s);       if(s==-1)break;       s ;       e=StringFind(aContent,'</' tag,s);         if(e==-1)break;         ArrayResize(aArray,ArraySize(aArray) 1);       aArray[ArraySize(aArray)-1]=StringSubstr(aContent,s,e-s);      }    return(ArraySize(aArray)); }

函數(shù)參數(shù):

  • string aContent — 我們要搜索的字符串;
  • string & aTags[] — 存儲標(biāo)簽的數(shù)組;
  • string aArray[] — 返回包含所有已發(fā)現(xiàn)標(biāo)簽內(nèi)容的數(shù)組的引用。

只有在解析單元格時,才會將搜索到的標(biāo)簽數(shù)組傳遞給 TagsToArray()。 這就是為什么,對于所有其他情況,我們將使用通常的字符串參數(shù)來編寫函數(shù)模擬:

int TagsToArray(string aContent,string aTag,string & aArray[]){
   string Tags[1];
   Tags[0]=aTag;
   return(TagsToArray(aContent,Tags,aArray));
}

函數(shù)參數(shù):

  • string aContent — 我們要搜索的字符串;
  • string & aTag — 搜索的標(biāo)簽;
  • string aArray[] — 返回包含所有已發(fā)現(xiàn)標(biāo)簽內(nèi)容的數(shù)組的引用。

現(xiàn)在我們繼續(xù)完善可提取表格內(nèi)容的函數(shù)。 含有已解析文件名稱的字符串參數(shù) aFileName 將傳遞給該函數(shù)。 函數(shù)中用到一個局部字符串變量保存文件內(nèi)容,以及一個局部數(shù)組保存 STable 結(jié)構(gòu):

STable tables[]; string FileContent;

使用 FileGetContent 函數(shù)獲取整個報表內(nèi)容:

if(!FileGetContent(aFileName,FileContent)){
   return(true);
}

這是輔助變量:

string tags[]={'td','th'}; string ttmp[],trtmp[],tdtmp[]; int tcnt,trcnt,tdcnt;

'tags' 數(shù)組中提供了單元格標(biāo)簽的可能選項(xiàng)。 表格,行和單元格的內(nèi)容將分別放置到臨時字符串?dāng)?shù)組 ttmp[],trtmp[] 和 tdtmp[] 當(dāng)中。 

從表格中檢索數(shù)據(jù):

tcnt=TagsToArray(FileContent,'table',ttmp);
ArrayResize(tables,tcnt);

循環(huán)遍歷所有表格并提取行,然后從行中提取單元格:

for(int i=0;i<tcnt;i ){    trcnt=TagsToArray(ttmp[i],'tr',trtmp);    ArrayResize(tables[i].tr,trcnt);          for(int j=0;j<trcnt;j ){               tdcnt=TagsToArray(trtmp[j],tags,tdtmp);       ArrayResize(tables[i].tr[j].td,tdcnt);       for(int k=0;k<tdcnt;k ){            tables[i].tr[j].td[k]=RemoveTags(tdtmp[k]);       }    } }

首先接收表格,行和單元格并保存到臨時數(shù)組,然后將單元格數(shù)組復(fù)制到結(jié)構(gòu)中。 在復(fù)制期間中,單元格由 RemoveTags() 函數(shù)過濾。 表格單元格可以有嵌套標(biāo)簽。 RemoveTags() 會刪除它們,只留下必需的標(biāo)簽。

RemoveTags() 函數(shù):

string RemoveTags(string aStr){
   string rstr='';
   int e,s=0;
   while((e=StringFind(aStr,'<',s))!=-1){
      if(e>s){

         rstr=rstr StringSubstr(aStr,s,e-s);
      }
      s=StringFind(aStr,'>',e);
      if(s==-1)break;
      s  ;
   }
   if(s!=-1){
      rstr=rstr StringSubstr(aStr,s,StringLen(aStr)-s);
   }
   StringReplace(rstr,' ',' ');
   while(StringReplace(rstr,'  ',' ')>0);
   StringTrimLeft(rstr);
   StringTrimRight(rstr);
   return(rstr);
}

我們研究一下 RemoveTags() 函數(shù)。 “s”變量位于所用數(shù)據(jù)的開頭。 它的數(shù)據(jù)首先等于 0,因?yàn)閿?shù)據(jù)可以從行首開始。 在“while”循環(huán)中搜索起始尖括號“<”,意即標(biāo)簽起始。 找到起始標(biāo)簽時,從“s”變量指定的位置,直到發(fā)現(xiàn)關(guān)鍵字的位置,之間所有數(shù)據(jù)都將復(fù)制到“rstr”變量中。 之后,搜索標(biāo)簽的結(jié)尾,即下一個新的有效數(shù)據(jù)的起始。 在循環(huán)之后,如果“s”變量的值不等于 -1(這意味著字符串以有效數(shù)據(jù)結(jié)尾,但它們尚未被復(fù)制),則數(shù)據(jù)被復(fù)制到“rstr”變量。 在函數(shù)結(jié)束時,空格指代符   用一個簡單的空格替換,同時刪除重復(fù)的空格以及字符串開頭和結(jié)尾的空格。

在這一步,我們的“tables”數(shù)組結(jié)構(gòu)由純表格數(shù)據(jù)填充。 我們將此數(shù)組保存到文本文件中。 在保存時,我們?yōu)楸砀瘢泻蛦卧裨O(shè)置數(shù)量(數(shù)據(jù)保存到文件 1.txt):

int h=FileOpen('1.txt',FILE_TXT|FILE_WRITE); for(int i=0;i<ArraySize(tables);i ){    FileWriteString(h,'table ' (string)i '\n');    for(int j=0;j<ArraySize(tables[i].tr);j ){             FileWriteString(h,'   tr ' (string)j '\n');               for(int k=0;k<ArraySize(tables[i].tr[j].td);k ){          FileWriteString(h,'      td ' (string)k ': ' tables[i].tr[j].td[k] '\n');       }    } } FileClose(h);

通過此文件,我們可以輕松了解數(shù)據(jù)所在的單元格。 以下是文件片段:

table 0
   tr 0
      td 0: Strategy Test report
   tr 1
      td 0: IMPACT-Demo (Build 1940)
   tr 2
      td 0: 
   tr 3
      td 0: Settings
   tr 4
      td 0: Expert Advisor:
      td 1: ExpertMACD
   tr 5
      td 0: Symbol:
      td 1: USDCHF
   tr 6
      td 0: Period:
      td 1: H1 (2018.11.01 - 2018.12.01)
   tr 7
      td 0: Parameters:
      td 1: Inp_Expert_Title=ExpertMACD
   tr 8
      td 0: 
      td 1: Inp_Signal_MACD_PeriodFast=12
   tr 9
      td 0: 
      td 1: Inp_Signal_MACD_PeriodSlow=24
   tr 10
      td 0: 
      td 1: Inp_Signal_MACD_PeriodSignal=9
   tr 11

報告數(shù)據(jù)的結(jié)構(gòu)

現(xiàn)在我們有一些無聊的例行公事:我們需要編寫一個與報表數(shù)據(jù)相對應(yīng)的結(jié)構(gòu),并使用 tables 數(shù)組中的數(shù)據(jù)填充此結(jié)構(gòu)。 該報告分為幾個部分。 所以,我們將使用若干種結(jié)構(gòu)來組合成一種通用結(jié)構(gòu)。

Settings 部分的結(jié)構(gòu):

struct SSettings{    string Expert;    string Symbol;    string Period;    string Inputs;    string Broker;    string Currency;    string InitialDeposit;    string Leverage; };

結(jié)構(gòu)字段的目的顧名思義。 結(jié)構(gòu)的所有字段均包含與報告中顯示的數(shù)據(jù)完全相同的數(shù)據(jù)。 智能交易系統(tǒng)參數(shù)列表將位于一個字符串變量“Inputs”當(dāng)中。

Results 數(shù)據(jù)部分的結(jié)構(gòu):

struct SResults{
   string HistoryQuality;
   string Bars;
   string Ticks;
   string Symbols;
   string TotalNetProfit;
   string BalanceDrawdownAbsolute;
   string EquityDrawdownAbsolute;
   string GrossProfit;
   string BalanceDrawdownMaximal;
   string EquityDrawdownMaximal;
   string GrossLoss;
   string BalanceDrawdownRelative;
   string EquityDrawdownRelative;
   string ProfitFactor;
   string ExpectedPayoff;
   string MarginLevel;
   string RecoveryFactor;
   string SharpeRatio;
   string ZScore;
   string AHPR;
   string LRCorrelation;
   string OnTesterResult;
   string GHPR;
   string LRStandardError;
   string TotalTrades;
   string ShortTrades_won_pers;
   string LongTrades_won_perc;
   string TotalDeals;
   string ProfitTrades_perc_of_total;
   string LossTrades_perc_of_total;
   string LargestProfitTrade;
   string LargestLossTrade;
   string AverageProfitTrade;
   string AverageLossTrade;
   string MaximumConsecutiveWins_cur;
   string MaximumConsecutiveLosses_cur;
   string MaximalConsecutiveProfit_count;
   string MaximalConsecutiveLoss_count;
   string AverageConsecutiveWins;
   string AverageConsecutiveLosses;
   string Correlation_Profits_MFE;
   string Correlation_Profits_MAE;
   string Correlation_MFE_MAE;      
   string MinimalPositionHoldingTime;
   string MaximalPositionHoldingTime;
   string AveragePositionHoldingTime;
};

有關(guān)單筆訂單的數(shù)據(jù)結(jié)構(gòu):

struct SOrder{    string OpenTime;    string Order;    string Symbol;    string Type;    string Volume;    string Price;    string SL;    string TP;    string Time;    string State;    string Comment; };

有關(guān)單筆成交的數(shù)據(jù)結(jié)構(gòu):

struct SDeal{
   string Time;
   string Deal;
   string Symbol;
   string Type;
   string Direction;
   string Volume;
   string Price;
   string Order;
   string Commission;
   string Swap;
   string Profit;
   string Balance;
   string Comment;
};

最終存款狀態(tài)的結(jié)構(gòu):

struct SSummary{    string Commission;    string Swap;    string Profit;    string Balance; };

一般結(jié)構(gòu):

struct STestingReport{
   SSettings Settings;
   SResults Results;
   SOrder Orders[];
   SDeal Deals[];
   SSummary Summary;
};

SOrder 和 SDeals 結(jié)構(gòu)數(shù)組用于訂單和成交。

用數(shù)據(jù)填充結(jié)構(gòu)

這一點(diǎn)例行工作也需要注意。 查看以前收到的含有表格數(shù)據(jù)的文本文件,并填寫結(jié)構(gòu):

aTestingReport.Settings.Expert=tables[0].tr[4].td[1]; aTestingReport.Settings.Symbol=tables[0].tr[5].td[1]; aTestingReport.Settings.Period=tables[0].tr[6].td[1]; aTestingReport.Settings.Inputs=tables[0].tr[7].td[1];

在上述代碼示例的最后一行中,為 Inputs 字段分配了一個值,但之后該字段將只存儲一個參數(shù)的數(shù)據(jù)。 然后收集其他參數(shù)。 這些參數(shù)的位置從第 8 行開始,而每個參數(shù)行中的第一個單元格為空。 只要行中的第一個單元格為空,循環(huán)就會執(zhí)行:

int i=8;
while(i<ArraySize(tables[0].tr) && tables[0].tr[i].td[0]==''){
   aTestingReport.Settings.Inputs=aTestingReport.Settings.Inputs ', ' tables[0].tr[i].td[1];
   i  ;
}

以下是完整的測試報告解析函數(shù):

bool TestingHTMLReportToStruct(string aFileName,STestingReport & aTestingReport){    STable tables[];    string FileContent;       if(!FileGetContent(aFileName,FileContent)){       return(true);    }    string tags[]={'td','th'};    string ttmp[],trtmp[],tdtmp[];    int tcnt,trcnt,tdcnt;       tcnt=TagsToArray(FileContent,'table',ttmp);    ArrayResize(tables,tcnt);       for(int i=0;i<tcnt;i ){       trcnt=TagsToArray(ttmp[i],'tr',trtmp);       ArrayResize(tables[i].tr,trcnt);             for(int j=0;j<trcnt;j ){                  tdcnt=TagsToArray(trtmp[j],tags,tdtmp);          ArrayResize(tables[i].tr[j].td,tdcnt);          for(int k=0;k<tdcnt;k ){               tables[i].tr[j].td[k]=RemoveTags(tdtmp[k]);          }       }    }         // 設(shè)置部分       aTestingReport.Settings.Expert=tables[0].tr[4].td[1];    aTestingReport.Settings.Symbol=tables[0].tr[5].td[1];    aTestingReport.Settings.Period=tables[0].tr[6].td[1];    aTestingReport.Settings.Inputs=tables[0].tr[7].td[1];    int i=8;    while(i<ArraySize(tables[0].tr) && tables[0].tr[i].td[0]==''){       aTestingReport.Settings.Inputs=aTestingReport.Settings.Inputs ', ' tables[0].tr[i].td[1];       i ;    }    aTestingReport.Settings.Broker=tables[0].tr[i ].td[1];    aTestingReport.Settings.Currency=tables[0].tr[i ].td[1];      aTestingReport.Settings.InitialDeposit=tables[0].tr[i ].td[1];    aTestingReport.Settings.Leverage=tables[0].tr[i ].td[1];         // 結(jié)果部分       i =2;    aTestingReport.Results.HistoryQuality=tables[0].tr[i ].td[1];    aTestingReport.Results.Bars=tables[0].tr[i].td[1];    aTestingReport.Results.Ticks=tables[0].tr[i].td[3];    aTestingReport.Results.Symbols=tables[0].tr[i].td[5];    i ;    aTestingReport.Results.TotalNetProfit=tables[0].tr[i].td[1];    aTestingReport.Results.BalanceDrawdownAbsolute=tables[0].tr[i].td[3];    aTestingReport.Results.EquityDrawdownAbsolute=tables[0].tr[i].td[5];    i ;    aTestingReport.Results.GrossProfit=tables[0].tr[i].td[1];    aTestingReport.Results.BalanceDrawdownMaximal=tables[0].tr[i].td[3];    aTestingReport.Results.EquityDrawdownMaximal=tables[0].tr[i].td[5];    i ;    aTestingReport.Results.GrossLoss=tables[0].tr[i].td[1];    aTestingReport.Results.BalanceDrawdownRelative=tables[0].tr[i].td[3];    aTestingReport.Results.EquityDrawdownRelative=tables[0].tr[i].td[5];    i =2;    aTestingReport.Results.ProfitFactor=tables[0].tr[i].td[1];    aTestingReport.Results.ExpectedPayoff=tables[0].tr[i].td[3];    aTestingReport.Results.MarginLevel=tables[0].tr[i].td[5];    i ;    aTestingReport.Results.RecoveryFactor=tables[0].tr[i].td[1];    aTestingReport.Results.SharpeRatio=tables[0].tr[i].td[3];    aTestingReport.Results.ZScore=tables[0].tr[i].td[5];    i ;    aTestingReport.Results.AHPR=tables[0].tr[i].td[1];    aTestingReport.Results.LRCorrelation=tables[0].tr[i].td[3];    aTestingReport.Results.tables[0].tr[i].td[5];    i ;    aTestingReport.Results.GHPR=tables[0].tr[i].td[1];    aTestingReport.Results.LRStandardError=tables[0].tr[i].td[3];    i =2;    aTestingReport.Results.TotalTrades=tables[0].tr[i].td[1];    aTestingReport.Results.ShortTrades_won_pers=tables[0].tr[i].td[3];    aTestingReport.Results.LongTrades_won_perc=tables[0].tr[i].td[5];    i ;    aTestingReport.Results.TotalDeals=tables[0].tr[i].td[1];    aTestingReport.Results.ProfitTrades_perc_of_total=tables[0].tr[i].td[3];    aTestingReport.Results.LossTrades_perc_of_total=tables[0].tr[i].td[5];    i ;    aTestingReport.Results.LargestProfitTrade=tables[0].tr[i].td[2];    aTestingReport.Results.LargestLossTrade=tables[0].tr[i].td[4];    i ;    aTestingReport.Results.AverageProfitTrade=tables[0].tr[i].td[2];    aTestingReport.Results.AverageLossTrade=tables[0].tr[i].td[4];    i ;    aTestingReport.Results.MaximumConsecutiveWins_cur=tables[0].tr[i].td[2];    aTestingReport.Results.MaximumConsecutiveLosses_cur=tables[0].tr[i].td[4];    i ;    aTestingReport.Results.MaximalConsecutiveProfit_count=tables[0].tr[i].td[2];    aTestingReport.Results.MaximalConsecutiveLoss_count=tables[0].tr[i].td[4];    i ;    aTestingReport.Results.AverageConsecutiveWins=tables[0].tr[i].td[2];    aTestingReport.Results.AverageConsecutiveLosses=tables[0].tr[i].td[4];        i =6;    aTestingReport.Results.Correlation_Profits_MFE=tables[0].tr[i].td[1];    aTestingReport.Results.Correlation_Profits_MAE=tables[0].tr[i].td[3];    aTestingReport.Results.Correlation_MFE_MAE=tables[0].tr[i].td[5];        i =3;    aTestingReport.Results.MinimalPositionHoldingTime=tables[0].tr[i].td[1];    aTestingReport.Results.MaximalPositionHoldingTime=tables[0].tr[i].td[3];    aTestingReport.Results.AveragePositionHoldingTime=tables[0].tr[i].td[5];         // 訂單    ArrayFree(aTestingReport.Orders);    int ocnt=0;    for(i=3;i<ArraySize(tables[1].tr);i ){       if(ArraySize(tables[1].tr[i].td)==1){          break;       }         ArrayResize(aTestingReport.Orders,ocnt 1);       aTestingReport.Orders[ocnt].OpenTime=tables[1].tr[i].td[0];       aTestingReport.Orders[ocnt].Order=tables[1].tr[i].td[1];       aTestingReport.Orders[ocnt].Symbol=tables[1].tr[i].td[2];       aTestingReport.Orders[ocnt].Type=tables[1].tr[i].td[3];       aTestingReport.Orders[ocnt].Volume=tables[1].tr[i].td[4];       aTestingReport.Orders[ocnt].Price=tables[1].tr[i].td[5];       aTestingReport.Orders[ocnt].SL=tables[1].tr[i].td[6];       aTestingReport.Orders[ocnt].TP=tables[1].tr[i].td[7];       aTestingReport.Orders[ocnt].Time=tables[1].tr[i].td[8];       aTestingReport.Orders[ocnt].State=tables[1].tr[i].td[9];       aTestingReport.Orders[ocnt].Comment=tables[1].tr[i].td[10];             ocnt ;    }       // 成交       i =3;    ArrayFree(aTestingReport.Deals);    int dcnt=0;    for(;i<ArraySize(tables[1].tr);i ){       if(ArraySize(tables[1].tr[i].td)!=13){          if(ArraySize(tables[1].tr[i].td)==6){             aTestingReport.Summary.Commission=tables[1].tr[i].td[1];             aTestingReport.Summary.Swap=tables[1].tr[i].td[2];             aTestingReport.Summary.Profit=tables[1].tr[i].td[3];             aTestingReport.Summary.Balance=tables[1].tr[i].td[4];                      }          break;       }         ArrayResize(aTestingReport.Deals,dcnt 1);         aTestingReport.Deals[dcnt].Time=tables[1].tr[i].td[0];       aTestingReport.Deals[dcnt].Deal=tables[1].tr[i].td[1];       aTestingReport.Deals[dcnt].Symbol=tables[1].tr[i].td[2];       aTestingReport.Deals[dcnt].Type=tables[1].tr[i].td[3];       aTestingReport.Deals[dcnt].Direction=tables[1].tr[i].td[4];       aTestingReport.Deals[dcnt].Volume=tables[1].tr[i].td[5];       aTestingReport.Deals[dcnt].Price=tables[1].tr[i].td[6];       aTestingReport.Deals[dcnt].Order=tables[1].tr[i].td[7];       aTestingReport.Deals[dcnt].Commission=tables[1].tr[i].td[8];       aTestingReport.Deals[dcnt].Swap=tables[1].tr[i].td[9];       aTestingReport.Deals[dcnt].Profit=tables[1].tr[i].td[10];       aTestingReport.Deals[dcnt].Balance=tables[1].tr[i].td[11];       aTestingReport.Deals[dcnt].Comment=tables[1].tr[i].td[12];       dcnt ;    }    return(true); }

報告文件的名稱將傳遞給函數(shù)。 此函數(shù)中要填充的 STestingReport 結(jié)構(gòu)也通過引用傳遞。

請注意以“Orders”和“Deals”開頭的注釋代碼部分。 訂單列表開頭的行號已定義,而訂單列表的末尾則要基于檢測含有單個單元格的行:

if(ArraySize(tables[1].tr[i].td)==1){
   break;
}  

訂單后會跳過三行:

// 成交    i =3;

之后收集成交數(shù)據(jù)。 交易清單的結(jié)尾由含有 6 個單元格的行確定 - 該行包含有關(guān)最終存款狀態(tài)的數(shù)據(jù)。 在退出循環(huán)之前,填充最終存款狀態(tài)的結(jié)構(gòu):

if(ArraySize(tables[1].tr[i].td)!=13){
   if(ArraySize(tables[1].tr[i].td)==6){
      aTestingReport.Summary.Commission=tables[1].tr[i].td[1];
      aTestingReport.Summary.Swap=tables[1].tr[i].td[2];
      aTestingReport.Summary.Profit=tables[1].tr[i].td[3];
      aTestingReport.Summary.Balance=tables[1].tr[i].td[4];            
   }
   break;
} 

含有報告數(shù)據(jù)的結(jié)構(gòu)已完全準(zhǔn)備就緒。

交易歷史報告

交易歷史報告可如同策略測試器報告那樣進(jìn)行解析,盡管其中包含的最終數(shù)據(jù)結(jié)構(gòu),以及結(jié)構(gòu)所含內(nèi)容會有所不同:

struct SHistory{    SHistoryInfo Info;    SOrder Orders[];    SDeal Deals[];    SSummary Summary;      SDeposit Deposit;    SHistoryResults Results; };

SHistory 結(jié)構(gòu)包含以下結(jié)構(gòu): SHistoryInfo — 一般帳戶信息,含有訂單和成交數(shù)據(jù)結(jié)構(gòu)的數(shù)組, SSummary — 交易結(jié)果, SDeposit — 最終存款狀態(tài), SHistoryResults — 一般數(shù)值 (盈利, 成交數(shù)量, 回撤, 等等)。

解析交易報告的完整 full HistoryHTMLReportToStruct() 函數(shù)代碼如下所附。 兩個參數(shù)傳遞給函數(shù):

  • string FileName — 報告文件的名稱
  • SHistory History — 填寫交易報告的數(shù)據(jù)結(jié)構(gòu)

優(yōu)化報告

關(guān)于優(yōu)化報告的第一個區(qū)別是不同的文件類型。 報告以 ANSI 格式保存,因此我們將使用另一個函數(shù)來讀取其內(nèi)容:

bool FileGetContentAnsi(string aFileName,string & aContent){
   int h=FileOpen(aFileName,FILE_READ|FILE_TXT|FILE_ANSI);
   if(h==-1)return(false);
   aContent='';
   while(!FileIsEnding(h)){
      aContent=aContent FileReadString(h);
   }
   FileClose(h);
   return(true);
}

另一個區(qū)別在于標(biāo)簽。 <table>, <tr> 和 <td>, 會由以下標(biāo)簽替代: <Table>, <Row> 和 <Cell>. 最后一個主要區(qū)別是與數(shù)據(jù)結(jié)構(gòu)相關(guān):

struct SOptimization{    string ParameterName[];    SPass Pass[]; };

該結(jié)構(gòu)包括一個字符串?dāng)?shù)組,其中包含要優(yōu)化的參數(shù)名稱(最右邊的報告列)和 SPass 結(jié)構(gòu)數(shù)組:

struct SPass{
   string Pass;
   string Result;
   string Profit;
   string ExpectedPayoff;
   string ProfitFactor;
   string RecoveryFactor;
   string SharpeRatio;
   string Custom;
   string EquityDD_perc;
   string Trades;
   string Parameters[];
};

該結(jié)構(gòu)包括報告列中包含的字段。 最后一個字段是字符串?dāng)?shù)組,根據(jù) ParameterName() 數(shù)組中的名稱,它包含相應(yīng)的需進(jìn)行優(yōu)化的參數(shù)值。

解析優(yōu)化報告的 OptimizerXMLReportToStruct() 函數(shù)的完整代碼如下所附。 兩個參數(shù)傳遞給函數(shù):

  • string FileName — 報告文件的名稱
  • SOptimization Optimization — 含有要填充的優(yōu)化報告數(shù)據(jù)的結(jié)構(gòu)

輔助函數(shù)

如果我們將本文中創(chuàng)建的所有結(jié)構(gòu)和函數(shù)安排在一個包含文件中,那么報告解析將在三行代碼中實(shí)現(xiàn)。 

1. 包含文件:

#include <HTMLReport.mqh>

2. 聲明結(jié)構(gòu):

SHistory History;

3. 調(diào)用函數(shù):

HistoryHTMLReportToStruct('ReportHistory-555849.html',History);

之后,所有報告數(shù)據(jù)都將位于相應(yīng)的結(jié)構(gòu)字段中,與報告中的排列方式完全相同。 雖然所有字段都是字符串類型,但它們可以很容易地用于計(jì)算。 為此,我們只需要將字符串轉(zhuǎn)換為數(shù)字。 不過,某些報告字段和相應(yīng)的結(jié)構(gòu)字段包含兩個數(shù)值。 例如,訂單交易量寫入兩個數(shù)值“0.1 / 0.1”,其中第一個數(shù)值是訂單交易量,第二個值表示已填單的交易量。 一些總計(jì)也有雙重數(shù)值,主要數(shù)值和括號中的附加數(shù)值。 例如,交易數(shù)量可寫為“11 (54.55%)” — 實(shí)際數(shù)量和相對百分比值。 因此,我們要編寫輔助函數(shù)來簡化這些數(shù)值的使用。

用于提取單個交易量數(shù)值的函數(shù):

string Volume1(string aStr){
   int p=StringFind(aStr,'/',0);
   if(p!=-1){
      aStr=StringSubstr(aStr,0,p);
   }
   StringTrimLeft(aStr);
   StringTrimRight(aStr);
   return(aStr);
}

string Volume2(string aStr){
   int p=StringFind(aStr,'/',0);
   if(p!=-1){
      aStr=StringSubstr(aStr,p 1);
   }
   StringTrimLeft(aStr);
   StringTrimRight(aStr);
   return(aStr);
}

Volume1() 函數(shù)從類似“0.1 / 0.1”的字符串中提取第一個交易量數(shù)值,Volume2() 提取第二個數(shù)值。

提取雙重數(shù)據(jù)的函數(shù):

string Value1(string aStr){    int p=StringFind(aStr,'(',0);    if(p!=-1){       aStr=StringSubstr(aStr,0,p);    }    StringTrimLeft(aStr);    StringTrimRight(aStr);    return(aStr); } string Value2(string aStr){    int p=StringFind(aStr,'(',0);    if(p!=-1){       aStr=StringSubstr(aStr,p 1);    }    StringReplace(aStr,')','');    StringReplace(aStr,'%','');    StringTrimLeft(aStr);    StringTrimRight(aStr);    return(aStr); }

Value1() 函數(shù)從類型為“8.02 (0.08%)”的字符串中提取第一個數(shù)值,Value2() 提取第二個數(shù)值,刪除右括號和百分號(如果有)。

可能需要的另一個有用的函數(shù),就是測試報告結(jié)構(gòu)的參數(shù)數(shù)據(jù)轉(zhuǎn)換。 我們將使用以下結(jié)構(gòu)將其轉(zhuǎn)換為便利的形式:

struct SInput{
   string Name,
   string Value;
}

Name 字段用于參數(shù)名稱,Value 用于其數(shù)值。 數(shù)值類型是事先已知的,這就是 Value 字段具有字符串類型的原因。

以下函數(shù)將參數(shù)字符串轉(zhuǎn)換為 SInputs 結(jié)構(gòu)數(shù)組:

void InputsToStruct(string aStr,SInput & aInputs[]){    string tmp[];    string tmp2[];    StringSplit(aStr,',',tmp);    int sz=ArraySize(tmp);    ArrayResize(aInputs,sz);    for(int i=0;i<sz;i ){       StringSplit(tmp[i],'=',tmp2);       StringTrimLeft(tmp2[0]);       StringTrimRight(tmp2[0]);             aInputs[i].Name=tmp2[0];       if(ArraySize(tmp2)>1){          StringTrimLeft(tmp2[1]);          StringTrimRight(tmp2[1]);                aInputs[i].Value=tmp2[1];       }       else{          aInputs[i].Value='';       }    } }

參數(shù)字符串以“,”字符分割到數(shù)組。 然后,以字符“=”將結(jié)果數(shù)組的每個元素分割為名稱和數(shù)值。

現(xiàn)在,分析所提取數(shù)據(jù)并生成自定義報告的一切準(zhǔn)備工作就緒。

創(chuàng)建自定義報告

現(xiàn)在我們可以訪問報告數(shù)據(jù),我們可以用任何偏好方式對其進(jìn)行分析。 這有很多種選擇。 使用交易結(jié)果,我們可以計(jì)算一般度量值,例如鋒銳比率,恢復(fù)因子,等等。 自定義 HTML 報告可以訪問所有 HTML,CSS 和 JavaScript 功能。 使用 HTML,我們可以重新排列報告。 例如,我們可以創(chuàng)建訂單和成交表格,或買入成交和賣出成交的單獨(dú)表格,以及其他類型的報告

CSS 支持?jǐn)?shù)據(jù)著色,令報告更易于分析。 JavaScript 有助于令報告具有交互性。 例如,將鼠標(biāo)懸停在成交行上可高亮顯示訂單表格中的相應(yīng)訂單行。 可以生成圖像并將其添加到 HTML 報告中。 可選地,JavaScript 允許直接在 html5 中的 canvas 元素內(nèi)繪制圖像。 這一切都取決于您的需求,想象力和技能。

我們lai1創(chuàng)建一個自定義的 HTML 交易報告。 我們將訂單和成交合并到一個表格中。 取消的訂單將以灰色顯示,因?yàn)樗鼈儾惶峁└信d趣的信息。 灰色也將用于市價訂單,因?yàn)樗鼈兲峁┑男畔⑴c訂單被執(zhí)行后的成交結(jié)果相同。 成交將以明亮的顏色高亮顯示:藍(lán)色和紅色。 填單的掛單也應(yīng)該有顯眼的顏色,如粉紅色和藍(lán)色。 

自定義報告表將包含訂單表和成交表格中的標(biāo)題。 我們來準(zhǔn)備相應(yīng)的數(shù)組:

   string TableHeader[]={  'Time',
                           'Order',
                           'Deal',
                           'Symbol',
                           'Type',
                           'Direction',
                           'Volume',
                           'Price',
                           'Order',
                           'S/L',
                           'T/P',
                           'Time',
                           'State',
                           'Commission',
                           'Swap',
                           'Profit',
                           'Balance',
                           'Comment'};

我們接收到包含報告數(shù)據(jù)的結(jié)構(gòu):

SHistory History; HistoryHTMLReportToStruct('ReportHistory-555849.html',History);

我們打開生成報告的文件,并編寫 HTMLStart() 函數(shù)形成的 HTML 頁面的標(biāo)準(zhǔn)開頭:

   int h=FileOpen('Report.htm',FILE_WRITE);
   if(h==-1)return;

   FileWriteString(h,HTMLStart('Report'));
   FileWriteString(h,'<table>\n');
   
   FileWriteString(h,'\t<tr>\n');   
   for(int i=0;i<ArraySize(TableHeader);i  ){
      FileWriteString(h,'\t\t<th>' TableHeader[i] '</th>\n'); 
   }
   FileWriteString(h,'\t</tr>\n');     

HTMLStart() 函數(shù)代碼如下所示:

string HTMLStart(string aTitle,string aCSSFile='style.css'){    string str='<!DOCTYPE html>\n';    str=str '<html>\n';    str=str '<head>\n';    str=str '<link href=\'' aCSSFile '\' rel=\'stylesheet\' type=\'text/css\'>\n';    str=str '<title>' aTitle '</title>\n';    str=str '</head>\n';      str=str '<body>\n';        return str; }

帶有 <title> 標(biāo)簽的頁面標(biāo)題的字符串和帶有樣式的文件名將傳遞給該函數(shù)。

遍歷所有訂單,定義字符串顯示類型并形成它:

int j=0;
for(int i=0;i<ArraySize(History.Orders);i  ){
   
   string sc='';
   
   if(History.Orders[i].State=='canceled'){
      sc='PendingCancelled';
   }
   else if(History.Orders[i].State=='filled'){
      if(History.Orders[i].Type=='buy'){
         sc='OrderMarketBuy';
      }
      else if(History.Orders[i].Type=='sell'){
         sc='OrderMarketSell';
      }
      if(History.Orders[i].Type=='buy limit' || History.Orders[i].Type=='buy stop'){
         sc='OrderPendingBuy';
      }
      else if(History.Orders[i].Type=='sell limit' || History.Orders[i].Type=='sell stop'){
         sc='OrderMarketSell';
      }         
   }

   FileWriteString(h,'\t<tr class=\'' sc '\'>\n');   
   FileWriteString(h,'\t\t<td>' History.Orders[i].OpenTime '</td>\n');  // 時間
   FileWriteString(h,'\t\t<td>' History.Orders[i].Order '</td>\n');     // 訂單 
   FileWriteString(h,'\t\t<td>' ' ' '</td>\n');                    // 成交 
   FileWriteString(h,'\t\t<td>' History.Orders[i].Symbol '</td>\n');    // 品種 
   FileWriteString(h,'\t\t<td>' History.Orders[i].Type '</td>\n');      // 類型 
   FileWriteString(h,'\t\t<td>' ' ' '</td>\n');                    // 方向       
   FileWriteString(h,'\t\t<td>' History.Orders[i].Volume '</td>\n');    // 成交量    
   FileWriteString(h,'\t\t<td>' History.Orders[i].Price '</td>\n');     // 價格  
   FileWriteString(h,'\t\t<td>' ' ' '</td>\n');                    // 訂單        
   FileWriteString(h,'\t\t<td>' History.Orders[i].SL '</td>\n');        // 止損
   FileWriteString(h,'\t\t<td>' History.Orders[i].TP '</td>\n');        // 止盈
   FileWriteString(h,'\t\t<td>' History.Orders[i].Time '</td>\n');      // 時間    
   FileWriteString(h,'\t\t<td>' History.Orders[i].State '</td>\n');     // 狀態(tài)
   FileWriteString(h,'\t\t<td>' ' ' '</td>\n');                    // 傭金
   FileWriteString(h,'\t\t<td>' ' ' '</td>\n');                    // 掉期利率
   FileWriteString(h,'\t\t<td>' ' ' '</td>\n');                    // 盈利     
   FileWriteString(h,'\t\t<td>' ' ' '</td>\n');                    // 余額    
   FileWriteString(h,'\t\t<td>' History.Orders[i].Comment '</td>\n');   // 注釋     
   FileWriteString(h,'\t</tr>\n');   
   

如果訂單已填單,則找到相應(yīng)的成交,定義其顯示樣式并形成其 HTML 代碼:  

// 查找一筆成交 if(History.Orders[i].State=='filled'){    for(;j<ArraySize(History.Deals);j ){       if(History.Deals[j].Order==History.Orders[i].Order){                   sc='';                   if(History.Deals[j].Type=='buy'){             sc='DealBuy';          }          else if(History.Deals[j].Type=='sell'){             sc='DealSell';          }                 FileWriteString(h,'\t<tr class=\'' sc '\'>\n');            FileWriteString(h,'\t\t<td>' History.Deals[j].Time '</td>\n');     // 時間          FileWriteString(h,'\t\t<td>' ' ' '</td>\n');     // 訂單          FileWriteString(h,'\t\t<td>' History.Deals[j].Deal '</td>\n');     // 成交          FileWriteString(h,'\t\t<td>' History.Deals[j].Symbol '</td>\n');     // 品種          FileWriteString(h,'\t\t<td>' History.Deals[j].Type '</td>\n');     // 類型          FileWriteString(h,'\t\t<td>' History.Deals[j].Direction '</td>\n');     // 方向          FileWriteString(h,'\t\t<td>' History.Deals[j].Volume '</td>\n');     // 成交量          FileWriteString(h,'\t\t<td>' History.Deals[j].Price '</td>\n');     // 價格            FileWriteString(h,'\t\t<td>' History.Deals[j].Order '</td>\n');     // 訂單           FileWriteString(h,'\t\t<td>' ' ' '</td>\n');     // 止損          FileWriteString(h,'\t\t<td>' ' ' '</td>\n');     // 止盈          FileWriteString(h,'\t\t<td>' ' ' '</td>\n');     // 時間              FileWriteString(h,'\t\t<td>' ' ' '</td>\n');     // 狀態(tài)          FileWriteString(h,'\t\t<td>' History.Deals[j].Commission '</td>\n');                    // 傭金          FileWriteString(h,'\t\t<td>' History.Deals[j].Swap '</td>\n');     // 掉期利率          FileWriteString(h,'\t\t<td>' History.Deals[j].Profit '</td>\n');     // 盈利              FileWriteString(h,'\t\t<td>' History.Deals[j].Balance '</td>\n');     // 余額              FileWriteString(h,'\t\t<td>' History.Deals[j].Comment '</td>\n');     // 注釋              FileWriteString(h,'\t</tr>\n');          break;       }    } }

之后我們關(guān)閉表格,并添加標(biāo)準(zhǔn) HTML 頁面結(jié)尾,它是由 HTMLEnd() 函數(shù)形成的:

FileWriteString(h,'</table>\n');   
FileWriteString(h,HTMLEnd('Report'));  

HTMLEnd() 函數(shù)代碼:

string HTMLEnd(){    string str='</body>\n';    str=str '</html>\n';    return str; }

現(xiàn)在我們只需要編寫樣式文件 style.css。 學(xué)習(xí) CSS 超出了本文的范圍,因此我們不會詳細(xì)研究這一點(diǎn)。 您可以查看下面附帶的文件。 附件還包含創(chuàng)建報告的腳本 - HTMLReportCreate.mq5。

這是現(xiàn)成的報告:

自定義 HTML 報告
圖例 5 自定義 HTML 報告的片段

結(jié)束語

您也許想知道運(yùn)用正則表達(dá)式是否更容易。 整體結(jié)構(gòu)和原則是一樣的。 我們將分別接收一個包含表格內(nèi)容的數(shù)組,然后是行和單元格。 我們將使用含正則表達(dá)式的函數(shù),來替代 TagsToArray()。 其余的操作將非常相似。

本文中描述的創(chuàng)建自定義報告的示例只是呈現(xiàn)報告的選項(xiàng)之一。 它僅作為示例。 您可以使用方便自己理解的表格。 本文最重要的結(jié)果是您可以輕松訪問所有報告數(shù)據(jù)。

附件

  • Include/HTMLReport.mqh — 包含報告解析函數(shù)的文件。
  • Scripts/HTMLReportTest.mq5 — 使用 HTMLReport.mqh 解析測試、優(yōu)化和歷史報告的示例。
  • Scripts/HTMLReportCreate.mq5 — 創(chuàng)建自定義 HTML 報告的示例。
  • Files/ReportTester-555849.html — 策略測試器報告。
  • Files/ReportOptimizer-555849.xml — 優(yōu)化報告。
  • Files/ReportHistory-555849.html — 交易歷史報告。
  • Files/Report.htm — 使用 HTMLReportCreate 腳本創(chuàng)建的報告文件。
  • Files/style.css — Report.htm 的樣式表


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多