-
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.
add failing tests (TDD), incrementor shell (incomplete)
- Loading branch information
1 parent
c676ed9
commit b4d724e
Showing
3 changed files
with
211 additions
and
0 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 |
---|---|---|
@@ -0,0 +1,128 @@ | ||
namespace Skender.Stock.Indicators; | ||
|
||
/// <summary> | ||
/// Interface for Average Directional Index (ADX) calculations. | ||
/// </summary> | ||
public interface IAdx | ||
{ | ||
/// <summary> | ||
/// Gets the number of periods to look back for the calculation. | ||
/// </summary> | ||
int LookbackPeriods { get; } | ||
} | ||
|
||
/// <summary> | ||
/// Average Directional Index (ADX) from incremental reusable values. | ||
/// </summary> | ||
public class AdxList : List<AdxResult>, IAdx, IAddQuote, IAddReusable | ||
{ | ||
private readonly Queue<double> _buffer; | ||
private double _bufferSum; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="AdxList"/> class. | ||
/// </summary> | ||
/// <param name="lookbackPeriods">The number of periods to look back for the calculation.</param> | ||
public AdxList(int lookbackPeriods) | ||
{ | ||
Adx.Validate(lookbackPeriods); | ||
LookbackPeriods = lookbackPeriods; | ||
|
||
_buffer = new(lookbackPeriods); | ||
_bufferSum = 0; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the number of periods to look back for the calculation. | ||
/// </summary> | ||
public int LookbackPeriods { get; init; } | ||
|
||
/// <summary> | ||
/// Adds a new value to the ADX list. | ||
/// </summary> | ||
/// <param name="timestamp">The timestamp of the value.</param> | ||
/// <param name="value">The value to add.</param> | ||
public void Add(DateTime timestamp, double value) | ||
{ | ||
// update buffer | ||
if (_buffer.Count == LookbackPeriods) | ||
{ | ||
_bufferSum -= _buffer.Dequeue(); | ||
} | ||
_buffer.Enqueue(value); | ||
_bufferSum += value; | ||
|
||
// add nulls for incalculable periods | ||
if (Count < LookbackPeriods - 1) | ||
{ | ||
base.Add(new AdxResult(timestamp)); | ||
return; | ||
} | ||
|
||
// re/initialize as SMA | ||
if (this[^1].Adx is null) | ||
{ | ||
base.Add(new AdxResult( | ||
timestamp, | ||
_bufferSum / LookbackPeriods)); | ||
return; | ||
} | ||
|
||
// calculate ADX normally | ||
base.Add(new AdxResult( | ||
timestamp, | ||
Adx.Increment(this[^1].Adx, value))); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a new reusable value to the ADX list. | ||
/// </summary> | ||
/// <param name="value">The reusable value to add.</param> | ||
/// <exception cref="ArgumentNullException">Thrown when the value is null.</exception> | ||
public void Add(IReusable value) | ||
{ | ||
ArgumentNullException.ThrowIfNull(value); | ||
Add(value.Timestamp, value.Value); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a list of reusable values to the ADX list. | ||
/// </summary> | ||
/// <param name="values">The list of reusable values to add.</param> | ||
/// <exception cref="ArgumentNullException">Thrown when the values list is null.</exception> | ||
public void Add(IReadOnlyList<IReusable> values) | ||
{ | ||
ArgumentNullException.ThrowIfNull(values); | ||
|
||
for (int i = 0; i < values.Count; i++) | ||
{ | ||
Add(values[i].Timestamp, values[i].Value); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Adds a new quote to the ADX list. | ||
/// </summary> | ||
/// <param name="quote">The quote to add.</param> | ||
/// <exception cref="ArgumentNullException">Thrown when the quote is null.</exception> | ||
public void Add(IQuote quote) | ||
{ | ||
ArgumentNullException.ThrowIfNull(quote); | ||
Add(quote.Timestamp, quote.Value); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a list of quotes to the ADX list. | ||
/// </summary> | ||
/// <param name="quotes">The list of quotes to add.</param> | ||
/// <exception cref="ArgumentNullException">Thrown when the quotes list is null.</exception> | ||
public void Add(IReadOnlyList<IQuote> quotes) | ||
{ | ||
ArgumentNullException.ThrowIfNull(quotes); | ||
|
||
for (int i = 0; i < quotes.Count; i++) | ||
{ | ||
Add(quotes[i]); | ||
} | ||
} | ||
} |
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,72 @@ | ||
namespace Increments; | ||
|
||
[TestClass] | ||
public class Adx : IncrementsTestBase | ||
{ | ||
private const int lookbackPeriods = 14; | ||
|
||
private static readonly IReadOnlyList<IReusable> reusables | ||
= Quotes | ||
.Cast<IReusable>() | ||
.ToList(); | ||
|
||
private static readonly IReadOnlyList<AdxResult> series | ||
= Quotes.ToAdx(lookbackPeriods); | ||
|
||
[TestMethod] | ||
public void FromReusableSplit() | ||
{ | ||
AdxList sut = new(lookbackPeriods); | ||
|
||
foreach (IReusable item in reusables) | ||
{ | ||
sut.Add(item.Timestamp, item.Value); | ||
} | ||
|
||
sut.Should().HaveCount(Quotes.Count); | ||
sut.Should().BeEquivalentTo(series); | ||
} | ||
|
||
[TestMethod] | ||
public void FromReusableItem() | ||
{ | ||
AdxList sut = new(lookbackPeriods); | ||
|
||
foreach (IReusable item in reusables) { sut.Add(item); } | ||
|
||
sut.Should().HaveCount(Quotes.Count); | ||
sut.Should().BeEquivalentTo(series); | ||
} | ||
|
||
[TestMethod] | ||
public void FromReusableBatch() | ||
{ | ||
AdxList sut = new(lookbackPeriods) { reusables }; | ||
|
||
sut.Should().HaveCount(Quotes.Count); | ||
sut.Should().BeEquivalentTo(series); | ||
} | ||
|
||
[TestMethod] | ||
public override void FromQuote() | ||
{ | ||
AdxList sut = new(lookbackPeriods); | ||
|
||
foreach (Quote q in Quotes) { sut.Add(q); } | ||
|
||
sut.Should().HaveCount(Quotes.Count); | ||
sut.Should().BeEquivalentTo(series); | ||
} | ||
|
||
[TestMethod] | ||
public override void FromQuoteBatch() | ||
{ | ||
AdxList sut = new(lookbackPeriods) { Quotes }; | ||
|
||
IReadOnlyList<AdxResult> series | ||
= Quotes.ToAdx(lookbackPeriods); | ||
|
||
sut.Should().HaveCount(Quotes.Count); | ||
sut.Should().BeEquivalentTo(series); | ||
} | ||
} |