Files
Encelado/Encelado/tests/Encelado.Tests/AdvancedIndicatorTests.cs
T

183 lines
4.2 KiB
C#

using Encelado.Core.Indicators;
using Encelado.Core.Market;
namespace Encelado.Tests;
public class EfficiencyRatioTests
{
[Fact]
public void AStraightLineIsPerfectlyEfficient()
{
EfficiencyRatio er = new(10);
for (int i = 1; i <= 20; i++)
{
er.Update(i);
}
Assert.True(er.IsReady);
Assert.Equal(1.0, er.Value, 10);
}
[Fact]
public void PureOscillationIsCompletelyInefficient()
{
EfficiencyRatio er = new(10);
for (int i = 0; i < 30; i++)
{
er.Update(i % 2 == 0 ? 100 : 101);
}
Assert.True(er.IsReady);
Assert.Equal(0.0, er.Value, 10);
}
[Fact]
public void ANoisyTrendSitsBetweenTheExtremes()
{
EfficiencyRatio er = new(10);
double price = 100;
for (int i = 0; i < 40; i++)
{
price += i % 3 == 0 ? -0.5 : 1.0;
er.Update(price);
}
Assert.InRange(er.Value, 0.05, 0.95);
}
[Fact]
public void NeedsAFullWindowBeforeReporting()
{
EfficiencyRatio er = new(5);
for (int i = 0; i < 5; i++)
{
er.Update(100 + i);
Assert.False(er.IsReady);
}
er.Update(105);
Assert.True(er.IsReady);
}
}
public class RealizedVolatilityTests
{
[Fact]
public void AFlatSeriesHasZeroVolatility()
{
RealizedVolatility vol = new(20, 525_600);
for (int i = 0; i < 40; i++)
{
vol.Update(100);
}
Assert.True(vol.IsReady);
Assert.Equal(0, vol.Value, 10);
}
[Fact]
public void AnnualisationScalesByTheSquareRootOfBarsPerYear()
{
RealizedVolatility perBar = new(20, 1);
RealizedVolatility annual = new(20, 4);
double price = 100;
for (int i = 0; i < 40; i++)
{
price *= i % 2 == 0 ? 1.01 : 0.995;
perBar.Update(price);
annual.Update(price);
}
Assert.True(perBar.Value > 0);
Assert.Equal(perBar.Value * 2, annual.Value, 10);
Assert.Equal(perBar.PerBar, annual.PerBar, 12);
}
[Fact]
public void IgnoresNonPositivePrices()
{
RealizedVolatility vol = new(5, 1);
for (int i = 0; i < 10; i++)
{
vol.Update(100);
}
double before = vol.Value;
vol.Update(0);
vol.Update(-5);
Assert.Equal(before, vol.Value, 12);
}
}
public class KeltnerTests
{
[Fact]
public void ChannelSitsAtTheAtrMultipleAroundTheEma()
{
Keltner keltner = new(period: 10, atrMultiplier: 2.0, atrPeriod: 10);
for (int i = 0; i < 40; i++)
{
keltner.Update(new Bar(DateTime.UtcNow, 100, 100.5, 99.5, 100, 1000, 100, 10));
}
Assert.True(keltner.IsReady);
Assert.Equal(100, keltner.Value, 6);
// Constant 1.0 range means ATR = 1, so the channel is +/- 2.
Assert.Equal(102, keltner.Upper, 6);
Assert.Equal(98, keltner.Lower, 6);
Assert.Equal(4, keltner.Width, 6);
}
}
public class RollingZScoreTests
{
[Fact]
public void ScoresTheLatestSampleAgainstItsOwnWindow()
{
RollingZScore z = new(4);
foreach (double v in new double[] { 2, 4, 4, 6 })
{
z.Update(v);
}
// mean 4, sample stddev sqrt(8/3); the last sample sits 2 above the mean.
Assert.True(z.IsReady);
Assert.Equal(2.0 / Math.Sqrt(8.0 / 3.0), z.Value, 10);
Assert.Equal(4, z.Mean, 10);
}
[Fact]
public void AConstantSeriesScoresZeroInsteadOfDividingByZero()
{
RollingZScore z = new(5);
for (int i = 0; i < 10; i++)
{
z.Update(42);
}
Assert.Equal(0, z.Value, 10);
}
}
public class BollingerDispersionTests
{
[Fact]
public void ExposesTheStandardDeviationTheBandsAreBuiltFrom()
{
BollingerBands bands = new(4, 2.0);
foreach (double v in new double[] { 2, 4, 4, 6 })
{
bands.Update(v);
}
double sd = Math.Sqrt(8.0 / 3.0);
Assert.Equal(sd, bands.StandardDeviation, 10);
Assert.Equal(4 + (2 * sd), bands.Upper, 10);
Assert.Equal(4 - (2 * sd), bands.Lower, 10);
}
}