#!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