在我的交易申请中,我有股票价格的“现场价格”。我需要保持SMA。我们假设我想要20支蜡烛的SMA,每支蜡烛的持续时间为10秒。这意味着
每隔10秒我就有“检查点”,其中:
每个嘀嗒声:
所以在任何时候我需要“重新计算”SMA。在大多数情况下,只更改最后一根蜡烛的价格(因为我们使用最后价格)。每10秒一次,我需要多做一些额外的工作 - 我需要“忘记”过时蜡烛的平均值,并“存储”“刚创建”蜡烛的平均值。
您能否建议如何以最低延迟实现此目标?低延迟是首要要求。
答案 0 :(得分:3)
我不确定这是否是您正在寻找的方法,但这里是非常快的SMA的伪代码。
简单移动平均线:
我假设您的数据以某种流的形式出现并存储在连续的内存位置(至少具有连续可映射的地址)
x[1] to x[2000] contain the first 2000 data points
// they don't have to be a real array, could just be a function which manages
// a 'circular' array and maps the 'locations' to real locations in memory.
// Either case, it's small enough to be fully in the cache hopefully
//
// Subsequent prices will be accessible in 'locations x[2001], etc.
// Here we are looking to calculate the MA over the latest 2000 ticks
MA2000(1,2000) = (x[1] + x[2] + ... + x[2000]) / 2000 // Usual average
// Done only once
MA2000(2,2001) = MA2000(1,2000) * 2000 + x[2001] - x[1]
MA2000(2,2001) /= 2000
通过两次加法和一次乘法(使用1/2000),您可以为新的刻度生成后续移动平均值。
指数移动平均线: 如上所述,这是一个不错的选择:
// For an N-day EMA
Alpha = 2 / (N+1) // one time calculation
EMA(new) = Alpha * NewTickPrice + (1-Alpha) * EMA(old)
这不是N日移动平均线。它只是一个加权移动平均线,最近N天的权重约为87%,因此几乎N天更像是它。
关于编译器优化的注意事项:
请注意,如果可用,启用SSE或AVX选项可以实现这些算法的大量加速,因为可以在单个CPU周期中生成多个计算。
答案 1 :(得分:0)
因此,您需要一个几乎固定大小的队列,您可以在其中有效地添加新项目并删除最旧的项目(将其从运行总计中删除)。为什么不std::queue
?
这可以放在各种容器之上,但是如果你真的只有20个元素,我怀疑vector
会表现得很好。 (删除项目需要将所有其他项目向下移动一个 - 但移动连续的内存块很快。)您可能希望将性能与双端队列或列表进行比较。
(答案可能取决于你为每个人存储的内容"蜡烛" - 只是一个浮点数/双/ int,或者更复杂的结构?)
答案 2 :(得分:0)
我的实施。 .H:
#pragma once
#include <deque>
class MovingAverage
{
public:
MovingAverage(int length);
~MovingAverage(void);
void Add(double val);
void Modify(double value);
double Value;
std::deque<double> _candlesExceptNewest;
private:
MovingAverage(MovingAverage& rhs):
_length(rhs._length)
{
printf("MovingAverage copy-constructor mustn't be executed, exiting.");
exit(0);
}
const int _length;
int addCounter;
static const int RECALCULATE_VALUE_MASK;
double _sumExceptNewest;
double NewestCandleMedian() {
return (_newestCandleMin + _newestCandleMax) / 2;
}
void RecalculateValue();
double _newestCandleMin;
double _newestCandleMax;
};
的.cpp:
#include "MovingAverage.h"
#include "CommonsNative.h"
const int MovingAverage::RECALCULATE_VALUE_MASK = 1024 - 1;
MovingAverage::MovingAverage(int length):
Value(quiet_NaN),
_length(length),
addCounter(0)
{
if (length < 20) {
std::cout << "Warning, MA is very short, less than 20! length = "
<< length << std::endl;
}
}
MovingAverage::~MovingAverage(void)
{
}
void MovingAverage::Add(double val)
{
++addCounter;
if (addCounter & RECALCULATE_VALUE_MASK) {
_sumExceptNewest = 0;
for (double val : _candlesExceptNewest)
{
_sumExceptNewest += val;
}
}
auto curQueueSize = _candlesExceptNewest.size();
if (curQueueSize == 0) {
_newestCandleMax = _newestCandleMin = val;
}
_sumExceptNewest += NewestCandleMedian();
_candlesExceptNewest.push_back(NewestCandleMedian());
if (curQueueSize == _length) {
_sumExceptNewest -= _candlesExceptNewest.front();
_candlesExceptNewest.pop_front();
}
_newestCandleMax = _newestCandleMin = val;
RecalculateValue();
}
void MovingAverage::RecalculateValue()
{
Value = (_sumExceptNewest + NewestCandleMedian())/(_candlesExceptNewest.size() + 1);
}
void MovingAverage::Modify(double val)
{
if (_candlesExceptNewest.size() == 0) {
Add(val);
} else {
if (val > _newestCandleMax) {
_newestCandleMax = val;
}
if (val < _newestCandleMin) {
_newestCandleMin = val;
}
}
}