概述
继续为 EA 交易和指标中的技术指标创建模板的主题,该主题从与创建仪表盘相关的文章中开始,然后在另外三篇文章中发展,其中我们探讨了振荡器、交易量指标和比尔威廉姆斯以及趋势指标,今天我们将开始创建多交易品种、多周期指标的主题。其中包括在当前图表上运行但使用其他交易品种和(或)其他图表时间框架的数据计算的指标。我们将创建一个基础多指标类和一组基于所有标准指标类型的类。我们还将为自定义指示器创建一个类,使用该类可以将任何指标“转换”为多交易品种和多周期指标。对于所有指标类,我们将创建一个统一集合类。程序中创建的所有指标都将被放入此集合中。此外,还可以使用方法访问集合中任何创建的指标,以获得相关的指标数据。最终目标是开发一种方便的工具,用于创建多指标和处理其数据。
基本原则
为了正确理解指标操作的逻辑,让我们试着了解它是如何工作的。指标分为两部分:计算和绘图。这些部分中的每一个都对另一个一无所知。当创建指标时,终端子系统在图表上查找这样的指标是否存在。在这种情况下,它会查找具有相同名称和参数的指标。如果这样的指标已经在图表上运行,或者已经为此图表以编程方式创建,则终端将使用现有指标的句柄,而不是创建新指标。指标的绘图部分使用其句柄从计算部分接收所需的数据。可能存在多个绘图部件同时访问一个计算部件的情况。
函数将指定数量的指定指标缓冲区的数据接收到“buffer”数组中。
复制数据的元素(具有索引buffer_num的指标缓冲区)从开始位置开始计数,从现在到过去,也就是说,等于0的开始位置表示当前柱(当前柱的指标值)。
如果事先不知道要复制的数据量,建议使用动态数组作为目标数组缓冲区,因为CopyBuffer()会尝试将接收数组的大小分配到复制的数据的量。如果接收数组缓冲区是一个指标缓冲区(之前由SetIndexBufer()函数分配的用于存储指标值的数组),则允许部分复制。
如果需要将指标值部分复制到另一个数组(而不是指标缓冲区),则应使用一个中间数组,将所需数量复制到该数组中。从这个中间数组中,逐个成员将所需数量的值复制到接收数组的所需位置。
如果需要复制预定量的数据,建议使用静态分配的缓冲区,以避免不必要的内存重新分配。
接收数组的属性,即as_series=true或as_series=false,将被忽略:在复制过程中,最旧的元素将被复制到为数组分配的物理内存的开头。有三种函数选项。
按根据初始位置和所需元素的数量的访问
int CopyBuffer(
int indicator_handle, // indicator handle
int buffer_num, // indicator buffer index
int start_pos, // starting point
int count, // amount to copy
double buffer[] // array the data to be copied to
);
按初始日期和所需元素数量访问
int CopyBuffer(
int indicator_handle, // indicator handle
int buffer_num, // indicator buffer index
datetime start_time, // starting date
int count, // amount to copy
double buffer[] // array the data to be copied to
);
按所需时间间隔的初始日期和最终日期访问
int CopyBuffer(
int indicator_handle, // indicator handle
int buffer_num, // indicator buffer index
datetime start_time, // starting date
datetime stop_time, // end date
double buffer[] // array the data to be copied to
);
参数
indicator_handle
[in] 通过相关指标函数获得的指标句柄。
buffer_num
[in] 指标缓冲区的编号。
start_pos
[in] 复制的第一个元素的索引。
count
[in] 复制的元素数。
start_time
[in] 第一个元素对应的柱的时间。
stop_time
[in] 最后一个元素对应的柱的时间。
buffer[]
[out] double类型的数组。
返回值
复制的数组元素数,如果出现错误,则为-1。
注意
当从指标请求数据时,如果请求的时间序列尚未构建或应该从服务器下载,则函数会立即返回-1。在这种情况下,会启动所需数据的下载或构建。
当从EA或脚本请求数据时,如果终端在本地没有适当的数据,则会启动从服务器的下载。另外,如果可以从本地历史构建数据,但尚未准备好,则开始构建必要的时间序列。该函数返回超时到期时准备好的数量。
我们将使用该函数的第一个版本,即基于起始位置(y为循环索引)和所需元素数量的访问。
对象结构如下:
多交易品种、多周期指标的基类,包含所有指标通用的主要函数;
从基本对象派生的类,这些类按其类型描述每个指标;
指标的集合–您可以使用此类创建任何指标并将其添加到集合中。该类将为用户提供创建指标和从中接收数据的所有工具。
使用非当前图表中的数据时,为了避免时间序列被释放,您应该至少每两分钟访问一次此时间序列。在这种情况下,时间序列将被“保留”,这将加快对它的访问(不需要每次都等待数据同步)。在类构造函数中,我们将执行构建指标的时间序列的第一次调用。这将允许我们开始下载时间序列(如果无法在本地访问)。然后,每隔90秒,在基类计时器中,我们将访问时间序列来保存它。
资源高效计算
为了计算和显示指标,我们需要资源高效的计算。第一次启动时,将计算所有历史数据的指标,然后将计算所有后续分时的一个或两个柱形。
OnCalculate()处理函数具有预定义的常量变量,这些变量存储(时间序列或数组的)输入数据大小以及在上一次OnCalculate()调用 期间计算的数据量:
基于数据数组的计算
int OnCalculate(
const int rates_total, // price[] array size
const int prev_calculated, // number of processed bars at the previous call
const int begin, // index number in the price[] array meaningful data starts from
const double& price[] // array of values for calculation
);
基于当前时间段时间序列的计算
int OnCalculate(
const int rates_total, // size of input timeseries
const int prev_calculated, // number of processed bars at the previous call
const datetime& time{}, // Time array
const double& open[], // Open array
const double& high[], // High array
const double& low[], // Low array
const double& close[], // Close array
const long& tick_volume[], // Tick Volume array
const long& volume[], // Real Volume array
const int& spread[] // Spread array
);
这种数据的存在使得能够快速、有效地计算指标。例如,让我们考虑“limit”值的计算:
//--- Number of bars for calculation
int limit=rates_total-prev_calculated;
//--- If limit > 1, then this is the first calculation or change in the history
if(limit>1)
{
//--- specify all the available history for calculation
limit=rates_total-1;
//--- If the indicator has any buffers, initialize them here with empty values set for these buffers
}