[英]How to calculate simple moving average faster in C#?
计算简单移动平均线的最快库/算法是什么? 我自己写的,但在 330 000 项十进制数据集上花费的时间太长。
这是我的方法的代码:
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) 的十进制数组。
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;
}
}
您的主要问题是每次迭代都丢弃了太多信息。 如果你想跑得这么快,你需要保留一个与帧长度相同大小的缓冲区。
此代码将为您的整个数据集运行移动平均值:
(不是真正的 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 而不是保持整个缓冲区并计算每次迭代的值可能很诱人,但这不适用于很长的数据长度,因为您的累积总和会变得如此之大,以至于添加小的附加值将导致舍入错误。
现在, Math DotNet库有一个名为RunningStatistics
的类,可以为您执行此操作。 如果您只想在最后的“X”项上执行此操作,请改用MovingStatistics
。
两者都将计算运行平均值、方差和标准偏差,仅通过一次运行,无需存储额外的数据副本。
如果数据是静态的,您可以对数组进行预处理以使移动平均查询非常快:
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];
}
当前(已接受)解决方案包含一个内部循环。 删除它也会更有效。 你可以在这里看到这是如何实现的:
你不需要保持一个正在运行的队列。 只需在窗口中选择最新的新条目并放下旧条目即可。 请注意,这仅使用一个循环,除总和外没有额外的存储空间。
// 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();
}
我发现提供的答案有点内存饥饿,而且速度很慢,你要求快。 添加 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;
}
// 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
这是我在我的应用程序中使用的 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;
}
一旦您为整个数据系列计算了它,您就可以立即获取特定值。
这是我尝试过的方法。 但是警告我是一个完全的业余爱好者所以这可能是完全错误的。
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;
}
应返回包含数据移动平均值的小数列表。
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));
}
/// <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;
}
}
}
}
使用 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# 微软): Skip() 、 Take() 、 Sum() ;
我的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);
}
}
在实践中,这就是我发现即使对数百万个样本也有效的方法。 它计算运行移动平均线,并且比我尝试过的任何其他方法都快。
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;
}
由于没有人显示我的方法会建议它。 我认为在大多数情况下,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 任务
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.