900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 股票量化软件:在交易中应用 OLAP4--定量和可视化分析测试器报告

股票量化软件:在交易中应用 OLAP4--定量和可视化分析测试器报告

时间:2019-12-13 18:40:39

相关推荐

股票量化软件:在交易中应用 OLAP4--定量和可视化分析测试器报告

在应用程赫兹序图形上

OLAP 的 GUI 中心是专门开发的 CGraphicInPlot 可视组件。 第二篇文章里展现的第一个实现略有瑕疵。 其中包括在坐标轴上显示标签。 必要时,我们设法在水平 X 轴上显示选择器单元的名称(例如,星期几的名称,或是货币名称)。 但是,在所有其他情况下,数字均按“原样”显示,而这对于用户并不友好。 Y 轴需要另一套自定义,通常显示汇总值。 取决于设置,它可以显示选择器值,这便是需要改进之处。 方法显示不佳的一个示例,譬如请求一个品种的平均持仓时间。赫兹股票量化软件

按品种的平均持仓生存期(单位秒)

因为 Y 轴显示的不是选择器(其值四舍五入到立方单元大小),而显示的是以秒为单位的汇总持续值,所以这么大的数字难以理解。 为了解决此问题,我们尝试把秒数除以当前时间帧柱线的持续时间。 在此情况下,这些值将代表柱线的数量。 为此,我们需要将某个标志传递给 CGraphicInPlot 类,进而会传递给处理坐标轴的 CAxis 类。 改变操作模式的标志可能很多。 因此,在文件 Plot.mqh 中为它们保留一个名为 AxisCustomizer 的特殊新类。赫兹股票量化软件

class AxisCustomizer{public:const CGraphicInPlot *parent;const bool y; // true for Y, false for Xconst bool periodDivider;const bool hide;AxisCustomizer(const CGraphicInPlot *p, const bool axisY,const bool pd = false, const bool h = false):parent(p), y(axisY), periodDivider(pd), hide(h) {}};

潜在地,要把各种标签显示功能添加到该类之中。 但于此刻,它仅存储坐标轴类型(X 或 Y)的符号,和少数逻辑选项,例如 periodDivider 和 '隐藏'。 第一个选项意味着值应除以 PeriodSeconds()。 第二个选项将在以后讲述。赫兹股票量化软件

该类的对象通过特殊方法注入 CGraphicInPlot 当中:

class CGraphicInPlot: public CGraphic{...void InitAxes(CAxis &axe, const AxisCustomizer *custom = NULL);void InitXAxis(const AxisCustomizer *custom = NULL);void InitYAxis(const AxisCustomizer *custom = NULL);};void CGraphicInPlot::InitAxes(CAxis &axe, const AxisCustomizer *custom = NULL){if(custom){axe.Type(AXIS_TYPE_CUSTOM);axe.ValuesFunctionFormat(CustomDoubleToStringFunction);axe.ValuesFunctionFormatCBData((AxisCustomizer *)custom);}else{axe.Type(AXIS_TYPE_DOUBLE);}}void CGraphicInPlot::InitXAxis(const AxisCustomizer *custom = NULL){InitAxes(m_x, custom);}void CGraphicInPlot::InitYAxis(const AxisCustomizer *custom = NULL){InitAxes(m_y, custom);}

若此类对象尚未创建且未传递给图形类时,标准库将按常规方式显示为 AXIS_TYPE_DOUBLE 数字。赫兹股票量化软件

在此,我们用标准库的方式来自定义坐标轴上的标签:轴类型设置为等于 AXIS_TYPE_CUSTOM,且通过 ValuesFunctionFormatCBData 传递指向 AxisCustomizer 的指针。 进而,它由 CGraphic 基类传递给 CustomDoubleToStringFunction 标签绘制函数(在上述代码中调用 ValuesFunctionFormat 设置)。 当然,我们需要 CustomDoubleToStringFunction 函数,该函数早前曾以简化形式实现,没有 AxisCustomizer 类对象(CGraphicInPlot 图表充当设置对象)。

string CustomDoubleToStringFunction(double value, void *ptr){AxisCustomizer *custom = dynamic_cast<AxisCustomizer *>(ptr);if(custom == NULL) return NULL;// check optionsif(!custom.y && custom.hide) return NULL; // case of X axis and "no marks" mode// in simple cases return a stringif(custom.y) return (string)(float)value; const CGraphicInPlot *self = custom.parent; // obtain actual object with cache if(self != NULL){... // retrieve selector mark for value}}

AxisCustomizer 定制对象存储在 CPlot 类中,CPlot 类是一个 GUI 控件(继承自 CWndClient),且是 CGraphicInPlot 的容器:赫兹股票量化软件

class CPlot: public CWndClient{private:CGraphicInPlot *m_graphic;ENUM_CURVE_TYPE type;AxisCustomizer *m_customX;AxisCustomizer *m_customY;...public:void InitXAxis(const AxisCustomizer *custom = NULL){if(CheckPointer(m_graphic) != POINTER_INVALID){if(CheckPointer(m_customX) != POINTER_INVALID) delete m_customX;m_customX = (AxisCustomizer *)custom;m_graphic.InitXAxis(custom);}}...};

因此,在 m_customX 和 m_customY 对象中设置坐标轴不仅可以在 CustomDoubleToStringFunction中 的数值格式化阶段使用,而是可以更早地使用它们,如调用 CurveAdd 方法之一时,将数据数组传递给 CPlot。 例如:

CCurve *CPlot::CurveAdd(const PairArray *data, const string name = NULL){if(CheckPointer(m_customY) != POINTER_INVALID) && m_customY.periodDivider){for(int i = 0; i < ArraySize(data.array); i++){data.array[i].value /= PeriodSeconds();}}return m_graphic.CurveAdd(data, type, name);}

该代码展示了 periodDivider 选项的用法,该选项将所有值除以 PeriodSeconds()。 此操作的执行应在标准库接收数据并计算它们的网格大小之前。 这一步很重要,因为网格计数之后,在 CustomDoubleToStringFunction 函数中再进行自定义则为时已晚。赫兹股票量化软件

对话框中的调用者代码必须在多维数据集构建时创建,并初始化 AxisCustomizer 对象。 例如:

AGGREGATORS at = ... // get aggregator type from GUIENUM_FIELDS af = ... // get aggregator field from GUISORT_BY sb = ...// get sorting mode from GUIint dimension = 0; // calculate cube dimensions from GUIfor(int i = 0; i < AXES_NUMBER; i++){if(Selectors[i] != SELECTOR_NONE) dimension++;}bool hideMarksOnX = (dimension > 1 && SORT_VALUE(sb));AxisCustomizer *customX = NULL;AxisCustomizer *customY = NULL;customX = new AxisCustomizer(m_plot.getGraphic(), false, Selectors[0] == SELECTOR_DURATION, hideMarksOnX);if(af == FIELD_DURATION){customY = new AxisCustomizer(m_plot.getGraphic(), true, true);}m_plot.InitXAxis(customX);m_plot.InitYAxis(customY);

此处,m_plot 是对话框变量,存储 CPlot 控件。 下面是 OLAPDialog::process 方法的完整代码,展示了它是如何实际执行的。 这是上面的示例,其中的 periodDivider 模式会自动启用:赫兹股票量化软件

按品种的平均持仓生存期(当前时间帧,D1)

AxisCustomizer 中的另一个变量 “hide”,提供了沿 X 轴完全隐藏标签的功能。 当选择按多维数组中的数值进行排序时,需要此模式。 在此情况下,每行中的标签都有其自己的顺序,故此沿 X 轴没有任何显示。 多维数据集支持排序,它也可以在其他模式下使用,尤其是按标签。

“隐藏”选项在 CustomDoubleToStringFunction 内部操作。 此函数的标准行为意味着选择器的存在。对于 X 轴,选择器的标签被缓存在专门的 CurveSubtitles 类中,并按照网格划分索引返回到图表。 不过,对于任何横坐标,设置的“隐藏”标志可在开始时终止此过程,且该函数将返回 NULL(不可显示数值)。

需要在图形中解决的第二个问题与直方图的渲染有关。 当图表中显示若干行(数据向量)时,直方图柱线相互重叠,并且其中最大的柱线可以彻底遮掩其他所有柱线。

CGraphic 基类含有虚拟 HistogramPlot 方法。 必须覆盖它,以便将每列可视化分隔。 最好在 CCurve 对象中有一个自定义字段,存储任意数据(数据将根据客户代码按需解读)。 不幸的是,这样的字段不存在。 因此,我们借用当前项目中未用的标准属性之一。 我选择了 LinesSmoothStep。 利用 CCurve::LinesSmoothStep 赋值方法,我们的调用者方代码将序列号写入其中。 利用新 HistogramPlot 实现中的 CCurve::LinesSmoothStep 取值方法,可以轻松获得此代码。 这是如何在 LinesSmoothStep 中写代码的示例:

CCurve *CGraphicInPlot::CurveAdd(const double &x[], const double &y[], ENUM_CURVE_TYPE type, const string name = NULL){CCurve *c = CGraphic::CurveAdd(x, y, type, name);c.LinesSmoothStep((int)CGraphic::CurvesTotal()); // +...return CacheIt(c);}

了解行的总数,和当前行号后,可以在渲染时将其每个点稍微向左或向左偏移。 这是 HistogramPlot 的改编版本。 修改后的行带有 “*” 注释标记;新添加的行则用 “+” 标记。

void CGraphicInPlot::HistogramPlot(CCurve *curve) override{const int size = curve.Size();const double offset = curve.LinesSmoothStep() - 1; // +double x[], y[];int histogram_width = curve.HistogramWidth();if(histogram_width <= 0) return;curve.GetX(x);curve.GetY(y);if(ArraySize(x) == 0 || ArraySize(y) == 0) return;const int w = m_width / size / 2 / CGraphic::CurvesTotal();// +const int t = CGraphic::CurvesTotal() / 2; // +const int half = ((CGraphic::CurvesTotal() + 1) % 2) * (w / 2);// +int originalY = m_height - m_down;int yc0 = ScaleY(0.0);uint clr = curve.Color();for(int i = 0; i < size; i++){if(!MathIsValidNumber(x[i]) || !MathIsValidNumber(y[i])) continue;int xc = ScaleX(x[i]);int yc = ScaleY(y[i]);int xc1 = xc - histogram_width / 2 + (int)(offset - t) * w + half; // *int xc2 = xc + histogram_width / 2 + (int)(offset - t) * w + half; // *int yc1 = yc;int yc2 = (originalY > yc0 && yc0 > 0) ? yc0 : originalY;if(yc1 > yc2) yc2++;else yc2--;m_canvas.FillRectangle(xc1,yc1,xc2,yc2,clr);}}

很快我们将检查其模样。

另一个恼人的时刻则与显示线条的标准实现有关。 如果数据为非数字值,则 CGraphic 将断行。 对于我们的任务来说这很糟糕,因为某些多维数据集单元可能不包含数据,且聚合器会在单元格里写入 NaN。 某些多维数据集,譬如若干部分中的累计余额总计,显示不正确,因为每笔成交的数值仅在一个部分中变化。 若要查看断线的负面影响,请查看第二篇文章里的图例“每个品种的余额曲线”。

为了解决此问题,另外重新定义了 LinesPlot 方法(请参阅源代码文件 Plot.mqh)。 操作结果如下所示,位于处理测试器标准文件相关的部分里。

最后,与标准库中零轴定义有关的最后一个图形问题。 在 CGraphic::CreateGrid 方法中按如下简单方式搜索零(显示 Y 轴的情况;且 X 轴的处理相同):

if(StringToDouble(m_yvalues[i]) == 0.0)...

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。