diff --git a/v2/Indicators/SMA.cs b/v2/Indicators/SMA.cs index 2929cdf0..15366e16 100644 --- a/v2/Indicators/SMA.cs +++ b/v2/Indicators/SMA.cs @@ -24,21 +24,9 @@ public class SMA public TValue Update(TValue input, bool IsNew = true) { - if (buffer.Count == 0 || isNew) - { - if (buffer.Count == period) - { - sum -= buffer[0]; - } - buffer.Add(input); - sum += input.Value; - } - else - { - sum -= buffer[buffer.Count - 1]; - sum += input.Value; - buffer[buffer.Count - 1] = input; - } + buffer.Add(input.value, IsNew); + + //calculate rolling sum double sma = sum / buffer.Count; Value = new TValue(input.Time, sma, isNew, IsHot); diff --git a/v2/basics/CircularBuffer.cs b/v2/basics/CircularBuffer.cs index 3e087dd1..485913c1 100644 --- a/v2/basics/CircularBuffer.cs +++ b/v2/basics/CircularBuffer.cs @@ -1,57 +1,82 @@ namespace QuanTAlib; -public class CircularBuffer +public class CircularBuffer: IEnumerable { private double[] _buffer = null!; private int _start; private int _size; + public int Capacity => _buffer.Length; + public int Count => _size; + public CircularBuffer(int capacity) { + _buffer = new double[capacity]; _start = 0; _size = 0; } - public int Capacity => _buffer.Length; - public int Count => _size; - - public void Add(double item, bool isNew) - { - if (_size == 0 || isNew) - { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(double item, bool isNew = true) { + if (_size == 0 || isNew) { // If buffer is empty or isNew is true, add new item - if (_size < Capacity) - { + if (_size < Capacity) { _buffer[(_start + _size) % Capacity] = item; _size++; - } - else - { + } else { _buffer[_start] = item; _start = (_start + 1) % Capacity; } - } - else - { + } else { // If isNew is false, just update the last item _buffer[(_start + _size - 1) % Capacity] = item; } } - public double this[int index] - { - get - { + public double this[int index] { + get { if (index < 0 || index >= _size) throw new IndexOutOfRangeException(); return _buffer[(_start + index) % Capacity]; - } - set - { + } set { if (index < 0 || index >= _size) throw new IndexOutOfRangeException(); _buffer[(_start + index) % Capacity] = value; } } + + public Enumerator GetEnumerator() => new Enumerator(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator { + private readonly CircularBuffer _buffer; + private int _index; + private double _current; + + internal Enumerator(CircularBuffer buffer) { + _buffer = buffer; + _index = -1; + _current = default; + } + + public bool MoveNext() { + if (_index + 1 >= _buffer._size) + return false; + + _index++; + _current = _buffer[_index]; + return true; + } + + public double Current => _current; + object IEnumerator.Current => Current; + + public void Reset() { + _index = -1; + _current = default; + } + + public void Dispose() { } + } } \ No newline at end of file diff --git a/v2/circularbuffer.dib b/v2/circularbuffer.dib new file mode 100644 index 00000000..ac976a0d --- /dev/null +++ b/v2/circularbuffer.dib @@ -0,0 +1,162 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"name":"csharp"}]}} + +#!csharp + +#r ".\bin\Debug\calculations.dll" +using QuanTAlib; + +#!csharp + +using System.Runtime.CompilerServices; + +public class CircularBuffer: IEnumerable +{ + private double[] _buffer = null!; + private int _start; + private int _size; + + public int Capacity => _buffer.Length; + public int Count => _size; + + public CircularBuffer(int capacity) + { + + _buffer = new double[capacity]; + _start = 0; + _size = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(double item, bool isNew = true) { + if (_size == 0 || isNew) { + // If buffer is empty or isNew is true, add new item + if (_size < Capacity) { + _buffer[(_start + _size) % Capacity] = item; + _size++; + } else { + _buffer[_start] = item; + _start = (_start + 1) % Capacity; + } + } else { + // If isNew is false, just update the last item + _buffer[(_start + _size - 1) % Capacity] = item; + } + } + + public double this[int index] { + get { + if (index < 0 || index >= _size) + throw new IndexOutOfRangeException(); + return _buffer[(_start + index) % Capacity]; + } set { + if (index < 0 || index >= _size) + throw new IndexOutOfRangeException(); + _buffer[(_start + index) % Capacity] = value; + } + } + + public Enumerator GetEnumerator() => new Enumerator(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator { + private readonly CircularBuffer _buffer; + private int _index; + private double _current; + + internal Enumerator(CircularBuffer buffer) { + _buffer = buffer; + _index = -1; + _current = default; + } + + public bool MoveNext() { + if (_index + 1 >= _buffer._size) + return false; + + _index++; + _current = _buffer[_index]; + return true; + } + + public double Current => _current; + object IEnumerator.Current => Current; + + public void Reset() { + _index = -1; + _current = default; + } + + public void Dispose() { } + } +} + +#!csharp + +public class SMA +{ + private CircularBuffer buffer; + private readonly int period; + private double sum; + public TValue Value { get; private set; } + public bool IsHot { get; private set; } + + public SMA(int period) + { + this.period = period; + Init(); + } + + public void Init() + { + this.buffer = new CircularBuffer(period); + this.sum = 0; + this.IsHot = false; + this.Value = default; + } + + public TValue Update(TValue input, bool isNew = true) + { + double oldValue = 0; + if (buffer.Count == period && isNew) + { + oldValue = buffer[0]; + } + + buffer.Add(input.Value, isNew); + + // Update sum + if (isNew) + { + if (buffer.Count <= period) + { + sum += input.Value; + } + else + { + sum = sum - oldValue + input.Value; + } + } + else if (buffer.Count > 0) + { + // If not new, update the sum by replacing the last value + sum = sum - buffer[buffer.Count - 1] + input.Value; + } + + double sma = buffer.Count > 0 ? sum / buffer.Count : double.NaN; + IsHot = buffer.Count >= period; + Value = new TValue(input.Time, sma, isNew, IsHot); + return Value; + } +} + +#!csharp + +CircularBuffer buffer = new(3); +buffer.Add(1, false); +buffer.Add(2, false); +buffer.Add(3, true); +for (int i=0; i { - private double[] _buffer; + private double[] _buffer = null!; private int _start; private int _size; + public int Capacity => _buffer.Length; + public int Count => _size; + public CircularBuffer(int capacity) { + _buffer = new double[capacity]; _start = 0; _size = 0; } - public int Capacity => _buffer.Length; - public int Count => _size; - - public void Add(double item, bool isNew) - { - if (!isNew) - { - // Add new item - if (_size < Capacity) - { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Add(double item, bool isNew = true) { + if (_size == 0 || isNew) { + // If buffer is empty or isNew is true, add new item + if (_size < Capacity) { _buffer[(_start + _size) % Capacity] = item; _size++; - } - else - { + } else { + _buffer[_start] = item; _start = (_start + 1) % Capacity; - _buffer[(_start + _size - 1) % Capacity] = item; - } - } - else - { - // Update the last item - if (_size > 0) - { - _buffer[(_start + _size - 1) % Capacity] = item; - } - else - { - // If buffer is empty, add the item even if isNew is true - _buffer[0] = item; - _size = 1; } + } else { + // If isNew is false, just update the last item + _buffer[(_start + _size - 1) % Capacity] = item; } } - public double this[int index] - { - get - { + public double this[int index] { + get { if (index < 0 || index >= _size) throw new IndexOutOfRangeException(); return _buffer[(_start + index) % Capacity]; - } - set - { + } set { if (index < 0 || index >= _size) throw new IndexOutOfRangeException(); _buffer[(_start + index) % Capacity] = value; } } + + public Enumerator GetEnumerator() => new Enumerator(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public struct Enumerator : IEnumerator { + private readonly CircularBuffer _buffer; + private int _index; + private double _current; + + internal Enumerator(CircularBuffer buffer) { + _buffer = buffer; + _index = -1; + _current = default; + } + + public bool MoveNext() { + if (_index + 1 >= _buffer._size) + return false; + + _index++; + _current = _buffer[_index]; + return true; + } + + public double Current => _current; + object IEnumerator.Current => Current; + + public void Reset() { + _index = -1; + _current = default; + } + + public void Dispose() { } + } } #!csharp @@ -79,52 +97,36 @@ public class CircularBuffer public class SMA1 { private CircularBuffer buffer; - private int period; + private readonly int period; private double sum; public TValue Value { get; private set; } - public bool IsHot => buffer.Count >= period; + public bool IsHot { get; private set; } public SMA1(int period) { - Init(period); + this.period = period; + Init(); } - public void Init(int period) + public void Init() { - this.period = period; this.buffer = new CircularBuffer(period); this.sum = 0; + this.IsHot = false; this.Value = default; } public TValue Update(TValue input, bool isNew = true) { - if (!isNew) - { - if (buffer.Count == period) - { - sum -= buffer[0]; - } - sum += input.Value; - buffer.Add(input.Value, isNew); - } - else - { - if (buffer.Count > 0) - { - sum -= buffer[buffer.Count - 1]; - sum += input.Value; - buffer.Add(input.Value, isNew); - } - else - { - // If buffer is empty, add the item even if isNew is true - sum += input.Value; - buffer.Add(input.Value, false); - } + buffer.Add(input.Value, isNew); + + sum = 0; + for (int i=0; i 0 ? sum / buffer.Count : double.NaN; + IsHot = buffer.Count >= period; Value = new TValue(input.Time, sma, isNew, IsHot); return Value; } @@ -139,7 +141,7 @@ Console.WriteLine($"{"Close",10} {"MA(" + i + ")",10}"); for (int i = 0; i < 20; i++) { TValue c =(double)feed.Generate().Close; - ma.Update(1000,false); + //ma.Update(10000,false); ma.Update(-10000,false); ma.Update(c,true);