在时间序列中查找最大值和最小值

在处理报价时间序列的函数组中,有两个提供最简单聚合处理的函数:用于查找给定时间区间内序列的最大值和最小值,分别为 iHighestiLowest

int iHighest(const string symbol, ENUM_TIMEFRAMES timeframe, ENUM_SERIESMODE type, int count = WHOLE_ARRAY, int offset = 0)

int iLowest(const string symbol, ENUM_TIMEFRAMES timeframe, ENUM_SERIESMODE type, int count = WHOLE_ARRAY, int offset = 0)

这两个函数将返回特定时间序列类型中最大值/最小值的索引,时间序列类型由 symbol/timeframe参数对以及 ENUM_SERIESMODE 枚举元素(用于描述我们已熟知的报价字段)共同指定。

标识符

说明

MODE_OPEN

开盘价

MODE_LOW

最高价

MODE_HIGH

最低价

MODE_CLOSE

收盘价

MODE_VOLUME

分时报价成交量

MODE_REAL_VOLUME

实际成交量

MODE_SPREAD

点差

offset参数用于指定开始搜索的索引位置。编号方式与时间序列一致,即offset增大意味着向历史方向移动,而 0 号索引代表当前柱线(此为默认值)。分析的柱线数量通过 count参数指定(默认值为 WHOLE_ARRAY)。

如果出现错误,函数将返回 -1。使用 GetLastError 获取错误代码。

为了演示其中一个函数 (iHighest) 的工作原理,我们将修改上一节中通过柱线估算点差实际大小的示例,并对结果进行比较。当然,它们必须匹配。脚本的新版本附在文件 SeriesSpreadHighest.mq5中。

这些变化影响了 SpreadPerBar结构体以及 OnStart 内部的工作循环。

该结构体中添加了一些字段,这些字段允许你了解新函数的工作原理。由于算法的特性,这些字段不是必填字段。

struct SpreadPerBar
{
   datetime time;
   int spread;
   int max// through index of the M1 bar with a spread, the value of which is maximum
            // among all M1-bars within the current bar of the higher timeframe
   int num// number of M1 bars in the current bar of the higher timeframe
   int pos// initial index of the M1 bar within the current bar of the higher timeframe
};

主要修改影响了 OnStart,但这些修改都位于循环内部(所有其他代码片段保持不变)。

   for(int i = 0i < BarCount; ++i)
   {
      const datetime next = iTime(WorkSymbolTimeFramei);
      const datetime prev = iTime(WorkSymbolTimeFramei + 1);
      ...

当前柱线的边界(prevnext)的定义与之前相同。不过,我们不再将这些标签之间的时间序列元素复制到自有数组 spreads中,也不再随后对其调用ArrayMaximum 函数,而是确定构成更高时间范围当前柱线的 M1 柱线的索引及数量。这可以按以下方式完成。

iBarShift函数允许你确定 M1 历史数据中的偏移量(变量 p),该偏移量指向时间为next - 1 的柱线右边界所在位置。bars函数用于计算介于 prevnext - 1 之间的 M1 柱线数量(变量n)这两个值将作为参数传入 iHighest函数调用中,用于在从索引 p 开始的n 个 M1 柱线中,找出 MODE_SPREAD 类型的最大值。如果能够顺利找到最大值 (m > -1),我们只需使用 iSpread 获取相应值并将其存入结构体即可。

      const int p = iBarShift(WorkSymbolPERIOD_M1next - 1);
      const int n = Bars(WorkSymbolPERIOD_M1prevnext - 1);
      const int m = iHighest(WorkSymbolPERIOD_M1MODE_SPREADnp);
      if(m > -1)
      {
         peaks[i].spread = iSpread(WorkSymbolPERIOD_M1m);
         peaks[i].time = prev;
         peaks[i].max = m;
         peaks[i].num = n;
         peaks[i].pos = p;
      }
   }

将包含结果的数组输出到日志时,我们现在还会看到 M1 柱线的索引,其中包括更高时间范围柱线“起始”的位置,以及在该柱线中找到的最大点差的位置。“起始”一词之所以加上引号,是因为随着新的 M1 柱线不断生成,这些索引会逐渐递增,每条柱线的虚拟“起始位置”也会随之移动,不过历史柱线的开盘时间当然是保持不变的。

Maximal speeds per intraday bar
Processed 100 bars on EURUSD PERIOD_H1
               [time] [spread] [max] [num] [pos]
[ 0] 2021.10.12 15:00        0     7    60     7
[ 1] 2021.10.12 14:00        1    89    60    67
[ 2] 2021.10.12 13:00        1   181    60   127
[ 3] 2021.10.12 12:00        1   213    60   187
[ 4] 2021.10.12 11:00        1   248    60   247
[ 5] 2021.10.12 10:00        0   307    60   307
[ 6] 2021.10.12 09:00        1   385    60   367
[ 7] 2021.10.12 08:00        2   469    60   427
[ 8] 2021.10.12 07:00        2   497    60   487
[ 9] 2021.10.12 06:00        1   550    60   547
[10] 2021.10.12 05:00        1   616    60   607
[11] 2021.10.12 04:00        1   678    60   667
[12] 2021.10.12 03:00        1   727    60   727
[13] 2021.10.12 02:00        4   820    60   787
[14] 2021.10.12 01:00       16   906    60   847
[15] 2021.10.12 00:00       65   956    60   907
[16] 2021.10.11 23:00       15   967    60   967
[17] 2021.10.11 22:00        2  1039    60  1027
[18] 2021.10.11 21:00        1  1090    60  1087
[19] 2021.10.11 20:00        1  1148    60  1147
[20] 2021.10.11 19:00        2  1210    60  1207
[21] 2021.10.11 18:00        1  1313    60  1267
[22] 2021.10.11 17:00        1  1345    60  1327
[23] 2021.10.11 16:00        1  1411    60  1387
[24] 2021.10.11 15:00        2  1461    60  1447
[25] 2021.10.11 14:00        1  1526    60  1507
...

例如,在脚本启动时,标签为 2021.10.12 14:00 的柱线是从第 67 根 M1 柱线开始的(即它在 67 分钟前开盘),而在该 H1 柱线内具有最大点差的 M1 柱线的索引为 89。显然,该索引应小于前一个 H1 柱线起始位置的 M1 柱线编号:2021.10.12 13:00 - 其标记为 127 分钟前开盘。在该 H1 柱线中,又发现索引 181 处存在最大点差。而该数值小于更早的 2021.10.12 12:00 柱线所对应的索引 187。

posmax 列中的索引值会持续递增,这是因为我们按照从当前到过去的时间顺序遍历柱线数据。num列中的值几乎均为 60,因为大多数 H1 柱线都是由 60 个 M1 柱线构成。但实际情况并非总是如此。例如,以下是不完整的小时柱线,由不足一小时的数据构成:这可能是由于节假日导致市场提前休市,或是交易活动出现缺口(流动性不足)所致。

...
[38] 2021.10.11 01:00       20  2346    60  2287
[39] 2021.10.11 00:00       85  2404    58  2347
[40] 2021.10.08 23:00       15  2406    55  2405
[41] 2021.10.08 22:00        2  2463    60  2460
...