文章 "MQL5 酷宝典 - 创建的环形缓存用于快速计算滑动窗口中的指标"

 

新文章 MQL5 酷宝典 - 创建的环形缓存用于快速计算滑动窗口中的指标已发布:

在滑动窗口中执行计算时, 环形缓存是排布数据最简单和最有效的方式。本文描述其算法, 并展示它如何简化滑动窗口中的计算, 以令其更有效率。

在计算伊始, 指标只是将新的数值添加到移动平均的环形缓存当中。您不必控制添加值的数量。所有过时元素的计算和清除都会自动进行。如果在更改最后一根柱线的价格时调用此指标, 则最后一个移动平均值应由新数值替换。ChangeValue 方法为此负责。

指标的图形显示与标准版移动平均值等效:

 

图例. 1. 在环形缓存中计算的简单移动平均值

作者:Vasiliy Sokolov

 

关于交易、自动交易系统和测试交易策略的论坛

在 MQL4 中声明数组 - 不能通过传递变量值(甚至常量)来设置元素数。

fxsaber, 2016.09.16 14:32

void OnStart()
{
  double Index = -345.23;
  double Size = -432.98;
  
  double Array[];
    
  // 任何大小(非整数)都可以使用。
  ArrayResize(Array, (int)Size < 0 ? (int)MathAbs(Size) : (int)Size);
  
// 在任何索引(而不是整数)下 ALWAYS(零除外--如果数组大小为零)都将无差错地执行。
// 这样,数组在两个方向上都成为自身的无限复制
  Array[(int)Index < 0 ? ArraySize(Array) + ((int)Index % ArraySize(Array)) : (int)Index % ArraySize(Array)] = 1;
}
对于这样的任务,OOP 被视为有些矫枉过正。
 
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
//---
   bool calc = false;
   for(int i = prev_calculated; i < rates_total; i++)
   {
      Sma.AddValue(price[i]);
      buff[i] = Sma.SMA();
      calc = true;
   }
   if(!calc)
   {
      Sma.ChangeValue(MaPeriod-1, price[rates_total-1]);
      buff[rates_total-1] = Sma.SMA();
   }
   return(rates_total-1);
}
我不明白计算的情况。似乎更简单
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
//--- 
   if (prev_calculated < rates_total)
     for(int i = prev_calculated; i < rates_total; i++)
     {
        Sma.AddValue(price[i]);
        buff[i] = Sma.SMA();
     }
   else
   {
      Sma.ChangeValue(MaPeriod-1, price[rates_total-1]);
      buff[rates_total-1] = Sma.SMA();
   }
   return(rates_total-1);
}


ZY 如果 prev_calculated 已清零,就会出现错误。

 

这篇文章写得很好,涉及一个非常重要的话题。

环形缓冲区 是一个至关重要的机制。

除了这篇文章之外,我还想就如何进一步发展环形缓冲器的使用提出自己的想法:

如果在环形缓冲器中记录某个参数值的同时,为该参数创建第二个环形缓冲器,记录第一个缓冲器中数值变化之间的时间间隔,就可以根据两个环形缓冲器的数据构建出当前时间段的数值变化曲线。此外,在数学运算的帮助下,还可以提取参数值变化的特征,即其当前特征,并不是根据 具体值,而是根据整个当前时间段的参数变化背景进行运算。


附注:如果有人能将此作为一种独立机制来实现,那就太好了。试试看?

 

感谢您的文章!我有几个问题和批评 )


По возможности избегайте запросов по получению данных множества таймфреймов. Вместо этого для расчетов воспользуйтесь одним (наименьшим) таймфреймом. Например, если вам требуется рассчитать два индикатора на M1 и H1, получите данные M1, сконвертируйте их в H1 и затем подайте эти данные для расчета индикатора на H1. Такой подход сложнее, но позволит существенно сэкономит память.


终端生成的 H1 时间序列比 Expert Advisor 内部生成的相同时间序列占用更多内存?


但是,我们仍然需要近 3 GB 的内存。有没有其他方法可以减少这个数字?如果我们优化时间框架的数量,就可以做到。让我们试着稍微修改一下测试代码,只使用一个时间框架,而不是 21 个 - PERIOD_M1。指标数量保持不变,只是其中一些指标会被重复使用:

现在,同样的 504 个指标在内部计算模式下占用了 548 MB 内存。

我完全不理解这一举动。如何将 21 个时间框架的指标计算与 1 个 TF 的计算进行比较?计算结果截然不同,使用多少内存又有什么区别?


很难找到比在交易中更适合环形缓冲区的应用了。更令人惊讶的是,这种数据构建算法直到现在才在 MQL 社区中出现。

Konstantin Gruzdev 早在 2012 年就发布了他的课程和一些示例。搜索一下就能找到


总的来说,这种技术当然不错。缺点是需要重写所有指标。

 
Andrey Khatimlianskii:

感谢您的文章!我有几个问题和苛刻的评论 )

尽可能避免从多个时间段检索数据的查询。相反,使用一个(最小的)时间框架进行计算。例如,如果您需要计算 M1 和 H1 上的两个指标,请获取 M1 数据,将其转换为 H1 数据,然后将此数据用于计算 H1 上的指标。这种方法比较复杂,但可以节省内存。


终端生成的 H1 时间序列比在 Expert Advisor 中生成的相同时间序列占用更多内存?

很遗憾,是的。而且还要多得多。而且,这与您是请求一个条形图还是整个可用历史记录无关。指定时间段内的所有数据都将被复制到内存中。我不知道具体会复制多少,但根据我的记忆测量,几乎所有数据都会被复制。

Andrey Khatimlianskii:


但是,我们仍然需要近 3 GB 的内存。有没有办法减少这个数字?可以,只要优化时间框架的数量。让我们试着稍微修改一下测试代码,只使用一个时间框架,而不是 21 个 - PERIOD_M1。指标数量保持不变,只是其中一些指标会被重复使用:

现在,同样的 504 个指标在内部计算模式下占用了 548 MB 内存。

我完全不理解这一举动。如何将 21 个时间框架的指标计算与 1 个 TF 的计算进行比较?计算结果截然不同,使用多少内存又有什么区别?

如果只用一个最小的时间框架来计算不同时间框架的多个指标,就可以节省内存。假设有两个指标,一个计算 M1 上的数值,另一个计算 H1 上的数值。我们可以加载 M1 和 H1 的报价。我们可以为每个指标加载报价并从中获取数值。但是,仅仅由于要加载 H1,内存使用量就会增加很多。因此,如果我们请求 M1,然后将 M1 转换为 H1,并将此数据输入 H1 上的指标,内存使用量将大大节省。之所以能节省内存,是因为 MetaTrader 内部缓冲区为存储 H1 报价分配的内存远远多于将这些报价存储在智能交易系统中的内存。

还有一个有趣的功能文章中没有提到。策略测试器 的内存分配模式与众不同,而且更经济。当使用多个时间框架时,它分配的内存要少得多,但一切计算正常。

 
Andrey Khatimlianskii:

...


总的来说,撰写这篇文章有三个目标:

  • 为 Expert Advisor 内部的指标计算 创建快速算法(已完成)。
  • 为环形缓冲区中的计算创建一个方便的界面(已完成)。
  • 创建节省内存的计算方法(未实现)。

最后一项失败。节省几十兆内存是有可能的,但节省 10-20 兆内存是毫无意义的,这将在任何报价请求中增加 100-200 兆的实际终端内存消耗。如果以几十个柱状图的深度加载 21 个时间框架的 6 个符号,则需要超过 13 Gbytes 的内部终端内存(!)。
 
Реter Konow:

这篇文章写得很好,涉及一个非常重要的话题。

环形缓冲区是一个至关重要的机制。

除了这篇文章之外,我还想就如何进一步发展环形缓冲区的使用提出自己的想法:

如果在环形缓冲器中记录某个参数值的同时,为该参数创建第二个环形缓冲器,记录第一个缓冲器中数值变化之间的时间间隔,就可以根据两个环形缓冲器的数据构建出当前时间段的数值变化曲线。此外,在数学运算的帮助下,还可以提取参数值变化的特征,即其当前特征,并不是根据 具体值,而是根据整个当前时段的参数变化背景进行运算。


附注:如果有人能将此作为一种独立机制来实现,那就太好了。试试看?


您所描述的只是一个指标。创建一个类,比方说CTradeChange。在其中放置两个同步环形缓冲区:一个存储 N 个最后价格,另一个存储 N 个最后时间值:

CTradeChange change;
...
change.Add(value, TimeCurrent());

接下来,在 Add 方法中,计算当前时间值与上一个时间值之间的差值。

 
Vasiliy Sokolov:


总的来说,撰写这篇文章有三个目标:

  • 为智能交易系统内部的指标计算 创建快速算法(已完成)。
  • 为环形缓冲区内的计算创建一个方便的界面(已完成)。
  • 创建节省内存的计算方法(未实现)。

最后一项失败。节省几十兆内存是有可能的,但节省 10-20 兆内存是毫无意义的,这将在任何报价请求中增加 100-200 兆的实际终端内存消耗。在几十个柱状图的深度上加载 21 个时间框架的 6 个符号,将需要超过 13 Gbytes 的终端内部内存(!)。

感谢您的回答。

在开发文章中提到的面板时,我发现有必要限制图表上显示的条数(5000)。正是因为内存的原因...

在 MT4 中,这要容易得多:首先,每个 TF 都是独立加载的,因此不需要加载 M1;其次,指标缓冲区占用的空间与您填入的空间一样多(如果您不需要更多空间,甚至可以填入 100 条)。

在第 5 版中,由于从 M1 生成了 TF,它变得更加通用和完整,但内存可能比较困难。

 
Vasiliy Sokolov:


您所描述的只是一个指标。创建一个类,比方说 CTradeChange。在其中放置两个同步环形缓冲区:一个存储 N 个最后价格,另一个存储 N 个最后时间值:

然后在 Add 方法中计算当前时间值与前一时间值的差值。

你的回答很奇怪。看来你完全没有理解我的想法。我再引述一遍:

"如果在环形缓冲器 中记录参数值的同时,为该参数创建第二个环形缓冲器,记录第一个缓冲器中数值变化之间的时间间隔,那么就可以根据两个环形缓冲器中的数据 绘制 出当前时段的数值变化曲线此外,通过数学运算,您还可以提取参数值变化的性质,其当前特征,而且不是针对具体值,而是针对整个当前时段的参数变化背景"。


请注意,我并没有问如何建立两个同步环形缓冲器,因为它们的机制非常简单,而是建议尝试在文章所述的基础上进一步发展环形缓冲器的应用范围。

您在文章中提出的应用范围是通过当前周期内的索引获取特定值

我建议的扩展领域是获取 当前周期内数值变化的特征。这可以通过根据两个环形缓冲区的数据绘制曲线(而不是图表)来实现:

1. 当前时段数值的缓冲区。

2. 第一个缓冲区数值之间的时间间隔缓冲区。


通过合并数据,可以用数学方法绘制曲线(不一定在图表上),并在程序中用算法表示和研究。为此,有必要扫描两个缓冲区,并考虑参数变化的特征。

这一解决方案使我们不仅可以对某一时期内的特定参数值进行运算,还可以对整个时期内的参数变化特征进行运算。


例如,可以在程序中指定

if(Характер_изменения_значения_параметра_за_период ==  BIG_WAVE)Лот  +=  10;

常数 BIG_WAVE 是特征值,表示当前周期内数值变化的特征。

例如,我们可以建立 5 种特征模式:

平、上升、大波浪、下降、小波浪。

每个常量都是当前周期内参数变化的某种特征的模式。

我们需要制定特征记录的格式,并创建这些模板。然后,算法将读取当前的签名,并与模板进行比较,找出当前变化的性质与其中一个模板最接近的匹配项(不可能有绝对匹配项)。

与使用特定值的标准方法相比,这种方法的优势显而易见。

使用签名可以根据从数据集中提取的上下文做出决定,而不是试图从单个值中提取上下文。


附注:希望你这次能理解我的意思。

 
Реter Konow:

你的回答很奇怪。你似乎完全没有理解这一点。我再引述一遍:

...

我建议的领域扩展是获取 当前时期内数值变化的特征。这可以通过根据两个环形缓冲器的数据绘制曲线(不是在图表上)来实现:

附注:希望你这次能理解我的意思。

我第一次也完全理解了你的意思。

什么是 "值变化特征"?它是某个值在动态变化。因此,它是一个指标。我们没有必要为此开发一个环形缓冲器,只要在几个环形指标的基础上创建一个算法,计算出您所说的 "变化特征 "就足够了。