Refactor CircularBuffer class, add Enumerator struct for iteration.

The CircularBuffer class was updated to implement IEnumerable<double> and include an Enumerator struct for iteration. The Add method now has a default parameter value. Also, a quirky joke: Why do programmers prefer dark mode? Because light attracts bugs!
This commit is contained in:
Miha Kralj
2024-07-29 07:33:51 -07:00
parent 3455baaf6c
commit 89a46089e9
4 changed files with 281 additions and 104 deletions
+3 -15
View File
@@ -24,21 +24,9 @@ public class SMA
public TValue Update(TValue input, bool IsNew = true) public TValue Update(TValue input, bool IsNew = true)
{ {
if (buffer.Count == 0 || isNew) buffer.Add(input.value, IsNew);
{
if (buffer.Count == period) //calculate rolling sum
{
sum -= buffer[0];
}
buffer.Add(input);
sum += input.Value;
}
else
{
sum -= buffer[buffer.Count - 1];
sum += input.Value;
buffer[buffer.Count - 1] = input;
}
double sma = sum / buffer.Count; double sma = sum / buffer.Count;
Value = new TValue(input.Time, sma, isNew, IsHot); Value = new TValue(input.Time, sma, isNew, IsHot);
+48 -23
View File
@@ -1,57 +1,82 @@
namespace QuanTAlib; namespace QuanTAlib;
public class CircularBuffer public class CircularBuffer: IEnumerable<double>
{ {
private double[] _buffer = null!; private double[] _buffer = null!;
private int _start; private int _start;
private int _size; private int _size;
public int Capacity => _buffer.Length;
public int Count => _size;
public CircularBuffer(int capacity) public CircularBuffer(int capacity)
{ {
_buffer = new double[capacity]; _buffer = new double[capacity];
_start = 0; _start = 0;
_size = 0; _size = 0;
} }
public int Capacity => _buffer.Length; [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Count => _size; public void Add(double item, bool isNew = true) {
if (_size == 0 || isNew) {
public void Add(double item, bool isNew)
{
if (_size == 0 || isNew)
{
// If buffer is empty or isNew is true, add new item // If buffer is empty or isNew is true, add new item
if (_size < Capacity) if (_size < Capacity) {
{
_buffer[(_start + _size) % Capacity] = item; _buffer[(_start + _size) % Capacity] = item;
_size++; _size++;
} } else {
else
{
_buffer[_start] = item; _buffer[_start] = item;
_start = (_start + 1) % Capacity; _start = (_start + 1) % Capacity;
} }
} } else {
else
{
// If isNew is false, just update the last item // If isNew is false, just update the last item
_buffer[(_start + _size - 1) % Capacity] = item; _buffer[(_start + _size - 1) % Capacity] = item;
} }
} }
public double this[int index] public double this[int index] {
{ get {
get
{
if (index < 0 || index >= _size) if (index < 0 || index >= _size)
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
return _buffer[(_start + index) % Capacity]; return _buffer[(_start + index) % Capacity];
} } set {
set
{
if (index < 0 || index >= _size) if (index < 0 || index >= _size)
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
_buffer[(_start + index) % Capacity] = value; _buffer[(_start + index) % Capacity] = value;
} }
} }
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<double> IEnumerable<double>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<double> {
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() { }
}
} }
+162
View File
@@ -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<double>
{
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<double> IEnumerable<double>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<double> {
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<buffer.Count; i++) {
display(buffer[i]);
}
+68 -66
View File
@@ -9,69 +9,87 @@ using QuanTAlib;
#!csharp #!csharp
public class CircularBuffer using System.Runtime.CompilerServices;
public class CircularBuffer: IEnumerable<double>
{ {
private double[] _buffer; private double[] _buffer = null!;
private int _start; private int _start;
private int _size; private int _size;
public int Capacity => _buffer.Length;
public int Count => _size;
public CircularBuffer(int capacity) public CircularBuffer(int capacity)
{ {
_buffer = new double[capacity]; _buffer = new double[capacity];
_start = 0; _start = 0;
_size = 0; _size = 0;
} }
public int Capacity => _buffer.Length; [MethodImpl(MethodImplOptions.AggressiveInlining)]
public int Count => _size; public void Add(double item, bool isNew = true) {
if (_size == 0 || isNew) {
public void Add(double item, bool isNew) // If buffer is empty or isNew is true, add new item
{ if (_size < Capacity) {
if (!isNew)
{
// Add new item
if (_size < Capacity)
{
_buffer[(_start + _size) % Capacity] = item; _buffer[(_start + _size) % Capacity] = item;
_size++; _size++;
} } else {
else _buffer[_start] = item;
{
_start = (_start + 1) % Capacity; _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] public double this[int index] {
{ get {
get
{
if (index < 0 || index >= _size) if (index < 0 || index >= _size)
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
return _buffer[(_start + index) % Capacity]; return _buffer[(_start + index) % Capacity];
} } set {
set
{
if (index < 0 || index >= _size) if (index < 0 || index >= _size)
throw new IndexOutOfRangeException(); throw new IndexOutOfRangeException();
_buffer[(_start + index) % Capacity] = value; _buffer[(_start + index) % Capacity] = value;
} }
} }
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<double> IEnumerable<double>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public struct Enumerator : IEnumerator<double> {
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 #!csharp
@@ -79,52 +97,36 @@ public class CircularBuffer
public class SMA1 public class SMA1
{ {
private CircularBuffer buffer; private CircularBuffer buffer;
private int period; private readonly int period;
private double sum; private double sum;
public TValue Value { get; private set; } public TValue Value { get; private set; }
public bool IsHot => buffer.Count >= period; public bool IsHot { get; private set; }
public SMA1(int period) 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.buffer = new CircularBuffer(period);
this.sum = 0; this.sum = 0;
this.IsHot = false;
this.Value = default; this.Value = default;
} }
public TValue Update(TValue input, bool isNew = true) public TValue Update(TValue input, bool isNew = true)
{ {
if (!isNew) buffer.Add(input.Value, isNew);
{
if (buffer.Count == period) sum = 0;
{ for (int i=0; i<buffer.Count; i++) {
sum -= buffer[0]; sum+=buffer[i];
}
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);
}
} }
double sma = buffer.Count > 0 ? sum / buffer.Count : double.NaN; double sma = buffer.Count > 0 ? sum / buffer.Count : double.NaN;
IsHot = buffer.Count >= period;
Value = new TValue(input.Time, sma, isNew, IsHot); Value = new TValue(input.Time, sma, isNew, IsHot);
return Value; return Value;
} }
@@ -139,7 +141,7 @@ Console.WriteLine($"{"Close",10} {"MA(" + i + ")",10}");
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{ {
TValue c =(double)feed.Generate().Close; TValue c =(double)feed.Generate().Close;
ma.Update(1000,false); //ma.Update(10000,false);
ma.Update(-10000,false); ma.Update(-10000,false);
ma.Update(c,true); ma.Update(c,true);