22

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

  • 周期/时间(毫秒)
  • 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)的十进制数组。

4

16 回答 16

23
    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;
        }
    }
于 2017-06-01T22:55:04.907 回答
16

您的主要问题是每次迭代都丢弃了太多信息。如果你想跑得这么快,你需要保持一个与帧长度相同大小的缓冲区。

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

(不是真正的 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 可能很诱人,而不是保留整个缓冲区并计算每次迭代的值,但这不适用于非常长的数据长度,因为您的累积和会增长得如此之大,以至于添加小的附加值会导致舍入错误。

于 2012-10-14T21:44:00.373 回答
8

这些天来,Math DotNet库有一个名为的类RunningStatistics,可以为您完成这项工作。如果您只想对最后的“X”项执行此操作,请MovingStatistics改用。

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

于 2017-02-02T19:14:18.727 回答
5

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

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];
}
于 2012-10-14T17:37:10.907 回答
2

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

如何有效地计算移动标准差

于 2016-02-19T21:13:48.240 回答
2

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

  // 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();
  }
于 2018-06-16T21:10:47.730 回答
2

我发现提供的答案有点记忆饥饿,而且速度慢,你要求快。添加 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;

}

于 2019-07-31T19:42:26.717 回答
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
于 2013-12-21T09:12:57.763 回答
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;
}

一旦你为整个数据系列计算了它,你就可以立即获取一个特定的值。

于 2014-06-26T21:36:53.897 回答
1

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

List<decimal> MovingAverage(int period, decimal[] Data)
{
     decimal[] interval = new decimal[period];
     List<decimal> MAs = new List<decimal>();

     for (int i=0, i < Data.length, i++)
     {
          interval[i % period] = Data[i];
          if (i > period - 1)
          {
               MAs.Add(interval.Average());
          }
     }
     return MAs;
}

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

于 2016-02-19T21:06:26.293 回答
1
/// <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;
            }
        }
    }
}
于 2017-01-18T14:36:32.210 回答
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 decimal 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));
}
于 2017-12-12T20:31:21.577 回答
0

使用 Dotnet Core 3 和 Linq 测试:

int period = 20;
for(int k=0;data.Count()-period;k++){
   decimal summe = data.Skip(k).Take(period).Sum();
   summe /= (decimal)period;
}

它确实依赖于 Linq 及其内部优化,没有计时。
使用 Skip() 和 Take() 作为移动平均的“rangeBetween”解决方案,然后将总和除以周期数量。
* for 循环设置上限以避免不完整的求和操作。
参考(C# Microsoft):Skip()Take()Sum()

于 2019-11-25T14:43:52.880 回答
0

我的MovingAverage课程实现是:

  • 线程安全
  • 无锁
  • 仅限于windowSize那是二的力量

这是课程:

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);
    }
}
于 2020-12-06T17:42:59.950 回答
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上的 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;

}
于 2021-05-04T17:42:27.597 回答
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 任务

于 2021-06-18T12:20:00.343 回答