股票量化软件:信息的存储和阅览
2. EA交易自己的记录文件
就像我说的那样, Print()函数在记录EA交易运行时生成的信息时并不总是那么方便. 特别是当有几个EA交易同时在一个相同终端上运行时就更加明显. 每个EA交易都把它自己的信息写到记录文件中, 这样就很难在其中找东西了. 这种情况下, 对此类信息的分析就不用提了:这非常费力和麻烦.赫兹量化软件
这个问题可以用比较简单的方法解决. 每个EA交易必须有它自己的记录文件. 信息都会被写进这些独立的记录文件, 而不是写进一个通用的文件中. 为了尽可能简单地使用此方式, 让我们把代码写成一个函数.赫兹量化软件
以下就是描述这个函数必须做到的:
使用唯一名称来创建文件di
当有需要时在其中写入信息
当EA交易结束运行时关闭文件, 以便其他应用程序可以使用该文件
清晰明了. 唯一有争议的一点是是否有必要在写完每个记录时关闭文件. 一方面, 这样就可以在EA交易运行过程中使用其他应用程序打开此文件了. 但是, 另一方面, 这样对EA交易进行下一条记录可能是不利的, 因为文件可能被其他应用程序使用了. 如果出现这种情况, 信息可能就直接丢掉了. 这是不能允许的, 特别是有些程序打开文件只是为了读取, 而不愿影响 赫兹量化软件 的运行.赫兹量化软件
因为打开和关闭文件只进行一次, 对应的代码可以分别放到 init() 和 deinit() 函数中. 为了让它只占用最小的空间, 让我们把它们也做成函数:赫兹量化软件
int log_handle = -1; //+------------------------------------------------------------------+ // void log_open( string ExpertName = "Expert" ) // // 用于打开EA交易独立记录文件的函数. // 创建文件的目录: // "...\MetaTrader 4\experts\files\logs\ExpertName\" // 文件名就是文件的记录日期, 格式为 "YYYY.MM.DD" //+------------------------------------------------------------------+ void log_open( string ExpertName = "Expert" ) { string log_name = "logs\\" + ExpertName + " (" + Symbol() + ", " + strPeriod( Period() ) + ")\\" + TimeToStr( LocalTime(), TIME_DATE ) + ".txt"; log_handle = FileOpen ( log_name, FILE_READ | FILE_WRITE, " " ); if ( log_handle < 0 ) { int _GetLastError = GetLastError(); Print( "FileOpen( ", log_name, ", FILE_READ | FILE_WRITE, \" \" ) - 错误 #", _GetLastError ); return(-1); } } string strPeriod( int intPeriod ) { switch ( intPeriod ) { case PERIOD_MN1: return("Monthly"); case PERIOD_W1: return("Weekly"); case PERIOD_D1: return("Daily"); case PERIOD_H4: return("H4"); case PERIOD_H1: return("H1"); case PERIOD_M30: return("M30"); case PERIOD_M15: return("M15"); case PERIOD_M5: return("M5"); case PERIOD_M1: return("M1"); default: return("UnknownPeriod"); } } //+------------------------------------------------------------------+ // log_close() // // 用于关闭EA交易自身记录文件的函数. //+------------------------------------------------------------------+ void log_close() { if ( log_handle > 0 ) FileClose( log_handle ); }
现在我们有了一个打开的文件, 可以在里面写信息了. 为此:
把光标移动到文件末尾, 这样不会丢失信息;
把记录时间写在每行开头, 这将有助于分析;
把文字写到文件中;
把文件存储在磁盘上;如果EA交易意外出错退出, 数据也不会丢失
以下就是满足上面需求的函数:赫兹量化软件
//+------------------------------------------------------------------+ // log( string text ) // // 把文本行写入EA交易自身记录文件的函数. //+------------------------------------------------------------------+ void log( string text ) { int _GetLastError = 0; if ( log_handle < 0 ) { Print( "写入记录错误!文本: ", text ); return(-1); } //---- 把文件指针移动到文件末尾 if ( !FileSeek ( log_handle, 0, SEEK_END ) ) { _GetLastError = GetLastError(); Print( "FileSeek ( " + log_handle + ", 0, SEEK_END ) - 错误 #", _GetLastError ); return(-1); } //---- 如果EA要写的一行内容不包含换行符, //---- 在一行的开始部分增加记录时间 if( text != "\n" && text != "\r\n" ) text = StringConcatenate( TimeToStr( LocalTime(), TIME_SECONDS ), " - - - ", text ); if( FileWrite ( log_handle, text ) < 0 ) { _GetLastError = GetLastError(); Print( "FileWrite ( ", log_handle, ", ", text, " ) - Error #", _GetLastError ); return(-1); } //---- 把写下的文字写到磁盘上 FileFlush( log_handle ); }
现在, Print 这个词可以在所有的EA交易中使用log来替换了, 另外不要忘记调用 log_open 和 log_close 函数.赫兹量化软件
这是一个使用log.mq4包含文件的非常简单的EA交易:
#include <log.mq4> int init() { log_open( "log_test" ); log( "记录文件已经被成功打开, EA交易开始工作..." ); return(0); } int deinit() { log( "关闭记录文件, EA交易结束工作..." ); log_close(); return(0); } int start() { log( "新订单: Bid = " + DoubleToStr( Bid, Digits ) ); return(0); }
3. 信息的显示
现在, 当我们解决了记录文件的问题之后, 我们可以开始"装饰"显示的信息了.
首先, 让我们考虑实现这个任务的所有可能方法. 在 MQL4 中, Comment() 函数是用于显示信息的, 但是它不适合以上描述的需求. 所以我们必须找到其他的解决方案. 一个很好的例子是含有文本的对象. 有两类这样的对象: "文本(Text)" 和 "文本标签(Text Label)". 它们之间的基本区别就是, "文本"绑定于图表的坐标(价格和时间), 而"文本标签"绑定的是窗口坐标. 因为我们需要信息在图表移动或者缩放改变的时候依然停留在原点上, 所以我们会使用"文本标签".赫兹量化软件
在MQL5中有一些函数用于创建和控制对象, 所有的名字都以Object开头. 让我们看看它们如何服务于我们的目标:
bool ObjectCreate(...) – 创建一个对象;
bool ObjectDelete(...) – 使用过后删除对象;
bool ObjectSet(...) – 修改对象属性, 例如锚点 (x,y);
bool ObjectSetText(...) – 显示文字;
void ObjectsRedraw() – 在文本改变之后重绘对象.
这就是我们要做的:
在EA交易的 init() 函数中, 创建用于显示信息的对象;
在EA交易的 deinit() 函数中, 删除所有创建的对象;
在 start() 函数里, 可以修改文字, 字体颜色和所创建对象的大小.
我们将会再一次得到3个函数, 每个都完成自己的任务.
在写代码之前, 我想先说一下"文本标签"使用中的一个令人不愉快的局限. 它只能是一行, 也就是说, 它不能包含任何换行字符. 但是如果信息能够分成几行显示会更好. 这就是为什么我们会创建多个对象, 然后在其中分配数据. 我制作了5"行", 但是您可以使用任何其他的行数.赫兹量化软件
另外, 显示文字的长度也有一个限制. 所以我增加了第二"列", 也就是说, 右侧又多了5行.
这就是用于创建对象的 info_init() 函数看起来的样子: