refinement

This commit is contained in:
Miha
2024-07-31 17:57:52 -07:00
parent 60c2331cc0
commit f1378e82ba
6 changed files with 382 additions and 179 deletions
+58 -32
View File
@@ -1,45 +1,71 @@
public class EMA
{
private readonly int period;
private int index;
private readonly int _period;
private int _index, _hotIndex;
public TValue Value { get; private set; }
public bool IsHot => index > period;
public int Period => Math.Min(index, period);
private double k;
private double lastEMA, lastEMAcandidate;
public bool IsHot => _index > _period;
public int Period => Math.Min(_index, _period);
private double _k;
private double _lastEMA, _lastEMACandidate;
public EMA(int Period) {
this.period = Period;
public EMA(int period) {
_period = period;
Init();
}
public void Init() {
this.Value = default;
this.index = 0;
this.k = 2.0 / (period + 1);
this.lastEMA = 0;
this.lastEMAcandidate = 0;
}
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++;
public EMA(object source, int period) : this(period) {
var sourceType = source.GetType();
var updateMethod = sourceType.GetMethod("Update", new[] { typeof(TValue) });
if (updateMethod != null) {
var pubEvent = sourceType.GetEvent("Pub");
if (pubEvent != null && pubEvent.EventHandlerType == typeof(Signal)) {
pubEvent.AddEventHandler(source, new Signal(Sub));
} else {
throw new ArgumentException("Source object must have a Pub event of type NewValue.");
}
} else {
if (index<=1) { lastEMAcandidate = Input.Value; }
lastEMA = lastEMAcandidate;
throw new ArgumentException("Source object must have an Update(TValue) method.");
}
}
public void Init() {
Value = default;
_index = _hotIndex = 0;
_k = 2.0 / (_period + 1);
_lastEMA = 0;
_lastEMACandidate = 0;
}
public TValue Update(TValue input) {
if (!input.IsHot && input.IsNew) { _hotIndex++; }
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) {
Value = new TValue(input.Time, _lastEMA, input.IsNew, _index > _period);
Pub?.Invoke(this, Value);
return Value;
}
if (input.IsNew) {
if (_index < 1) { _lastEMA = input.Value; }
_lastEMACandidate = _lastEMA;
_index++;
} else {
if (_index <= 1) { _lastEMACandidate = input.Value; }
_lastEMA = _lastEMACandidate;
}
double kk = (index <= period) ? (2.0 / (index+1)) : k;
ma = (Input.Value - lastEMA) * kk + lastEMA;
lastEMA = ma;
double kk = (_index <= _period) ? (2.0 / (_index + 1)) : _k;
this.Value = new TValue(Input.Time, ma, IsNew, index > period);
return this.Value;
double ema = (input.Value - _lastEMA) * kk + _lastEMA;
_lastEMA = ema;
Value = new TValue(input.Time, ema, input.IsNew, _index > (_period + _hotIndex));
Pub?.Invoke(this, Value);
return Value;
}
public void Sub(object source, TValue arg) {
Update(arg);
}
public event Signal Pub;
}
+66 -41
View File
@@ -1,55 +1,80 @@
public class SMA
{
public CircularBuffer buffer = null!;
private readonly int period;
public double sum;
private readonly int _period;
private int _index, _hotIndex;
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 _sum;
private double _lastValidSMA;
private CircularBuffer _buffer;
private double _lastAddedValue;
public SMA(int period)
{
this.period = period;
public SMA(int period) {
_period = period;
Init();
}
public void Init()
{
this.buffer = new CircularBuffer(period);
this.sum = 0;
this.IsHot = false;
this.Value = default;
public SMA(object source, int period) : this(period) {
var sourceType = source.GetType();
var updateMethod = sourceType.GetMethod("Update", new[] { typeof(TValue) });
if (updateMethod != null) {
var pubEvent = sourceType.GetEvent("Pub");
if (pubEvent != null && pubEvent.EventHandlerType == typeof(Signal)) {
pubEvent.AddEventHandler(source, new Signal(Sub));
} else {
throw new ArgumentException("Source object must have a Pub event of type NewValue.");
}
} else {
throw new ArgumentException("Source object must have an Update(TValue) method.");
}
}
public TValue Update(TValue input, bool isNew = true)
{
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;
public void Init() {
_buffer = new CircularBuffer(_period);
_sum = 0;
_lastValidSMA = 0;
Value = default;
_index = _hotIndex = 0;
_lastAddedValue = 0;
}
public TValue Update(TValue input) {
if (!input.IsHot && input.IsNew) { _hotIndex++; }
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) {
Value = new TValue(input.Time, _lastValidSMA, input.IsNew, _index > _period);
Pub?.Invoke(this, Value);
return Value;
}
double sma = sum / buffer.Count;
IsHot = buffer.Count >= period;
Value = new TValue(input.Time, sma, isNew, IsHot);
if (input.IsNew) {
if (_buffer.Count == _buffer.Capacity) {
_sum -= _buffer[0];
}
_buffer.Add(input.Value, true);
_sum += input.Value;
_lastAddedValue = input.Value;
_index++;
} else {
_sum = _sum - _lastAddedValue + input.Value;
_buffer[_buffer.Count - 1] = input.Value;
_lastAddedValue = input.Value;
}
double sma = _sum / _buffer.Count;
_lastValidSMA = sma;
Value = new TValue(input.Time, sma, input.IsNew, _index > (_period + _hotIndex));
Pub?.Invoke(this, Value);
return Value;
}
public void Sub(object source, TValue arg) {
Update(arg);
}
public event Signal Pub;
}
+1
View File
@@ -18,3 +18,4 @@ public readonly record struct TValue(DateTime Time, double Value, bool IsNew = t
public override string ToString() => $"[{Time:yyyy-MM-dd HH:mm:ss}: {Value:F2}]";
}
public delegate void Signal(object source, TValue args);
+236 -35
View File
@@ -15,66 +15,267 @@ using System.Runtime.CompilerServices;
#load "./basics/CircularBuffer.cs"
#load "./basics/TValue.cs"
#load "./basics/TBar.cs"
#load "./Indicators/SMA.cs"
#load "./Indicators/EMA.cs"
//#load "./Indicators/SMA.cs"
//#load "./Indicators/EMA.cs"
#load "./GBM_Feed.cs"
#!csharp
public delegate void Signal(object source, TValue args);
#!csharp
public class Template
{
private CircularBuffer buffer = null!;
private readonly int period;
private int index;
private CircularBuffer _buffer = null!;
private readonly int _period;
private int _index, _hotIndex;
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 _sum;
private double _lastValidValue;
private double _lastAddedValue;
public Template(int Period) {
this.period = Period;
public Template(int period) {
_period = period;
Init();
}
public void Init() {
this.buffer = new CircularBuffer(period);
this.IsHot = false;
this.Value = default;
this.index = 0;
public Template(object source, int period) : this(period) {
var sourceType = source.GetType();
var updateMethod = sourceType.GetMethod("Update", new[] { typeof(TValue), typeof(bool) });
if (updateMethod != null) {
var pubEvent = sourceType.GetEvent("Pub");
if (pubEvent != null && pubEvent.EventHandlerType == typeof(Signal)) {
pubEvent.AddEventHandler(source, new Signal(Sub));
} else {
throw new ArgumentException("Source object must have a Pub event of type NewValue.");
}
} else {
throw new ArgumentException("Source object must have an Update(TValue, bool) method.");
}
}
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;
public void Init() {
_buffer = new CircularBuffer(_period);
_sum = 0;
_lastValidValue = 0;
Value = default;
_index = _hotIndex = 0;
_lastAddedValue = 0;
}
public TValue Update(TValue input, bool isNew = true) {
if (!input.IsHot && isNew) { _hotIndex++; }
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) {
Value = new TValue(input.Time, _lastValidValue, isNew, _index > _period);
Pub?.Invoke(this, Value);
return Value;
}
if (IsNew) {
// starting a new bar, fresh calc
index++;
} else {
// updating existing bar, recalc
}
double ma = Input.Value;
if (isNew) {
if (_buffer.Count == _buffer.Capacity) {
_sum -= _buffer[0];
}
_buffer.Add(input.Value, true);
_sum += input.Value;
_lastAddedValue = input.Value;
_index++;
} else {
_sum = _sum - _lastAddedValue + input.Value;
_buffer[_buffer.Count - 1] = input.Value;
_lastAddedValue = input.Value;
}
this.Value = new TValue(Input.Time, ma, IsNew, index > period);
return this.Value;
double result = _sum / _buffer.Count;
_lastValidValue = result;
Value = new TValue(input.Time, result, isNew, _index > (_period + _hotIndex));
Pub?.Invoke(this, Value);
return Value;
}
public void Sub(object source, TValue arg) {
Update(arg);
}
public event Signal Pub;
}
#!csharp
//public delegate void Signal(object source, TValue args);
public class SMA
{
private readonly int _period;
private int _index, _hotIndex;
public TValue Value { get; private set; }
public bool IsHot => _index >= _period;
public int Period => Math.Min(_index, _period);
private double _sum;
private double _lastValidSMA;
private CircularBuffer _buffer;
private double _lastAddedValue;
public SMA(int period) {
_period = period;
Init();
}
public SMA(object source, int period) : this(period) {
var sourceType = source.GetType();
var updateMethod = sourceType.GetMethod("Update", new[] { typeof(TValue) });
if (updateMethod != null) {
var pubEvent = sourceType.GetEvent("Pub");
if (pubEvent != null && pubEvent.EventHandlerType == typeof(Signal)) {
pubEvent.AddEventHandler(source, new Signal(Sub));
} else {
throw new ArgumentException("Source object must have a Pub event of type NewValue.");
}
} else {
throw new ArgumentException("Source object must have an Update(TValue) method.");
}
}
public void Init() {
_buffer = new CircularBuffer(_period);
_sum = 0;
_lastValidSMA = 0;
Value = default;
_index = _hotIndex = 0;
_lastAddedValue = 0;
}
public TValue Update(TValue input) {
if (!input.IsHot && input.IsNew) { _hotIndex++; }
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) {
Value = new TValue(input.Time, _lastValidSMA, input.IsNew, _index > _period);
Pub?.Invoke(this, Value);
return Value;
}
if (input.IsNew) {
if (_buffer.Count == _buffer.Capacity) {
_sum -= _buffer[0];
}
_buffer.Add(input.Value, true);
_sum += input.Value;
_lastAddedValue = input.Value;
_index++;
} else {
_sum = _sum - _lastAddedValue + input.Value;
_buffer[_buffer.Count - 1] = input.Value;
_lastAddedValue = input.Value;
}
double sma = _sum / _buffer.Count;
_lastValidSMA = sma;
Value = new TValue(input.Time, sma, input.IsNew, _index > (_period + _hotIndex));
Pub?.Invoke(this, Value);
return Value;
}
public void Sub(object source, TValue arg) {
Update(arg);
}
public event Signal Pub;
}
#!csharp
public class EMA
{
private readonly int _period;
private int _index, _hotIndex;
public TValue Value { 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) {
_period = period;
Init();
}
public EMA(object source, int period) : this(period) {
var sourceType = source.GetType();
var updateMethod = sourceType.GetMethod("Update", new[] { typeof(TValue) });
if (updateMethod != null) {
var pubEvent = sourceType.GetEvent("Pub");
if (pubEvent != null && pubEvent.EventHandlerType == typeof(Signal)) {
pubEvent.AddEventHandler(source, new Signal(Sub));
} else {
throw new ArgumentException("Source object must have a Pub event of type NewValue.");
}
} else {
throw new ArgumentException("Source object must have an Update(TValue) method.");
}
}
public void Init() {
Value = default;
_index = _hotIndex = 0;
_k = 2.0 / (_period + 1);
_lastEMA = 0;
_lastEMACandidate = 0;
}
public TValue Update(TValue input) {
if (!input.IsHot && input.IsNew) { _hotIndex++; }
if (double.IsNaN(input.Value) || double.IsInfinity(input.Value)) {
Value = new TValue(input.Time, _lastEMA, input.IsNew, _index > _period);
Pub?.Invoke(this, Value);
return Value;
}
if (input.IsNew) {
if (_index < 1) { _lastEMA = input.Value; }
_lastEMACandidate = _lastEMA;
_index++;
} else {
if (_index <= 1) { _lastEMACandidate = input.Value; }
_lastEMA = _lastEMACandidate;
}
double kk = (_index <= _period) ? (2.0 / (_index + 1)) : _k;
double ema = (input.Value - _lastEMA) * kk + _lastEMA;
_lastEMA = ema;
Value = new TValue(input.Time, ema, input.IsNew, _index > (_period + _hotIndex));
Pub?.Invoke(this, Value);
return Value;
}
public void Sub(object source, TValue arg) {
Update(arg);
}
public event Signal Pub;
}
#!csharp
EMA ma = new(3);
SMA ma1 = new(ma, 3);
Console.WriteLine($"{"Close",5} {"MA()",10}");
for (int i = 1; i < 10; i=i+1)
{
//ma.Update(100,true);
ma.Update(10,true);
ma.Update(i,false);
Console.WriteLine($"{i} {(double)ma.Value,10:F2} {ma.Value.IsNew}");
TValue tv = new(DateTime.Now, 1000, IsNew: true);
ma.Update(tv);
tv = new(DateTime.Now, i, IsNew: false);
ma.Update(tv);
Console.WriteLine($"{i} {(double)ma.Value,10:F2} {(double)ma1.Value,10:F2} {ma1.Value.IsHot}");
}
#!csharp