如何在 MetaTrader 5 中创建并测试自定义 MOEX(莫斯科证券交易所) 品种

5 二月 2019, 07:07
Dmitrii Troshin
0
167

概述

两种基本类型的金融市场包括交易所和场外交易市场。 我们可以使用现代 MetaTrader 和 MetaEditor 工具享受 OTC(场外交易市场) 外汇交易,这些工具正在不断得到进一步改进。 除了交易自动化,这些工具还可以使用历史数据对交易算法进行全面测试。

如何运用我们自己的思路进行兑换交易? 一些兑换交易终端具有内置的可编程语言。 例如,广受欢迎的 Transaq 终端具有 ATF (超级交易装置) 可编程语言。 但是,当然了,它无法与 MQL5 进行比较。 此外,它没有任何策略测试功能。 一个好的解决方案是在 MetaTrader 策略测试器中获取兑换数据并优化交易算法。

这可以通过创建自定义品种来完成。 文章 在 MetaTrader 5 中创建并测试自定义品种 中详细描述了自定义品种的创建过程。 所需要的只是从 CSV(TXT)格式中获取数据,并按照本文中描述的步骤导入价格历史记录。

如果数据格式没有差异,这将很容易。 例如,我们研究一下兑换相关的流行网站资源 finam.ru。 报价可以在这里下载:



从莫斯科交易所导出报价


Finam 提供的数据格式:




可用的日期格式: "yyyymmdd", "yymmdd", "ddmmyy", "dd/mm/yy", "mm/dd/yy"。 我们的格式:



未提供我们需要的“yyyy.mm.dd”格式。 所以,finam.ru 提供了大量不同的格式,但没有一种格式是我们需要的。 

进而,还有很多其他的兑换资源。 其他网站提供的格式也可能不合适。 我们需要一定顺序的数据。 然而,报价可按不同的顺序存储,例如,开盘价,收盘价,最高价,最低价。 

所以,我们的任务是将随机顺序和不同格式提供的数据转换为所需格式。 这将为 MetaTrader 5 提供从任意资源接收数据的机会。 然后,我们将基于收到的数据利用 MQL5 工具创建自定义品种,这将令我们能够执行测试。

导入报价有几分困难。

兑换支持点差,竞卖价(Ask)和竞买价(Bid)。 但是,在市场深度里所有这些数值仅存在“片刻”。 此后,无论其执行价格如何,只有成交价格被写入,即竞卖价或竞买价。 我们需要终端的点差值。 此处加入了固定点差,因为无法复原市场深度点差。 如果点差是必要的,您可按某种方式模拟它。 其中一种方法已在文章 根据指定分布定律为自定义品种的时间序列建模 中有所描述。 或者,您可以编写一个简单的函数来体现点差对波动率的依赖性 Spread = f(High-Low)

当按照时间帧操作时,使用固定点差是完全可以接受的。 这点误差在很大的周期内是微不足道的。 不过,点差建模对于逐笔报价非常重要。 兑换逐笔报价格式:


我们的格式:



除了最后成交( LAST) 之外,我们还需要设置竞卖价(ASK)和竞买价(BID)。 数据以毫秒精度排序。 兑换仅提供价格流。 第一页中的数据更像是将大数据切分为几个片段。 外汇方面没有逐笔报价。 它可以是竞买价(Bid),竞卖价(Ask),或竞买价和竞卖价同时出现。 此外,我们需要人为地按时间对交易进行排位并添加毫秒数。

因此,本文不涉及数据导入,而是涉及数据建模,就像上面提到的文章一样。 所以,为了不会误导您,我决定不按照竞卖价=竞买价(+点差)=最后成交的原则来发送逐笔报价导入应用程序。 当使用毫秒处理时,点差很重要,因此在测试中我们需要选择合适的建模方法。

在此之后,修改逐笔报价导入代码将花费几分钟。 只需要用 MqlTick 替换 MqlRates 结构。 CustomRatesUpdate() 函数需要由 CustomTicksAdd() 替代。

下一个关联点是无法考虑所有可能的数据格式。 例如,在书写数字时,可以使用空白作为分隔符(1 000 000),或使用逗号来代替小数点(如 3,14)。 或者甚至更糟 - 当数据分隔符和小数点分隔符都是圆点或逗号时(您会如何区分它们呢)。 这里只考虑最常见的格式。 如果您需要处理非标准格式,则您必须自行处理它。

此外,兑换没有逐笔报价历史记录 — 它只提供交易量。 因此,在本文中我们设定兑换交换量 =VOL=TICKVOL。

本文分为两部分。 第一部分介绍了代码说明。 它可令您熟悉代码,以便稍后您可以编辑它,以便用于非标准数据格式的处理。 第二部分包含循序渐进的指南(用户手册)。 它适用于那些对编程不感兴趣,但仅需要使用已实现功能的人士。 如果您使用标准数据格式(特别是使用 finam.ru 网站作为来源),您可以立即进入第 2 部分。

第 1 部分 代码说明

在此仅提供部分代码。 完整代码可在附件中找到。

首先,我们输入所需的参数,例如字符串中数据所在位置,文件参数,品种名称,等等。

input int SkipString        =1;                               // 要跳过的字符串数量
input string mark1          ="Time position and format";      // 时间
input DATE indate           =yyyymmdd;                        // 源日期格式
input TIME intime           =hhdmmdss;                        // 源时间格式
input int DatePosition      =1;                               // 日期位置
input int TimePosition      =2;                               // 时间位置
//------------------------------------------------------------------+
input string mark2          ="Price data position";           // 价格
input int OpenPosition      =3;                               // 开盘价位置
input int HighPosition      =4;                               // 最高价位置
input int LowPosiotion      =5;                               // 最低价位置
input int ClosePosition     =6;                               // 收盘价位置
input int VolumePosition    =7;                               // 成交量位置
input string mark3          ="File parameters";               // 文件
//-------------------------------------------------------------------+
input string InFileName     ="sb";                            // 源文件名
input DELIMITER Delimiter   =comma;                           // 分隔符
input CODE StrType          =ansi;                            // 字符串类型
input string mark4          ="Other parameters";              // 其它
//-------------------------------------------------------------------+
input string spread         ="2";                             // 固定点差点数
input string Name           ="SberFX";                        // 您所创建的品种名称


为某些数据创建枚举。 例如,对于日期和时间格式:

enum DATE
{
yyyycmmcdd, // yyyy.mm.dd
yyyymmdd,   // yyyymmdd
yymmdd,     // yymmdd
ddmmyy,     // ddmmyy   
ddslmmslyy, // dd/mm/yy
mmslddslyy  // mm/dd/yy
// 其他格式在此处添加
};

enum TIME
{
hhmmss,     // hhmmss
hhmm,       // hhmm
hhdmmdss,   // hh:mm:ss
hhdmm       // hh:mm
// 其他格式在此处添加
};


如果所需格式未提供,则添加它。

然后打开源文件。 为了方便编辑格式化数据,我建议将它们保存在 CSV 文件中。 同时,应将数据写入 MqlRates 结构,以便能够自动创建自定义品种。

// 打开输入文件
    
  int out =FileOpen(InFileName,FILE_READ|StrType|FILE_TXT);
  if(out==INVALID_HANDLE)
  {
   Alert("Failed to open the file for reading");
   return; 
  }
// 打开输出文件
  int in =FileOpen(Name+"(f).csv",FILE_WRITE|FILE_ANSI|FILE_CSV);
  if(in==INVALID_HANDLE)
  {
   Alert("Failed to open the file for writing");
   return; 
  }
  //---插入标题字符串  
  string Caption ="<DATE>\t<TIME>\t<OPEN>\t<HIGH>\t<LOW>\t<CLOSE>\t<TICKVOL>\t<VOL>\t<SPREAD>";
  FileWrite(in,Caption);
//-----------------------------------------------------------
string fdate="",ftime="",open="";
string high="",low="",close="",vol="";
int left=0,right=0;

string str="",temp="";
for(int i=0;i<SkipString;i++)
   {
   str =FileReadString(out);
   i++;
   }
MqlRates Rs[];
ArrayResize(Rs,43200,43200);  // 一个月中有 43200 分钟
datetime time =0;


源文件必须保存到 MQL5/Files 目录。 SkipString 外部变量表示要自文件头跳过的行数。 为了能够使用空格和制表符作为分隔符,我们使用 标志 FILE_TXT 打开文件。

然后我们需要从字符串中提取数据。 在输入参数中指定该位置。 编号从 1 开始。 我们以 Sberbank 股票报价为例。



这里的日期位置是 1,时间是 2,等等。 SkipString=1。

若要解析字符串,我们可以使用 StringSplit() 函数。 但是最好开发自己的函数,以便更便洁地监控源文件中的错误。 可在这些函数中添加数据分析。 尽管,使用 StringSplit() 代码会更轻松。 查找数据边界的第一个函数接收字符串,分隔符和位置。 边界会被写入 a 和 b 变量,这些变量会作为引用传递。

//---搜索数据位置边界-----------------------------+
bool SearchBorders(string str,int pos,int &a,int &b,DELIMITER delim)
{
// 辅助变量
int left=0,right=0;
int count=0;
int start=0;
string delimiter="";
//-------------------------------------------------------------------+

switch(delim)
{
case comma : delimiter =",";
   break; 
case tab : delimiter ="/t";
   break;
case space : delimiter =" ";
   break;
case semicolon : delimiter =";";
   break;
}

while(count!=pos||right!=-1)
   {
   right =StringFind(str,delimiter,start);
      
   if(right==-1&&count==0){Print("Wrong date");return false;} //Incorrect data
   
   if(right==-1)
      {
      right =StringLen(str)-1;
      a =left;
      b =right;
      break;
      }
   
   count++;
      if(count==pos)
      {
      a =left;
      b =right-1;
      return true;
      }
   left =right+1;
   start =left;
   }

return true;
}


现在,我们来利用 StringSubstr() 函数获取相应的数据。 必须将接收的数值转换为所需的格式。 为此,我们来编写日期和时间转换函数。 例如,这是日期转换函数:

//---日期格式-------------------------------------------------+
//2017.01.02
string DateFormat(string str,DATE date)
{

string res="";
string yy="";
 
switch(date)
  { 
   case yyyycmmcdd :  //我们的格式
      res =str;
      if(StringLen(res)!=10)res=""; // 检查日期格式
   case yyyymmdd :
      res =StringSubstr(str,0,4)+"."+StringSubstr(str,4,2)+"."+StringSubstr(str,6,2);
      if(StringLen(res)!=10)res=""; // 检查日期格式
      break;
   case yymmdd :
      yy =StringSubstr(str,0,2);
      if(StringToInteger(yy)>=70)
         yy ="19"+yy;
      else
         yy ="20"+yy;
      res =yy+"."+StringSubstr(str,2,2)+"."+StringSubstr(str,4,2);
      if(StringLen(res)!=10)res=""; // 检查日期格式
      break;
//---其他格式 (完整代码在文件当中)-------------
//如有必要,添加其他格式的解析      
   default :
      break; 

  }

return res;
}


如果所需格式未提供(例如 1 月 18 日之前的日期),则应添加该格式。 此处检查接收的数据是否符合所需的格式(如果源文件中有错误) if(StringLen(res)!=10) res="";我明白这不是很彻底的检查。 但数据分析并不是一件容易的事,因此需要一个单独的程序来进行更详细的分析。 如果出现错误,函数将返回 res = "",然后跳过相应的行。

以下代码可供 ddmmyy 类型的格式转换,其中年份写为两位数。 数值 >=70 则转换为 19yy,数值小于它则转换为 20yy。

格式转换后,我们将数据写入相应的变量,并编译最终的字符串。

while(!FileIsEnding(out))
   {
   str =FileReadString(out);
   count++;
//---fdate-----------------------------
   if(SearchBorders(str,DatePosition,left,right,Delimiter))
   {
   temp =StringSubstr(str,left,right-left+1);
   fdate =DateFormat(temp,indate);
   if(fdate==""){Print("Error in string   ",count);continue;}
   }
   else {Print("Error in string   ",count);continue;}
//---其他数据的处理方式类似


如果在函数 SearchBorders,DateFormat 或 TimeFormat 中发现错误,则跳过该字符串并使用Print() 函数输出其顺序编号。 所有枚举和格式转换函数都位于单独的头文件 FormatFunctions.mqh 当中。 

然后形成并输出所得到的字符串。 数据被分配给 MqlRates 结构的相应元素。

//-------------------------------------------------------------------+
   str =fdate+","+ftime+","+open+","+high+","+low+","+close+","+vol+","+vol+","+Spread;
   FileWrite(in,str);
//---填充 MqlRates -----------------------------------------------+
   Rs[i].time               =time;
   Rs[i].open               =StringToDouble(open);
   Rs[i].high               =StringToDouble(high);
   Rs[i].low                =StringToDouble(low);
   Rs[i].close              =StringToDouble(close);
   Rs[i].real_volume        =StringToInteger(vol);
   Rs[i].tick_volume        =StringToInteger(vol);
   Rs[i].spread             =int(StringToInteger(Spread));
   i++;
//-------------------------------------------------------------------+   
   }


读取所有字符串后,动态数组将获得最终大小,且文件将关闭:

   ArrayResize(Rs,i);
   FileClose(out);
   FileClose(in);

现在,创建自定义品种已一切就绪。 此外,我们还有一个 CSV 文件,可以直接在 MetaEditor 中轻松编辑。 基于该 CSV 文件,我们可以使用 MetaTrader 5 终端中的标准方法创建自定义品种。



利用 MQL5 创建自定义品种

既然已经准备好了所有数据,我们只需要添加自定义品种。

   CustomSymbolCreate(Name);
   CustomRatesUpdate(Name,Rs);

利用 CustomRatesUpdate() 函数导入报价,这意味着该程序不仅可用于创建品种,还可用于添加新数据。 如果品种已存在,CustomSymbolCreate() 将返回 -1(负 1),且程序将继续执行,而报价将通过 CustomRatesUpdate() 函数进行更新。 该品种显示在 市场观察 窗口中,并以绿色高亮显示。



现在我们可以打开图表以确保一切正常:


EURUSD 图表


设定规格(品种属性)

在测试品种时,我们可能需要配置其特性(规格)。 我已编写了一个单独的规格包含文件,可以方便地编辑品种属性。 在此文件中,品种属性在 SetSpecifications() 函数中设置。 所有品种属性来自在此收集的 ENUM_SYMBOL_INFO_INTEGER, ENUM_SYMBOL_INFO_DOUBLE , ENUM_SYMBOL_INFO_STRING 枚举。

void SetSpecifications(string Name)
   {  
//---整数属性-------------------------------------
//   CustomSymbolSetInteger(Name,SYMBOL_CUSTOM,true);                       // 布尔值表示此品种自定义
//   CustomSymbolSetInteger(Name,SYMBOL_BACKGROUND_COLOR,clrGreen);         // 在市场观察中此品种所用的背景颜色
// 其它整数属性
//---双精度属性 ---------------------------------------------------   
//   CustomSymbolSetDouble(Name,SYMBOL_BID,0);                              // 竞买价,出售品种的最佳价格 
//  CustomSymbolSetDouble(Name,SYMBOL_BIDHIGH,0);                           // 每日最高的竞买价
//   CustomSymbolSetDouble(Name,SYMBOL_BIDLOW,0);                           // 每日最低的竞买价
// 其它双精度属性
//---字符串属性-----------------------------------------------+
//   CustomSymbolSetString(Name,SYMBOL_BASIS,"");                           // 自定义品种的基准资产名称
//   CustomSymbolSetString(Name,SYMBOL_CURRENCY_BASE,"");                   // 品种的基准货币
//   CustomSymbolSetString(Name,SYMBOL_CURRENCY_PROFIT,"");                 // 盈利货币
// 其它字符串属性
}


此函数在 CustomSymbolCreate 函数之后执行。 事先不知道这是什么类型的品种,期货,股票或期权,大多数属性不是必需的,且被注释掉。 源代码中只有部分行未注释:

   CustomSymbolSetInteger(Name,SYMBOL_CUSTOM,true);                       // 布尔值表示此品种是自定义的
   CustomSymbolSetInteger(Name,SYMBOL_BACKGROUND_COLOR,clrGreen);         // 在市场观察中此品种所用的背景颜色
   CustomSymbolSetInteger(Name,SYMBOL_SELECT,true);                       // 布尔值表示在市场观察中选择了此品种
   CustomSymbolSetInteger(Name,SYMBOL_VISIBLE,true);                      // 布尔值表示此品种已在市场观察中显示


出于测试目的,以下参数未被注释:最小交易量,交易量增量,价格增量,点数大小,这些都是必要的特性。 这些特性是 Sberbank 股票的典型特征。 不同品种的属性集合及其特征不同。

   CustomSymbolSetDouble(name,SYMBOL_POINT,0.01);               // 点数
   CustomSymbolSetDouble(name,SYMBOL_VOLUME_MIN,1);             // 一笔成交最小交易量
   CustomSymbolSetDouble(name,SYMBOL_VOLUME_STEP,1);            // 最小交易量变化增量
   CustomSymbolSetInteger(name,SYMBOL_DIGITS,2);                // 小数位
   CustomSymbolSetInteger(name,SYMBOL_SPREAD,2);                // 点差的点数
   CustomSymbolSetInteger(name,SYMBOL_SPREAD_FLOAT,false);      // 布尔值表示浮动点差
   CustomSymbolSetDouble(name,SYMBOL_TRADE_TICK_SIZE,0.01);	// 最小价格变化


这种方式很好,我们无需在每次需要设置必要属性时重新编译代码。 如果可以通过输入所需参数就可以完成就更方便了。 因此我不得不改变方法。 品种属性将在纯文本文件 Specifications.txt中 提供,可为每个新品种手动编辑。 这样就不需要重新编译源代码。

在 MetaEditor 中编辑文本文件更方便。 主要是因为 MetaEditor 提供了参数和数据的高亮显示。 属性按以下格式编写:




数据以逗号分隔。 字符串解析如下:

   while(!FileIsEnding(handle))
     {
     str =FileReadString(handle);
//--- 跳过行 -----------------------+     
     if(str=="") continue;
     if(StringFind(str,"//")<10) continue;
//------------------------------------------+     
     sub =StringSplit(str,u_sep,split);
     if(sub<2) continue;
     SetProperties(SName,split[0],split[1]);
     }


如果该行为空,或者在开头(位置<10)处有注释符号“//”,则跳过该行。 然后利用 StringSplit() 函数将字符串切分为子串。 之后,将字符串传递给 SetProperties() 函数,在其中设置品种属性。 函数代码结构:

void SetProperties(string name,string str1,string str2)
   {
   int n =StringTrimLeft(str1);
       n =StringTrimRight(str1);
       n =StringTrimLeft(str2);
       n =StringTrimRight(str2);
       
   if(str1=="SYMBOL_CUSTOM")
      {
      if(str2=="0"||str2=="false"){CustomSymbolSetInteger(name,SYMBOL_CUSTOM,false);}
      else {CustomSymbolSetInteger(name,SYMBOL_CUSTOM,true);}
      return;
      }
   if(str1=="SYMBOL_BACKGROUND_COLOR")
      {
      CustomSymbolSetInteger(name,SYMBOL_BACKGROUND_COLOR,StringToInteger(str2));
      return;
      }
   if(str1=="SYMBOL_CHART_MODE")
      {
      if(str2=="SYMBOL_CHART_MODE_BID"){CustomSymbolSetInteger(name,SYMBOL_CHART_MODE,SYMBOL_CHART_MODE_BID);}
      if(str2=="SYMBOL_CHART_MODE_LAST"){CustomSymbolSetInteger(name,SYMBOL_CHART_MODE,SYMBOL_CHART_MODE_LAST);}
      return;
      }
//--- 其他品种属性
}


如果用户在编辑时留下空格或制表符,则会为这些情况添加另外两个函数,StringTrimLeft()  StringTrimRight()

完整代码可在包含文件 PropertiesSet.mqh 中找到。

现在,所有品种属性都是通过附加的文本文件设置的,而对于其它的,需要重新编译。 您可以检查下面附带的两种代码变体。 第一个变体需要通过包含文件进行属性设置,它已被注释掉。


界面

为了方便编辑代码,使用 输入参数 指定设置。 如果没有可编辑的内容,我们可以考虑界面。 对于最终版本,我开发了输入面板:


关于面板代码。 此处用到的 标准控件集合 来自以下头文件:

#include <Controls\Dialog.mqh>
#include <Controls\Label.mqh>
#include <Controls\Button.mqh>
#include <Controls\ComboBox.mqh>

已为 OK 按钮创建了事件处理程序。

//+------------------------------------------------------------------+ 
//| 事件处理                                                          | 
//+------------------------------------------------------------------+ 
EVENT_MAP_BEGIN(CFormatPanel) 
ON_EVENT(ON_CLICK,BOK,OnClickButton) 
EVENT_MAP_END(CAppDialog)

void CFormatPanel::OnClickButton(void) 
  { 
// 上述属性
  } 

现在几乎将上述程序的整个代码移到此事件处理程序。 外部参数变为 局部变量

long SkipString        =1;                              // 要跳过的字符串数量
DATE indate           =yyyymmdd;                        // 源日期格式
TIME intime           =hhdmmdss;                        // 源时间格式
int DatePosition      =1;                               // 日期位置
int TimePosition      =2;                               // 时间位置
// 其它参数

为每个控件编写 Create() 函数,因此在执行后将相应的数值添加到控件列表中。 例如,对日期格式执行以下操作:

//-----------多选框日期格式------------------------------------+     
    if(!CreateComboBox(CDateFormat,"ComDateFormat",x0,y0+h+1,x0+w,y0+2*h+1))
     {
      return false;
     }
   CDateFormat.ListViewItems(6);
   CDateFormat.AddItem(" yyyy.mm.dd",0);
   CDateFormat.AddItem(" yyyymmdd",1);
   CDateFormat.AddItem(" yymmdd",2);
   CDateFormat.AddItem(" ddmmyy",3);
   CDateFormat.AddItem(" dd/mm/yy",4);
   CDateFormat.AddItem(" mm/dd/yy",5);
   CDateFormat.Select(1);
     }

然后,这些数值从输入字段返回到相应的变量:

long sw;  
SkipString =StringToInteger(ESkip.Text());

sw =CDateFormat.Value();
switch(int(sw))
{
   case 0 :indate =yyyycmmcdd;
      break;
   case 1 :indate =yyyymmdd;
      break;
   case 2 :indate =yymmdd;
      break;
   case 3 :indate =ddmmyy;
      break;
   case 4 :indate =ddslmmslyy;
      break;
   case 5 :indate =mmslddslyy;
      break;                
}
// 其他变量

 

此版本已得到大量实现,因此如果您需要编辑代码,您应使用输入版本。


第 2 部分 循序渐进指南

本部分循序渐进介绍创建自定义兑换品种所需的操作。 当可用报价拥有任何标准格式,且您无需编辑代码时,可以使用本指南。 例如,如果报价是从网站 finam.ru 网站获得的。 如果报价是某些非标准格式,那么您应该编辑第 1 部分中描述的代码。

因此,我们有一个包含金融产品兑换报价的源文件。 假设,我们已经从文章开头所述的 Finam 网站得到了它。 不要忘记我们需要的是一分钟时间帧的报价。

本文介绍了两种数据导入选项。 您可以使用 CreateCustomSymbol 脚本,以及 CreateSymbolPanel 智能交易系统,它拥有输入面板。 两个 EA 的表现完全相同。 例如,我们来研究使用输入面板进行操作。 在此处提供的示例中,我们使用来自 莫斯科交易所 的 Sberbank 股票报价。 报价附在下面的 sb.csv 文件中。

1. 文件的编排

首先,我们需要将报价文件保存到 MQL5/Files 当中。 这与 MQL5 编程概念有关,因为 出于安全原因,文件的操作严格受限。 找到所需目录的最简单方法是从 MetaTrader 中打开它。 在导航窗口中,右键单击文件夹,然后从关联菜单中选择“打开文件夹”。



源数据文件应保存到此文件夹(程序文件的位置在下面的文件一章中介绍)。 现在可以在 MetaEditor 中打开该文件。



将 Specifications.txt 添加到同一文件夹。 它设置品种属性。

2. 输入

下一步是确定数据格式和位置,选择文件属性并设置自定义品种的名称。 如何填充字段的示例如下所示:



数据应传送到面板。 在此版本中使用固定点差,因此不对浮动点差进行建模。 因此,您应在此处输入适当的点差值。



填写完整文件名,包括扩展名。

现在,在单击“OK”之前,请指定必要的品种规格。 它们可以在之前放在 MQL5/Files 当中的 Specifications.txt 文件中找到。

在 MetaEditor 中编辑文本文件非常方便。 主要原因是 MetaEditor 支持数据高亮显示。 如果您无法理解任何属性,请将光标悬停在其上并按 F1。



属性以红色高亮显示,数值则以绿色显示。 注释掉的属性(//)不会用到,并以绿色显示。 请注意,逗号用于数据分离。 编辑时不要删除属性。 为了避免错误,您应该保留现有格式。

若要编辑属性,请取消所需属性注释(删除“//”),然后设置适当的值。 附加文件中设置的最小属性集合:价格增量,点值,最小手数,等等。

莫斯科交易所的 Sberbank 股票需要所有这些特征(在源文件中)。 其他金融产品需要不同的特征,因此您需要编辑属性。 

最低要求的属性集合位于文件的最开头。

通常,股票价格有 2 位小数(SYMBOL_DIGITS),而点数值等于 0.01 卢布。 股票期货价格的小数位数为 0,点值为 1 卢布。 请参阅 moex.com 上的规格。

设置完所有必需属性后,单击“OK”。 创建的自定义品种将显示在导航窗口中。 在我的示例中,它以绿色高亮显示。


打开图表进行检查:



一切都很好,所以现在可以在 策略测试器 中测试自定义品种。

自定义品种设置的执行方式与标准品种类似。 在此重要的一点是正确配置品种规格。

例如,我们使用自己的数据来测试终端中可用的任意标准智能交易系统(此处为移动平均线):

 


一切都按预期工作。 如果需要添加新报价或更改属性,只需对现有品种重复上述操作即可。 如果规格未更改,请单击“OK”而无需编辑属性。


文件

位于文件夹中的附加文件,应保存在计算机中:

  • CreateCustomSymbol 脚本和代码: MQL5\Scripts
  • CreateSymbolPanel 智能交易系统和代码: MQL5\Experts
  • 包含文件 FormatFunctions, PropertiesSet, Specification: MQL5\Include
  • 品种设定的文本文件: MQL5\Files


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

附加的文件 |
MQL5.zip (526.52 KB)
PropertiesSet.mqh (35.55 KB)
Specification.mqh (21.21 KB)
Specifications.txt (8.04 KB)
将概率论应用于缺口交易 将概率论应用于缺口交易

在本文中,我们将应用概率论和数理统计方法来创建并测试交易策略。 我们还将利用价格和随机漫游之间的差值来寻找最佳交易风险。 事实证明,如果价格表现为零漂移随机漫游(没有方向趋势),那么盈利交易是不可能的。

运用 MQL5 和 MQL4 开发品种选择和导航实用程序 运用 MQL5 和 MQL4 开发品种选择和导航实用程序

经验丰富的交易者非常清楚交易中最劳神的事情并非开单和跟踪持仓,而是选择交易品种并寻找入场点。 在本文中,我们将开发一款 EA,可为您简化依据经纪商所提供交易产品搜索入场点的任务。

利用 MQL5 和 MQL4 实现的选择和导航实用程序:添加"homework"选项卡并保存图形对象 利用 MQL5 和 MQL4 实现的选择和导航实用程序:添加"homework"选项卡并保存图形对象

在本文中,我们打算扩展先前创建的实用程序功能,添加用于选择所需品种的选项卡。 我们还将学习如何保存我们在特定品种图表上创建的图形对象,这样我们就不必再次创建它们。 此外,我们将发掘如何仅使用已操控经指定网站初步遴选的品种。

MQL5.community - 用户手册 MQL5.community - 用户手册

如果你已经在本社区成功注册,那么你很可能会问:怎样在我发送的消息中插入图片?怎样格式化MQL5源代码?我的私信保存在哪?诸如此类的很多问题。本文我们为您准备了一些实用技巧,帮助你熟悉MQL5.community,并充分利用其提供的功能。