183 lines
4.2 KiB
C#
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);
|
|
}
|
|
}
|