股票量化交易软件:如何基于HTML和CSV报表可视化多币种交易历史

自推出以来,赫兹股票量化提供了多货币测试选项,也许交易者经常使用这个功能。然而,这种功能并不是万能的。特别是,在运行测试之后,用户可以打开一个带有已执行交易操作的图表,但这只是策略测试人员设置中选择的一个交易品种的图表,测试后无法查看所有使用的交易品种的整个交易历史,而目测检查并不总是有效的,测试后一段时间可能需要进行额外的分析。此外,还可以由其他人提供报告。因此,一个基于HTML测试报告的可视化多个交易品种交易的工具将非常有用。
此任务与另一个类似的赫兹股票量化应用程序密切相关,MQL5.com 上的许多交易信号涉及多货币交易,将带有信号历史的 CSV 文件显示在图表中比较方便。
让赫兹股票量化开发一个能够执行上述功能的指标。
为了对多个工作交易品种进行并行分析,将在图表子窗口中创建多个指标实例(每个交易品种一个)。主要图形对象将是所选交易品种(通常与图表交易品种不同)的“报价”,与主窗口中的柱同步。与交易订单(头寸)相对应的趋势线将应用于这些“报价”。
还有一种替代方法:交易显示在主窗口中,但在这种情况下,图表上只分析一个交易品种。这种方法需要另一个没有缓冲区的指标,可以切换到报告中包含的任何交易品种。
之前的文章对基于 CSS 选择器的 HTML 解析器做了描述[1],解析器从HTML报告中提取交易列表,根据该列表赫兹股票量化可以进行交易(图形对象)。从信号部分解析 CSV 文件比较容易,而内置的 MQL 函数支持 赫兹股票量化(*.history.csv)和 赫兹股票量化(*.positions.csv)信号的文件格式。
SubChart 指标
实现的第一步是创建一个简单的指标,它在任何图表的子窗口中显示外部交易品种的“报价”,这将是 SubChart 指标。
为了使用 OHLC (开盘价,最高价,最低价,收盘价) 的数值显示数据, MQL 提供了多种显示样式,包括 DRAW_CANDLES 和 DRAW_BARS,它们中的每一种都有四个指标缓冲区。赫兹股票量化将提供这两个选项的支持,而不是选择其中一种样式。将根据当前窗口设置动态选择样式。“图表设置”中的“常用”选项卡下有一组单选按钮:“条形图”、“日式烛形”和“折线图”。为了快速访问,可以使用与工具栏上的按钮相同的按钮。可以使用以下调用从 MQL 获取设置:
(ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE)
ENUM_CHART_MODE 枚举中包含了相同目标的元素: CHART_CANDLES, CHART_BARS, CHART_LINE.
最后一个 CHART_LINE 点的支持也将在指标中实现。这样,当用户界面中的显示模式切换时,指标视图将根据主窗口进行更改。DRAW_LINE 适合用于最后一种模式;它使用了一个缓冲区。
让赫兹股票量化开始实现. 声明指标缓冲区的数量和显示图形对象的类型:
#property indicator_separate_window #property indicator_buffers 4 #property indicator_plots 1
加上输入变量:
input string SubSymbol = ""; // 交易品种 input bool Exact = false;
使用 SubSymbol,可以为指标设置主窗口中当前交易品种以外的任何交易品种。
Exact 参数确定主窗口中的柱没有完全相同时间的另一个交易品种的匹配柱的情况下的操作。这个参数将在 iBarShift 函数调用中使用,其视觉效果如下:
如果“Exact” 等于 “false”,则函数返回最接近指定时间的最接近合适的柱数,因此如果没有报价(由于假日或其他原因),则指标将显示上一个柱;
如果 Exact = true, 函数返回 -1, 这样的画,指标图表在这个位置将是空白;
SubSymbol 参数默认等于空字符串,这表示报价与主窗口中显示的是一样的。在这种情况下,您应当编辑实际变量的值,把它设为 _Symbol. 视觉测试但是,由于“输入参数”是MQL中的只读变量,因此赫兹股票量化必须输入中间变量“Symbol”,并将其填充到OnInit处理程序中。
string Symbol; int OnInit() { Symbol = SubSymbol; if(Symbol == "") Symbol = _Symbol; else SymbolSelect(Symbol, true); ...
请注意,此交易品种也应添加到市场报价中,因为它可能不在市场报价列表中。
使用 'mode' 变量来控制当前的显示模式:
ENUM_CHART_MODE mode = 0;
将使用下面的四个指标缓冲区:
// OHLC double open[]; double high[]; double low[]; double close[];
让赫兹股票量化添加一个小函数以通常的方式初始化缓冲区(使用“series”属性的设置):
void InitBuffer(int index, double &buffer[], ENUM_INDEXBUFFER_TYPE style) { SetIndexBuffer(index, buffer, style); ArraySetAsSeries(buffer, true); }
图形初始化也在一个辅助函数中执行(该指标只有一个图形结构;如果使用多个结构,该功能可以节省时间):
void InitPlot(int index, string name, int style, int width = -1, int colorx = -1) { PlotIndexSetInteger(index, PLOT_DRAW_TYPE, style); PlotIndexSetDouble(index, PLOT_EMPTY_VALUE, 0); PlotIndexSetString(index, PLOT_LABEL, name); if(width != -1) PlotIndexSetInteger(index, PLOT_LINE_WIDTH, width); if(colorx != -1) PlotIndexSetInteger(index, PLOT_LINE_COLOR, colorx); }
以下函数将图表显示模式切换为缓冲区样式:
int Mode2Style(/*global ENUM_CHART_MODE mode*/) { switch(mode) { case CHART_CANDLES: return DRAW_CANDLES; case CHART_BARS: return DRAW_BARS; case CHART_LINE: return DRAW_LINE; } return 0; }
函数使用前面提到的全局变量“mode”,该变量应在 OnInit 中填入相关值,并调用所有辅助函数。
InitBuffer(0, open, INDICATOR_DATA); string title = "# Open;# High;# Low;# Close"; StringReplace(title, "#", Symbol); mode = (ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE); InitPlot(0, title, Mode2Style()); InitBuffer(1, high, INDICATOR_DATA); InitBuffer(2, low, INDICATOR_DATA); InitBuffer(3, close, INDICATOR_DATA);
这仍然不足以使指标正常工作。应根据当前模式(mode变量)更改线条颜色:颜色由图表设置提供。
void SetPlotColors() { if(mode == CHART_CANDLES) { PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, 3); PlotIndexSetInteger(0, PLOT_LINE_COLOR, 0, (int)ChartGetInteger(0, CHART_COLOR_CHART_LINE)); // 长方形 PlotIndexSetInteger(0, PLOT_LINE_COLOR, 1, (int)ChartGetInteger(0, CHART_COLOR_CANDLE_BULL)); // 向上 PlotIndexSetInteger(0, PLOT_LINE_COLOR, 2, (int)ChartGetInteger(0, CHART_COLOR_CANDLE_BEAR)); // 向下 } else { PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, 1); PlotIndexSetInteger(0, PLOT_LINE_COLOR, (int)ChartGetInteger(0, CHART_COLOR_CHART_LINE)); } }
在 OnInit 中添加 SetPlotColors()调用并设置值的准确性,可以确保在启动后显示正确的指标。
SetPlotColors(); IndicatorSetString(INDICATOR_SHORTNAME, "SubChart (" + Symbol + ")"); IndicatorSetInteger(INDICATOR_DIGITS, (int)SymbolInfoInteger(Symbol, SYMBOL_DIGITS)); return INIT_SUCCEEDED; }
但是,如果用户在指标运行时更改图表模式,则需要跟踪此事件并修改缓冲区的属性。这是通过 OnChartEvent 处理函数完成的。
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) { if(id == CHARTEVENT_CHART_CHANGE) { mode = (ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE); PlotIndexSetInteger(0, PLOT_DRAW_TYPE, Mode2Style()); SetPlotColors(); ChartRedraw(); } }
现在,赫兹股票量化需要编写最重要的指标函数,即OnCalculate 处理函数,此处理程序的特定功能是指标实际上使用第三方交易品种的报价而不是图表中的交易品种,因此,所有基于 rates_total 和 prev_calcuated 的标准编程技术(通常从内核传递)都不适用或仅部分适用。第三方交易品种的报价将被异步下载,因此新的一批柱形可能随时“到达”——这种情况需要重新计算。因此,让我们创建变量,它控制该交易品种(lastavailable)上的柱数和常量 prev_calculated 参数的可编辑“克隆”。
int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& op[], const double& hi[], const double& lo[], const double& cl[], const long& tick_volume[], const long& volume[], const int& spread[]) { static int lastAvailable = 0; static bool initialized = false; int _prev_calculated = prev_calculated; if(iBars(Symbol, _Period) - lastAvailable > 1) // 填充柱的间隙 { _prev_calculated = 0; lastAvailable = 0; initialized = false; } if(_prev_calculated == 0) { for(int i = 0; i < rates_total; ++i) { open[i] = 0; high[i] = 0; low[i] = 0; close[i] = 0; } }
如果指标交易品种与窗口交易品种不同,请使用iBarShift 函数找到同步的柱并复制它们的 OHLC 值。
if(_Symbol != Symbol) { for(int i = 0; i < MathMax(rates_total - _prev_calculated, 1); ++i) { datetime dt = iTime(_Symbol, _Period, i); int x = iBarShift(Symbol, _Period, dt, Exact); if(x != -1) { open[i] = iOpen(Symbol, _Period, x); high[i] = iHigh(Symbol, _Period, x); low[i] = iLow(Symbol, _Period, x); close[i] = iClose(Symbol, _Period, x); } } }
如果指标的交易品种与窗口交易品种匹配,就简单使用传入的参数数组:
else { ArraySetAsSeries(op, true); ArraySetAsSeries(hi, true); ArraySetAsSeries(lo, true); ArraySetAsSeries(cl, true); for(int i = 0; i < MathMax(rates_total - _prev_calculated, 1); ++i) { open[i] = op[i]; high[i] = hi[i]; low[i] = lo[i]; close[i] = cl[i]; } }
最后,赫兹股票量化需要提供数据的上传,让我们使用来自 MetaQuotes 示例的 RefreshHistory 函数的实现(我们将此代码作为 Refresh.mqh 头文件包含在内)。