mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-16 17:48:05 +00:00
EMA completed
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
#!csharp
|
||||
|
||||
#r "..\v2\bin\Debug\calculations.dll"
|
||||
#r "\bin\Debug\calculations.dll"
|
||||
using QuanTAlib;
|
||||
|
||||
#!csharp
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace QuanTAlib;
|
||||
//namespace QuanTAlib;
|
||||
public class GBM_Feed
|
||||
{
|
||||
private readonly double _mu;
|
||||
|
||||
+31
-28
@@ -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;
|
||||
}
|
||||
}
|
||||
+33
-19
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
namespace QuanTAlib;
|
||||
|
||||
public class WMA
|
||||
{
|
||||
private CircularBuffer buffer = null!;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
namespace QuanTAlib;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
public class CircularBuffer: IEnumerable<double>
|
||||
{
|
||||
@@ -30,7 +33,12 @@ public class CircularBuffer: IEnumerable<double>
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
namespace QuanTAlib;
|
||||
//namespace QuanTAlib;
|
||||
|
||||
public readonly record struct TValue(DateTime Time, double Value, bool IsNew = true, bool IsHot = true)
|
||||
{
|
||||
|
||||
+41
-120
@@ -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<double>
|
||||
{
|
||||
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<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() { }
|
||||
}
|
||||
}
|
||||
#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<buffer.Count; i++) {
|
||||
sum+=buffer[i];
|
||||
public TValue Update(TValue Input, bool IsNew = true) {
|
||||
this.buffer.Add(Input,IsNew);
|
||||
//first value
|
||||
if (this.index == 0) {
|
||||
if (IsNew) { this.index++; }
|
||||
this.Value = new TValue(Input.Time, Input.Value, IsNew, true);
|
||||
return this.Value;
|
||||
}
|
||||
|
||||
double sma = sum / buffer.Count;
|
||||
IsHot = buffer.Count >= 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
|
||||
|
||||
Reference in New Issue
Block a user