diff --git a/.refactoring/test.dib b/.refactoring/test.dib index a55e8863..d2277e66 100644 --- a/.refactoring/test.dib +++ b/.refactoring/test.dib @@ -4,7 +4,7 @@ #!csharp -#r "..\v2\bin\Debug\calculations.dll" +#r "\bin\Debug\calculations.dll" using QuanTAlib; #!csharp diff --git a/v2/GBM_Feed.cs b/v2/GBM_Feed.cs index 6a203c1f..0b643838 100644 --- a/v2/GBM_Feed.cs +++ b/v2/GBM_Feed.cs @@ -1,4 +1,4 @@ -namespace QuanTAlib; +//namespace QuanTAlib; public class GBM_Feed { private readonly double _mu; diff --git a/v2/Indicators/EMA.cs b/v2/Indicators/EMA.cs index 3448461a..536baa35 100644 --- a/v2/Indicators/EMA.cs +++ b/v2/Indicators/EMA.cs @@ -1,42 +1,45 @@ -namespace QuanTAlib; - public class EMA { - private double lastEma, lastEmaCandidate, k; - private int period, i; + private readonly int period; + private int index; public TValue Value { get; private set; } - public bool IsHot { get; private set; } + public bool IsHot => index > period; + public int Period => Math.Min(index, period); + private double k; + private double lastEMA, lastEMAcandidate; - public EMA(int period) - { - Init(period); + public EMA(int Period) { + this.period = Period; + Init(); } - public void Init(int period) - { - this.period = period; + public void Init() { + this.Value = default; + this.index = 0; this.k = 2.0 / (period + 1); - this.lastEma = this.lastEmaCandidate = double.NaN; - this.i = 0; + this.lastEMA = 0; + this.lastEMAcandidate = 0; } - public TValue Update(TValue input, bool IsNew = true) - { - double ema; - if (double.IsNaN(lastEma)) { lastEma = input.Value; } - - if (IsNew) - { - lastEma = lastEmaCandidate; - i++; + public TValue Update(TValue Input, bool IsNew = true) { + double ma; + if (double.IsNaN(Input.Value) || double.IsInfinity(Input.Value)) { + return new TValue(Input.Time, lastEMA, IsNew, index > period); + } + if (IsNew) { + if (index<1) { lastEMA = Input.Value; } + lastEMAcandidate = lastEMA; + index++; + } else { + if (index<=1) { lastEMAcandidate = Input.Value; } + lastEMA = lastEMAcandidate; } - double kk = (i < period) ? (2.0 / (i + 1)) : k; - ema = lastEma + kk * (input.Value - lastEma); - lastEmaCandidate = ema; + double kk = (index <= period) ? (2.0 / (index+1)) : k; + ma = (Input.Value - lastEMA) * kk + lastEMA; + lastEMA = ma; - IsHot = i >= period; - Value = new TValue(input.Time, ema, IsNew, IsHot); - return Value; + this.Value = new TValue(Input.Time, ma, IsNew, index > period); + return this.Value; } } \ No newline at end of file diff --git a/v2/Indicators/SMA.cs b/v2/Indicators/SMA.cs index 15366e16..490753cb 100644 --- a/v2/Indicators/SMA.cs +++ b/v2/Indicators/SMA.cs @@ -1,41 +1,55 @@ -namespace QuanTAlib; - public class SMA { - private CircularBuffer buffer = null!; - private int period; - private double sum; + public CircularBuffer buffer = null!; + private readonly int period; + public double sum; public TValue Value { get; private set; } public bool IsHot { get; private set; } public SMA(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) + public TValue Update(TValue input, bool isNew = true) { - buffer.Add(input.value, IsNew); - - //calculate rolling sum - - double sma = sum / buffer.Count; - Value = new TValue(input.Time, sma, isNew, IsHot); - return Value; + if (buffer.Count == 0) + { + // If buffer is empty, always add the value regardless of isNew + buffer.Add(input.Value, true); + sum = input.Value; + } + else if (isNew && buffer.Count == buffer.Capacity) + { + // If buffer is full and it's a new value, remove oldest + sum -= buffer[0]; + buffer.Add(input.Value, true); + sum += input.Value; + } + else + { + // If it's not new, or if buffer isn't full yet + if (!isNew) + { + // Remove the last value if we're updating + sum -= buffer[buffer.Count - 1]; + } + buffer.Add(input.Value, isNew); + sum += input.Value; } - double sma = buffer.Count > 0 ? sum / buffer.Count : double.NaN; + double sma = sum / buffer.Count; IsHot = buffer.Count >= period; - Value = new TValue(input.Time, sma, IsNew, IsHot); + Value = new TValue(input.Time, sma, isNew, IsHot); return Value; } -} +} \ No newline at end of file diff --git a/v2/Indicators/Template.cs b/v2/Indicators/Template.cs new file mode 100644 index 00000000..1f070f53 --- /dev/null +++ b/v2/Indicators/Template.cs @@ -0,0 +1,40 @@ +public class Template +{ + private CircularBuffer buffer = null!; + private readonly int period; + private int index; + public TValue Value { get; private set; } + public bool IsHot { get; private set; } + + public Template(int Period) { + this.period = Period; + Init(); + } + + public void Init() { + this.buffer = new CircularBuffer(period); + this.IsHot = false; + this.Value = default; + this.index = 0; + } + + public TValue Update(TValue Input, bool IsNew = true) { + this.buffer.Add(Input,IsNew); + if (this.index == 0) { + if (IsNew) { this.index++; } + this.Value = new TValue(Input.Time, Input.Value, IsNew, true); + return this.Value; + } + + if (IsNew) { + // starting a new bar, fresh calc + index++; + } else { + // updating existing bar, recalc + } + double ma = Input.Value; + + this.Value = new TValue(Input.Time, ma, IsNew, buffer.Count >= period); + return this.Value; + } +} \ No newline at end of file diff --git a/v2/Indicators/WMA.cs b/v2/Indicators/WMA.cs index f938fab9..f7673f7d 100644 --- a/v2/Indicators/WMA.cs +++ b/v2/Indicators/WMA.cs @@ -1,5 +1,3 @@ -namespace QuanTAlib; - public class WMA { private CircularBuffer buffer = null!; diff --git a/v2/basics/CircularBuffer.cs b/v2/basics/CircularBuffer.cs index 485913c1..cc457009 100644 --- a/v2/basics/CircularBuffer.cs +++ b/v2/basics/CircularBuffer.cs @@ -1,4 +1,7 @@ -namespace QuanTAlib; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; public class CircularBuffer: IEnumerable { @@ -30,7 +33,12 @@ public class CircularBuffer: IEnumerable } } else { // If isNew is false, just update the last item - _buffer[(_start + _size - 1) % Capacity] = item; + if (_size > 0) { + _buffer[(_start + _size - 1) % Capacity] = item; + } else { + _buffer[_start] = item; + _size = 1; + } } } diff --git a/v2/basics/TBar.cs b/v2/basics/TBar.cs index bb98d683..d5ea9a81 100644 --- a/v2/basics/TBar.cs +++ b/v2/basics/TBar.cs @@ -1,4 +1,4 @@ -namespace QuanTAlib; +//namespace QuanTAlib; public readonly record struct TBar(DateTime Time, double Open, double High, double Low, double Close, double Volume, bool IsNew = true) { public DateTime Time { get; init; } = Time; diff --git a/v2/basics/TValue.cs b/v2/basics/TValue.cs index 83a8efd7..49c2a6ed 100644 --- a/v2/basics/TValue.cs +++ b/v2/basics/TValue.cs @@ -1,4 +1,4 @@ -namespace QuanTAlib; +//namespace QuanTAlib; public readonly record struct TValue(DateTime Time, double Value, bool IsNew = true, bool IsHot = true) { diff --git a/v2/test.dib b/v2/test.dib index 80b85ff7..bde0d059 100644 --- a/v2/test.dib +++ b/v2/test.dib @@ -4,156 +4,77 @@ #!csharp -#r ".\bin\Debug\calculations.dll" -using QuanTAlib; - -#!csharp - +//#r "./bin/Debug/calculations.dll" +using System; +using System.Collections; +using System.Collections.Generic; using System.Runtime.CompilerServices; -public class CircularBuffer: IEnumerable -{ - private double[] _buffer = null!; - private int _start; - private int _size; +#!csharp - 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) { - - //refine this flow - 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() { } - } -} +#load "./basics/CircularBuffer.cs" +#load "./basics/TValue.cs" +#load "./basics/TBar.cs" +#load "./Indicators/SMA.cs" +#load "./Indicators/EMA.cs" +#load "./GBM_Feed.cs" #!csharp -public class SMA1 +public class Template { - private CircularBuffer buffer; + private CircularBuffer buffer = null!; private readonly int period; - private double sum; + private int index; public TValue Value { get; private set; } public bool IsHot { get; private set; } - public SMA1(int period) - { - this.period = period; + public Template(int Period) { + this.period = Period; Init(); } - public void Init() - { + public void Init() { this.buffer = new CircularBuffer(period); - this.sum = 0; this.IsHot = false; this.Value = default; + this.index = 0; } - public TValue Update(TValue input, bool isNew = true) - { - buffer.Add(input.Value, isNew); - - sum = 0; - for (int i=0; i= period; - Value = new TValue(input.Time, sma, isNew, IsHot); - return Value; + if (IsNew) { + // starting a new bar, fresh calc + index++; + } else { + // updating existing bar, recalc + } + double ma = Input.Value; + + this.Value = new TValue(Input.Time, ma, IsNew, index > period); + return this.Value; } } #!csharp -GBM_Feed feed = new(initialPrice: 100, mu: 0.1, sigma: 0.9); -int i=10; -SMA1 ma = new(i); -Console.WriteLine($"{"Close",10} {"MA(" + i + ")",10}"); -for (int i = 0; i < 20; i++) +EMA ma = new(3); +Console.WriteLine($"{"Close",5} {"MA()",10}"); +for (int i = 1; i < 10; i=i+1) { - //TValue c =(double)feed.Generate().Close; - //ma.Update(10000,false); - // ma.Update(-10000,false); - - //ma.Update(i,false); + //ma.Update(100,true); + ma.Update(10,true); ma.Update(i,false); - ma.Update(i,true); - ma.Update(10000,false); - ma.Update(i+1,false); - Console.WriteLine($"{i+1} {(double)i+1,10:F2} {(double)ma.Value,10:F2}"); + Console.WriteLine($"{i} {(double)ma.Value,10:F2} {ma.Value.IsNew}"); } #!csharp