请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3364677 新帖:0

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

汇市江湖百晓生发表于:4 月 17 日 18:27回复(1)

概述

两种基本类型的金融市场包括交易所和场外交易市场。 我们可以使用现代 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


全部回复

0/140

达人推荐

量化课程

    移动端课程