-
Notifications
You must be signed in to change notification settings - Fork 247
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
44 changed files
with
2,167 additions
and
589 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
trigger: | ||
- main | ||
- v3 | ||
|
||
pr: | ||
- main | ||
- v3 | ||
|
||
pool: | ||
name: Azure Pipelines | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
namespace Skender.Stock.Indicators; | ||
|
||
// TUPLE OBSERVER and TUPLE PROVIDER (CHAIN STREAM) | ||
|
||
public abstract class ChainProvider | ||
: TupleObserver, IObservable<(DateTime Date, double Value)> | ||
{ | ||
// fields | ||
private readonly List<IObserver<(DateTime Date, double Value)>> observers; | ||
|
||
// initialize | ||
protected ChainProvider() | ||
{ | ||
observers = new(); | ||
ProtectedChain = new(); | ||
Warmup = true; | ||
} | ||
|
||
// properties | ||
internal IEnumerable<(DateTime Date, double Value)> Output => ProtectedChain; | ||
|
||
internal List<(DateTime Date, double Value)> ProtectedChain { get; set; } | ||
|
||
private int OverflowCount { get; set; } | ||
|
||
private bool Warmup { get; set; } | ||
|
||
// METHODS | ||
|
||
// subscribe observer | ||
public IDisposable Subscribe(IObserver<(DateTime Date, double Value)> observer) | ||
{ | ||
if (!observers.Contains(observer)) | ||
{ | ||
observers.Add(observer); | ||
} | ||
|
||
return new Unsubscriber(observers, observer); | ||
} | ||
|
||
// close all observations | ||
public void EndTransmission() | ||
{ | ||
foreach (IObserver<(DateTime Date, double Value)> observer in observers.ToArray()) | ||
{ | ||
if (observers.Contains(observer)) | ||
{ | ||
observer.OnCompleted(); | ||
} | ||
} | ||
|
||
observers.Clear(); | ||
} | ||
|
||
// add one | ||
internal void SendToChain<TResult>(TResult result) | ||
where TResult : IReusableResult | ||
{ | ||
// candidate result | ||
(DateTime Date, double Value) r = new(result.Date, result.Value.Null2NaN()); | ||
|
||
int length = ProtectedChain.Count; | ||
|
||
// initialize | ||
if (length == 0 && result.Value != null) | ||
{ | ||
// add new tuple | ||
ProtectedChain.Add(r); | ||
Warmup = false; | ||
|
||
// notify observers | ||
NotifyObservers(r); | ||
return; | ||
} | ||
|
||
// do not proceed until first non-null Value recieved | ||
if (Warmup && result.Value == null) | ||
{ | ||
return; | ||
} | ||
else | ||
{ | ||
Warmup = false; | ||
} | ||
|
||
(DateTime lastDate, _) = ProtectedChain[length - 1]; | ||
|
||
// add tuple | ||
if (r.Date > lastDate) | ||
{ | ||
// add new tuple | ||
ProtectedChain.Add(r); | ||
|
||
// notify observers | ||
NotifyObservers(r); | ||
} | ||
|
||
// same date or tuple recieved | ||
else if (r.Date <= lastDate) | ||
{ | ||
// check for overflow condition | ||
// where same tuple continues (possible circular condition) | ||
if (r.Date == lastDate) | ||
{ | ||
OverflowCount++; | ||
|
||
if (OverflowCount > 100) | ||
{ | ||
string msg = "A repeated Chain update exceeded the 100 attempt threshold. " | ||
+ "Check and remove circular chains or check your Chain provider."; | ||
|
||
EndTransmission(); | ||
|
||
throw new OverflowException(msg); | ||
} | ||
} | ||
else | ||
{ | ||
OverflowCount = 0; | ||
} | ||
|
||
// seek old tuple | ||
int foundIndex = ProtectedChain | ||
.FindIndex(x => x.Date == r.Date); | ||
|
||
// found | ||
if (foundIndex >= 0) | ||
{ | ||
ProtectedChain[foundIndex] = r; | ||
} | ||
|
||
// add missing tuple | ||
else | ||
{ | ||
ProtectedChain.Add(r); | ||
|
||
// re-sort cache | ||
ProtectedChain = ProtectedChain | ||
.ToSortedList(); | ||
} | ||
|
||
// let observer handle old + duplicates | ||
NotifyObservers(r); | ||
} | ||
} | ||
|
||
// add many | ||
internal void SendToChain<TResult>(IEnumerable<TResult> results) | ||
where TResult : IReusableResult | ||
{ | ||
List<TResult> added = results | ||
.ToSortedList(); | ||
|
||
for (int i = 0; i < added.Count; i++) | ||
{ | ||
SendToChain(added[i]); | ||
} | ||
} | ||
|
||
// notify observers | ||
private void NotifyObservers((DateTime Date, double Value) tuple) | ||
{ | ||
List<IObserver<(DateTime Date, double Value)>> obsList = observers.ToList(); | ||
|
||
for (int i = 0; i < obsList.Count; i++) | ||
{ | ||
IObserver<(DateTime Date, double Value)> obs = obsList[i]; | ||
obs.OnNext(tuple); | ||
} | ||
} | ||
|
||
// unsubscriber | ||
private class Unsubscriber : IDisposable | ||
{ | ||
private readonly List<IObserver<(DateTime Date, double Value)>> observers; | ||
private readonly IObserver<(DateTime Date, double Value)> observer; | ||
|
||
// identify and save observer | ||
public Unsubscriber(List<IObserver<(DateTime Date, double Value)>> observers, IObserver<(DateTime Date, double Value)> observer) | ||
{ | ||
this.observers = observers; | ||
this.observer = observer; | ||
} | ||
|
||
// remove single observer | ||
public void Dispose() | ||
{ | ||
if (observer != null && observers.Contains(observer)) | ||
{ | ||
observers.Remove(observer); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
namespace Skender.Stock.Indicators; | ||
|
||
// OBSERVER of QUOTES (BOILERPLATE) | ||
|
||
public abstract class QuoteObserver : IObserver<Quote> | ||
{ | ||
// fields | ||
private IDisposable? unsubscriber; | ||
|
||
// properites | ||
internal QuoteProvider? Supplier { get; set; } | ||
|
||
// methods | ||
public virtual void Subscribe() | ||
=> unsubscriber = Supplier != null | ||
? Supplier.Subscribe(this) | ||
: throw new ArgumentNullException(nameof(Supplier)); | ||
|
||
public virtual void OnCompleted() => Unsubscribe(); | ||
|
||
public virtual void OnError(Exception error) => throw error; | ||
|
||
public virtual void OnNext(Quote value) | ||
{ | ||
// » handle new quote with override in observer | ||
} | ||
|
||
public virtual void Unsubscribe() => unsubscriber?.Dispose(); | ||
} |
Oops, something went wrong.