如何在C#中更快地计算出简单的移动平均线?

时间:2012-10-14 17:18:51

标签: c# algorithm financial moving-average

计算简单移动平均线的最快库/算法是什么?我写了自己的,但是在33万项十进制数据集上花了太长时间。

  • 期间/时间(ms)
  • 20/300;
  • 60/1500;
  • 120/3500。

以下是我方法的代码:

public decimal MA_Simple(int period, int ii) {
    if (period != 0 && ii > period) {
        //stp.Start();
        decimal summ = 0;
        for (int i = ii; i > ii - period; i--) {
            summ = summ + Data.Close[i];
        }
        summ = summ / period;
        //stp.Stop();
        //if (ii == 1500) System.Windows.Forms.MessageBox.Show((stp.ElapsedTicks * 1000.0) / Stopwatch.Frequency + " ms");
        return summ;
    } else return -1;
}

Data.Close[]是固定大小(1 000 000)十进制数组。

16 个答案:

答案 0 :(得分:12)

您的主要问题是每次迭代都会丢失太多信息。 如果要快速运行,则需要保留与帧长度相同大小的缓冲区。

此代码将运行整个数据集的移动平均值:

(不是真正的C#,但你应该明白这一点)

decimal buffer[] = new decimal[period];
decimal output[] = new decimal[data.Length];
current_index = 0;
for (int i=0; i<data.Length; i++)
    {
        buffer[current_index] = data[i]/period;
        decimal ma = 0.0;
        for (int j=0;j<period;j++)
            {
                ma += buffer[j];
            }
        output[i] = ma;
        current_index = (current_index + 1) % period;
    }
return output;

请注意,保持正在运行的cumsum可能很有吸引力,而不是保留整个缓冲区并计算每次迭代的值,但这对于很长的数据长度不起作用,因为累积的总和将增长到如此之大以至于增加小其他值将导致舍入错误。

答案 1 :(得分:9)

    public class MovingAverage  
    {
        private Queue<Decimal> samples = new Queue<Decimal>();
        private int windowSize = 16;
        private Decimal sampleAccumulator;
        public Decimal Average { get; private set; }

        /// <summary>
        /// Computes a new windowed average each time a new sample arrives
        /// </summary>
        /// <param name="newSample"></param>
        public void ComputeAverage(Decimal newSample)
        {
            sampleAccumulator += newSample;
            samples.Enqueue(newSample);

            if (samples.Count > windowSize)
            {
                sampleAccumulator -= samples.Dequeue();
            }

            Average = sampleAccumulator / samples.Count;
        }
    }

答案 2 :(得分:4)

如果数据是静态的,您可以预处理数组以非常快速地进行移动平均查询:

decimal[] GetCSum(decimal[] data) {
    decimal csum[] = new decimal[data.Length];
    decimal cursum = 0;
    for(int i=0; i<data.Length; i++) {
        cursum += data[i];
        csum[i] = cursum;
    }
    return csum;
}

现在移动平均值计算简单快捷:

decimal CSumMovingAverage(decimal[] csum, int period, int ii) {
    if(period == 0 || ii <= period)
        return -1;
    return csum[ii] - csum[ii - period];
}

答案 3 :(得分:4)

现在,Math DotNet库有一个名为RunningStatistics的类,它将为您执行此操作。如果您只想对最后一个“X”项目执行此操作,请改用MovingStatistics

两者都将仅使用一次通过即可计算运行平均值,方差和标准偏差,而无需存储额外的数据副本。

答案 4 :(得分:2)

当前(已接受)解决方案包含内部循环。删除它也会更有效。你可以在这里看到这是如何实现的:

How to efficiently calculate a moving Standard Deviation

答案 5 :(得分:1)

// simple moving average
int moving_average(double *values, double *&averages, int size, int periods)
{
    double sum = 0;
    for (int i = 0; i < size; i ++)
        if (i < periods) {
            sum += values[i];
            averages[i] = (i == periods - 1) ? sum / (double)periods : 0;
        } else {
            sum = sum - values[i - periods] + values[i];
            averages[i] = sum / (double)periods;
        }
    return (size - periods + 1 > 0) ? size - periods + 1 : 0;
}

一个C功能,13行代码,简单移动平均。 用法示例:

double *values = new double[10]; // the input
double *averages = new double[10]; // the output
values[0] = 55;
values[1] = 113;
values[2] = 92.6;
...
values[9] = 23;
moving_average(values, averages, 10, 5); // 5-day moving average

答案 6 :(得分:1)

这是我在我的应用程序中使用的MA。

double[] MovingAverage(int period, double[] source)
{
    var ma = new double[source.Length];

    double sum = 0;
    for (int bar = 0; bar < period; bar++)
        sum += source[bar];

    ma[period - 1] = sum/period;

    for (int bar = period; bar < source.Length; bar++)
        ma[bar] = ma[bar - 1] + source[bar]/period
                              - source[bar - period]/period;

    return ma;
}

计算完整个数据系列后,您可以立即获取特定值。

答案 7 :(得分:1)

这是我尝试的方式。但警告我是一个完全的业余爱好者,所以这可能是完全错误的。

GLUT_RGB

应返回包含数据移动平均值的小数列表。

答案 8 :(得分:1)

Queue怎么样?

using System.Collections.Generic;
using System.Linq;

public class MovingAverage
{
    private readonly Queue<decimal> _queue;
    private readonly int _period;

    public MovingAverage(int period)
    {
        _period = period;
        _queue = new Queue<decimal>(period);
    }

    public double Compute(decimal x)
    {
        if (_queue.Count >= _period)
        {
            _queue.Dequeue();
        }

        _queue.Enqueue(x);

        return _queue.Average();
    }
}

用法:

MovingAverage ma = new MovingAverage(3);

foreach(var val in new decimal[1,2,3,4,5,6,7,8,9])
{
   Console.WriteLine(ma.Compute(val));
}

答案 9 :(得分:1)

我发现提供的答案有点耗费内存,而且速度慢,您要求快速。 添加2个字段,一个字段保持运行总计,另一个字段将值更改为 平均值是值列表的总和/计数。我添加了一个Add方法,但是您也可以在方法中使用变量…。

public class Sample
{
    private decimal sum = 0;
    private uint count = 0;

    public void Add(decimal value)
    {
        sum += value;
        count++;
    }

    public decimal AverageMove => count > 0 ? sum / count : 0;
}

使其具有线程安全性:

public class ThreadSafeSample
{
private decimal sum = 0;
private uint count = 0;

private static object locker = new object();
public void Add(decimal value)
{
    lock (locker)
    {
        sum += value;
        count++;
    }
}

public decimal AverageMove => count > 0 ? sum / count : 0;

}

答案 10 :(得分:0)

/// <summary>
/// Fast low CPU usage moving average based on floating point math
/// Note: This algorithm algorithm compensates for floating point error by re-summing the buffer for every 1000 values
/// </summary>
public class FastMovingAverageDouble
{
    /// <summary>
    /// Adjust this as you see fit to suit the scenario
    /// </summary>
    const int MaximumWindowSize = 100;

    /// <summary>
    /// Adjust this as you see fit
    /// </summary>
    const int RecalculateEveryXValues = 1000;

    /// <summary>
    /// Initializes moving average for specified window size
    /// </summary>
    /// <param name="_WindowSize">Size of moving average window between 2 and MaximumWindowSize 
    /// Note: this value should not be too large and also bear in mind the possibility of overflow and floating point error as this class internally keeps a sum of the values within the window</param>
    public FastMovingAverageDouble(int _WindowSize)
    {
        if (_WindowSize < 2)
        {
            _WindowSize = 2;
        }
        else if (_WindowSize > MaximumWindowSize)
        {
            _WindowSize = MaximumWindowSize;
        }
        m_WindowSize = _WindowSize;
    }
    private object SyncRoot = new object();
    private Queue<double> Buffer = new Queue<double>();
    private int m_WindowSize;
    private double m_MovingAverage = 0d;
    private double MovingSum = 0d;
    private bool BufferFull;
    private int Counter = 0;

    /// <summary>
    /// Calculated moving average
    /// </summary>
    public double MovingAverage
    {
        get
        {
            lock (SyncRoot)
            {
                return m_MovingAverage;
            }
        }
    }

    /// <summary>
    /// Size of moving average window set by constructor during intialization
    /// </summary>
    public int WindowSize
    {
        get
        {
            return m_WindowSize;
        }
    }

    /// <summary>
    /// Add new value to sequence and recalculate moving average seee <see cref="MovingAverage"/>
    /// </summary>
    /// <param name="NewValue">New value to be added</param>
    public void AddValue(int NewValue)
    {
        lock (SyncRoot)
        {
            Buffer.Enqueue(NewValue);
            MovingSum += NewValue;
            if (!BufferFull)
            {
                int BufferSize = Buffer.Count;
                BufferFull = BufferSize == WindowSize;
                m_MovingAverage = MovingSum / BufferSize;
            }
            else
            {
                Counter += 1;
                if (Counter > RecalculateEveryXValues)
                {
                    MovingSum = 0;
                    foreach (double BufferValue in Buffer)
                    {
                        MovingSum += BufferValue;
                    }
                    Counter = 0;
                }
                MovingSum -= Buffer.Dequeue();
                m_MovingAverage = MovingSum / WindowSize;
            }
        }
    }
}

答案 11 :(得分:0)

您不需要保持正在运行的队列。只需选择最新的窗口条目并删除旧条目。请注意,这只使用一个循环,除了总和之外没有额外的存储空间。

  // n is the window for your Simple Moving Average
  public List<double> GetMovingAverages(List<Price> prices, int n)
  {
    var movingAverages = new double[prices.Count];
    var runningTotal = 0.0d;       

    for (int i = 0; i < prices.Count; ++i)
    {
      runningTotal += prices[i].Value;
      if( i - n >= 0) {
        var lost = prices[i - n].Value;
        runningTotal -= lost;
        movingAverages[i] = runningTotal / n;
      }
    }
    return movingAverages.ToList();
  }

答案 12 :(得分:0)

经过Dotnet Core 3和Linq的测试:

bar(1)                    // Deduces Foo<int>
bar<const int&>(1)        // Deduces Foo<int>
bar<const int>(1)         // Deduces Foo<int>

bar(Foo<int>{1})           // Deduces Foo<int>
bar(Foo<const int&>{1})    // Deduces Foo<const int&>

它确实依赖Linq及其内部优化,没有计时。
将Skip()和Take()用作moving average的“ rangeBetween”解决方案,然后将总和除以周期数量。
* for循环是上限,以避免总和运算不完整。
参考(C#Microsoft):Skip()Take()Sum()

答案 13 :(得分:0)

我的MovingAverage类实现是:

  • 线程安全
  • 免费锁定
  • 限制为windowSize,即2的幂

这是课程:

using System;
using System.Linq;
using System.Threading;

public class MovingAverage
{
    private readonly int _mask;
    private readonly double?[] _values;
    private int _nextIndex = -1;

    public MovingAverage(int windowSize)
    {
        _mask = windowSize - 1;
        if (windowSize == 0 || (windowSize & _mask) != 0)
        {
            throw new ArgumentException("Must be power of two", nameof(windowSize));
        }
        _values = new double?[windowSize];
    }

    public void Add(double newValue)
    {
        var index = Interlocked.Increment(ref _nextIndex) & _mask;
        _values[index] = newValue;
    }

    public double ComputeAverage()
    {
        return _values.TakeWhile(x => x.HasValue)
            .Select(x => x ?? 0)
            .DefaultIfEmpty(0)
            .Average();
    }
}

这是NUnit测试

using NUnit.Framework;

public class MovingAverageTest
{
    [Test]
    public void Should_compute_average()
    {
        var sut = new MovingAverage(4);

        Assert.That(sut.ComputeAverage(), Is.EqualTo(0));
        sut.Add(2);
        Assert.That(sut.ComputeAverage(), Is.EqualTo(2));
        sut.Add(4);
        Assert.That(sut.ComputeAverage(), Is.EqualTo(3));
        sut.Add(0);
        Assert.That(sut.ComputeAverage(), Is.EqualTo(2));
        sut.Add(6);
        Assert.That(sut.ComputeAverage(), Is.EqualTo(3));
        sut.Add(6);
        Assert.That(sut.ComputeAverage(), Is.EqualTo(4));
        sut.Add(0);
        sut.Add(0);
        sut.Add(0);
        sut.Add(0);
        Assert.That(sut.ComputeAverage(), Is.EqualTo(0));
        sut.Add(10);
        sut.Add(10);
        sut.Add(10);
        sut.Add(10);
        Assert.That(sut.ComputeAverage(), Is.EqualTo(10));
    }

    [Test]
    public void Should_check_windowsize_param()
    {
        Assert.That(() => new MovingAverage(3), Throws.ArgumentException);
    }
}

答案 14 :(得分:0)

在实践中,我发现这甚至适用于数百万个样本。它计算移动平均线,并且比我尝试过的任何其他方法都要快。

public class Sma
  {
    decimal mult = 0;
    private decimal[] samples;
    private readonly int max;

    private decimal average;
    public Sma(int period)
    {
        mult = 1m / period; //cache to avoid expensive division on each step.
        samples = new decimal[period];
        max = period - 1;
    }
    public decimal ComputeAverage(decimal value)
    {
        average -= samples[max];
        var sample = value * mult;
        average += sample;
        Array.Copy(samples, 0, samples, 1, max);
        samples[0] = sample;
        return average = average - samples[0];
    }
}

我发现我经常需要访问历史记录。我通过跟踪平均值来实现这一点:

public class Sma
{
    private readonly int max;
    private decimal[] history;
    public readonly int Period;
    public int Counter = -1;
    public SimpleSma RunningSma { get; }

    public Sma(int period, int maxSamples)
    {
        this.Period = period;
        this.RunningSma = new SimpleSma(period);
        max = maxSamples - 1;
        history = new decimal[maxSamples];
    }


    public decimal ComputeAverage(decimal value)
    {
        Counter++;
        Array.Copy(history, 0, history, 1, max);
        return history[0] = RunningSma.ComputeAverage(value);
    }

    public decimal Average => history[0];
    public decimal this[int index] => history[index];
    public int Length => history.Length;

}

现在在实践中,您的用例听起来像我的需要跟踪多个时间范围的用例:

public class MtfSma // MultiTimeFrame Sma
{
    public Dictionary<int, Sma> Smas { get; private set; }
    public MtfSma(int[] periods, int maxHistorySize = 100)
    {
        Smas = periods.ToDictionary(x=> x, x=> new Sma(x, maxHistorySize));
    }
}

A dictionary is no necessary, but is helpful to map an Sma to its period.

这可以如下使用:

IEnumerable<decimal> dataPoints = new List<Decimal>(); //330 000 data points.
foreach (var dataPoint in dataPoints)
{
    foreach (var kvp in Smas)
    {
        var sma = kvp.Value;
        var period = sma.Period;
        var average = sma.Average; // or sma[0];
        var lastAverage = sma[1];
        Console.WriteLine($"Sma{period} [{sma.Counter}]: Current {average.ToString("n2")}, Previous {lastAverage.ToString("n2")}");
    }
}

另一点是你可以看到这是强类型为十进制,这意味着对其他数据类型的完全重写。

为了处理这个问题,可以将类设为泛型,并使用接口来提供类型转换和所需的算术运算提供程序。

我有我使用的实际代码的完整工作示例,同样适用于数百万个数据点,以及 Github here 上 CrossOver 检测等的实现。与此问答相关的代码:

public interface INumericOperationsProvider<TNumeric>
    where TNumeric : IConvertible
{
    TNumeric Divide(TNumeric dividend, TNumeric divisor);
    TNumeric Multiply(TNumeric multiplicand, TNumeric multiplier);
    TNumeric Add(TNumeric operandA, TNumeric operandB);
    TNumeric Subtract(TNumeric operandA, TNumeric operandB);

    bool IsLessThan(TNumeric operandA, TNumeric operandB);
    bool IsLessThanOrEqual(TNumeric operandA, TNumeric operandB);
    bool IsEqual(TNumeric operandA, TNumeric operandB);
    bool IsGreaterThanOrEqual(TNumeric operandA, TNumeric operandB);
    bool IsGreaterThan(TNumeric operandA, TNumeric operandB);

    TNumeric ToNumeric(sbyte value);
    TNumeric ToNumeric(short value);
    TNumeric ToNumeric(int value);
    TNumeric ToNumeric(long value);
    TNumeric ToNumeric(byte value);
    TNumeric ToNumeric(ushort value);
    TNumeric ToNumeric(uint value);
    TNumeric ToNumeric(ulong value);
    TNumeric ToNumeric(float value);
    TNumeric ToNumeric(double value);
    TNumeric ToNumeric(decimal value);
    TNumeric ToNumeric(IConvertible value);
}



public abstract class OperationsProviderBase<TNumeric>
    : INumericOperationsProvider<TNumeric>
    where TNumeric : IConvertible
{

    private static Type Type = typeof(TNumeric);
    public abstract TNumeric Divide(TNumeric dividend, TNumeric divisor);
    public abstract TNumeric Multiply(TNumeric multiplicand, TNumeric multiplier);
    public abstract TNumeric Add(TNumeric operandA, TNumeric operandB);
    public abstract TNumeric Subtract(TNumeric operandA, TNumeric operandB);



    public TNumeric ToNumeric(sbyte value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(short value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(int value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(long value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(byte value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(ushort value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(uint value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(ulong value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(float value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(double value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(decimal value) => (TNumeric)Convert.ChangeType(value, Type);
    public TNumeric ToNumeric(IConvertible value) => (TNumeric)Convert.ChangeType(value, Type);


    public bool IsLessThan(TNumeric operandA, TNumeric operandB)
        => ((IComparable<TNumeric>)operandA).CompareTo(operandB) < 0;

    public bool IsLessThanOrEqual(TNumeric operandA, TNumeric operandB)
        => ((IComparable<TNumeric>)operandA).CompareTo(operandB) <= 0;

    public bool IsEqual(TNumeric operandA, TNumeric operandB)
        => ((IComparable<TNumeric>)operandA).CompareTo(operandB) == 0;

    public bool IsGreaterThanOrEqual(TNumeric operandA, TNumeric operandB)
        => ((IComparable<TNumeric>)operandA).CompareTo(operandB) >= 0;

    public bool IsGreaterThan(TNumeric operandA, TNumeric operandB)
        => ((IComparable<TNumeric>)operandA).CompareTo(operandB) > 0;
}

public class OperationsProviderFactory
{
    public static OperationsProviderBase<TNumeric> GetProvider<TNumeric>()
        where TNumeric : IConvertible
    {
        var name = typeof(TNumeric).Name;
        switch (name)
        {
            case nameof(Decimal):
                return new DecimalOperationsProvider() as OperationsProviderBase<TNumeric>;
            case nameof(Single):
                return new FloatOperationsProvider() as OperationsProviderBase<TNumeric>;
            case nameof(Double):
                return new DoubleOperationsProvider() as OperationsProviderBase<TNumeric>;
            default:
                throw new NotImplementedException();
        }
    }
}
public class DecimalOperationsProvider : OperationsProviderBase<decimal>
{
    public override decimal Add(decimal a, decimal b)
        => a + b;

    public override decimal Divide(decimal dividend, decimal divisor)
        => dividend / divisor;


    public override decimal Multiply(decimal multiplicand, decimal multiplier)
        => multiplicand * multiplier;

    public override decimal Subtract(decimal a, decimal b)
       => a - b;
}

public class FloatOperationsProvider : OperationsProviderBase<float>
{
    public override float Add(float a, float b)
        => a + b;

    public override float Divide(float dividend, float divisor)
        => dividend / divisor;


    public override float Multiply(float multiplicand, float multiplier)
        => multiplicand * multiplier;

    public override float Subtract(float a, float b)
       => a - b;
}

public class DoubleOperationsProvider : OperationsProviderBase<double>
{
    public override double Add(double a, double b)
        => a + b;

    public override double Divide(double dividend, double divisor)
        => dividend / divisor;


    public override double Multiply(double multiplicand, double multiplier)
        => multiplicand * multiplier;

    public override double Subtract(double a, double b)
       => a - b;
}

public interface ISma<TNumeric>
{
    int Count { get; }
    void AddSample(TNumeric sample);
    void AddSample(IConvertible sample);
    TNumeric Average { get; }
    TNumeric[] History { get; }
}

public class SmaBase<T> : ISma<T>
    where T : IConvertible
{
    public int Count { get; private set; }
    private int maxLen;
    public T[] History { get; private set; }
    public T Average { get; private set; } = default(T);
    public INumericOperationsProvider<T> OperationsProvider { get; private set; }
    public T SampleRatio { get; private set; }
    public SmaBase(int count, INumericOperationsProvider<T> operationsProvider = null)
    {
        if (operationsProvider == null)
            operationsProvider = OperationsProviderFactory.GetProvider<T>();
        this.Count = count;
        this.maxLen = Count - 1;
        History = new T[count];
        this.OperationsProvider = operationsProvider;
        SampleRatio = OperationsProvider.Divide(OperationsProvider.ToNumeric(1), OperationsProvider.ToNumeric(count));
    }

    public void AddSample(T sample)
    {
        T sampleValue = OperationsProvider.Multiply(SampleRatio, sample);

        if (maxLen==0)
        {
            History[0] = sample;
            Average = sample;
        }
        else
        {
            var remValue = OperationsProvider.Multiply(SampleRatio, History[0]);
            Average = OperationsProvider.Subtract(Average, remValue);
            Average = OperationsProvider.Add(Average, sampleValue);
            Array.Copy(History, 1, History, 0, Count - 1);
            History[maxLen]= sample;
        }
    }


    public void AddSample(IConvertible sample)
        => AddSample(OperationsProvider.ToNumeric(sample));

}
public class SmaOfDecimal : SmaBase<decimal>
{

    public SmaOfDecimal(int count) : base(count)
    {

    }
}

public class MultiTimeFrameSma<TNumeric>
    where TNumeric : IConvertible
{
    public Dictionary<int, SmaBase<TNumeric>> SimpleMovingAverages;
    public Dictionary<int, int> SimpleMovingAverageIndexes;
    public int[] SimpleMovingAverageKeys;
    private List<Action<TNumeric>> SampleActions;
    public TNumeric[] Averages;
    public int TotalSamples = 0;
    public TNumeric LastSample;

    public TNumeric[] History { get; private set; }
    public int MaxSampleLength { get; private set; }
    private int maxLen;
    public MultiTimeFrameSma(int maximumMovingAverage) : this(Enumerable.Range(1, maximumMovingAverage))
    {

    }

    public MultiTimeFrameSma(IEnumerable<int> movingAverageSizes)
    {
        SimpleMovingAverages = new Dictionary<int, SmaBase<TNumeric>>();
        SimpleMovingAverageIndexes = new Dictionary<int, int>();
        SimpleMovingAverageKeys = movingAverageSizes.ToArray();

        MaxSampleLength = SimpleMovingAverageKeys.Max(x => x);
        maxLen = MaxSampleLength - 1;
        History = new TNumeric[MaxSampleLength];//new List<TNumeric>();
        this.SampleActions = new List<Action<TNumeric>>();
        var averages = new List<TNumeric>();
        int i = 0;
        foreach (var smaSize in movingAverageSizes.OrderBy(x => x))
        {
            var sma = new SmaBase<TNumeric>(smaSize);
            SampleActions.Add((x) => { sma.AddSample(x); Averages[SimpleMovingAverageIndexes[sma.Count]] = sma.Average; });
            SimpleMovingAverages.Add(smaSize, sma);
            SimpleMovingAverageIndexes.Add(smaSize, i++);
            averages.Add(sma.Average);
        }
        this.Averages = averages.ToArray();
    }
    public void AddSample(TNumeric value)
    {
        if (maxLen > 0)
        {
            Array.Copy(History, 1, History, 0, maxLen);
            History[maxLen] = value;
        }
        else
        {
            History[0] = value;
        }
        LastSample = value;
        SampleActions.ForEach(action => action(value));
        TotalSamples++;
    }

}

public class MultiTimeFrameCrossOver<TNumeric>
    where TNumeric : IConvertible
{
    public MultiTimeFrameSma<TNumeric> SimpleMovingAverages { get; }
    public TNumeric[] History => SimpleMovingAverages.History;
    public TNumeric[] Averages => SimpleMovingAverages.Averages;
    public int TotalSamples => SimpleMovingAverages.TotalSamples;
    public TNumeric LastSample => SimpleMovingAverages.LastSample;
    private bool[][] matrix;
    public MultiTimeFrameCrossOver(MultiTimeFrameSma<TNumeric> simpleMovingAverages)
    {
        this.SimpleMovingAverages = simpleMovingAverages;
        int length = this.SimpleMovingAverages.Averages.Length;
        this.matrix = SimpleMovingAverages.Averages.Select(avg => SimpleMovingAverages.Averages.Select(x => true).ToArray()).ToArray();

    }
    public void AddSample(TNumeric value)
    {
        SimpleMovingAverages.AddSample(value);
        int max = SimpleMovingAverages.Averages.Length;

        for (var maIndex = 0; maIndex < max; maIndex++)
        {
            IComparable<TNumeric> ma = (IComparable<TNumeric>)SimpleMovingAverages.Averages[maIndex];
            var row = matrix[maIndex];
            for (var otherIndex = 0; otherIndex < max; otherIndex++)
            {
                row[otherIndex] = ma.CompareTo(SimpleMovingAverages.Averages[otherIndex]) >= 0;
            }
        }
    }

    public bool[][] GetMatrix() => matrix;

}

答案 15 :(得分:0)

因为没有人显示我的方法会建议它。 我认为在大多数情况下,Linq 的执行速度足够快,而无需创建缓冲区或代码复杂性。考虑到金融 _originalDataserie OHLC 开盘高低收盘我想要 sma 收盘是 Ilist<double>

  double[] smaSerie = new double[_originalDataSeries.Count];
      for (int i = 0; i < _originalDataSeries.Count;i++)
            {
                double sma = double.NaN;
                int period = 50;
              //  var rangeOfinterest = _originalDataSeries.CloseValues.AsParallel().Skip(i - period).Take(period).ToList();
                var rangeOfinterest = _originalDataSeries.CloseValues.Skip(i - period).Take(period).ToList();
                if (rangeOfinterest.Any())
                {
                    sma = rangeOfinterest.Average();                   
                }              
                 smaSerie[i] = sma;

            }

Sma 计算出 720 点:00:00:00.0075765

我不知道评论中的并行版本是否表现更好,因为它需要将平均值实现为并行并用于 _originalSerie 并处理空范围,但如果您有百万点来显示一个镜头,它可以通过这种方式改进。但是在这种情况下,我会进行 GPU 计算,因为 sma 有资格执行此 gpu 任务