mirror of
https://github.com/lukewilson2002/autotrader.git
synced 2025-06-14 07:53:51 +00:00
293 lines
8.8 KiB
Go
293 lines
8.8 KiB
Go
package autotrader
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestSeries(t *testing.T) {
|
|
series := NewFloatSeries("test", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
|
if series.Len() != 10 {
|
|
t.Fatalf("Expected 10 rows, got %d", series.Len())
|
|
}
|
|
series.Reverse()
|
|
if series.Len() != 10 {
|
|
t.Fatalf("Expected 10 rows, got %d", series.Len())
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
if val := series.Value(i); val != float64(10-i) {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, float64(10-i), val)
|
|
}
|
|
}
|
|
|
|
last5 := series.CopyRange(-5, -1)
|
|
if last5.Len() != 5 {
|
|
t.Fatalf("Expected 5 rows, got %d", last5.Len())
|
|
}
|
|
for i := 0; i < 5; i++ {
|
|
if val := last5.Value(i); val != float64(5-i) {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, float64(5-i), val)
|
|
}
|
|
}
|
|
last5.SetValue(-1, 0.0)
|
|
if series.Value(-1) == 0.0 {
|
|
t.Errorf("Expected data to be copied, not referenced")
|
|
}
|
|
|
|
outOfBounds := series.CopyRange(10, -1)
|
|
if outOfBounds == nil {
|
|
t.Fatal("Expected non-nil series, got nil")
|
|
}
|
|
if outOfBounds.Len() != 0 {
|
|
t.Fatalf("Expected 0 rows, got %d", outOfBounds.Len())
|
|
}
|
|
|
|
valueRange := series.ValueRange(-1, 0) // Out of bounds should result in an empty slice.
|
|
if valueRange == nil || len(valueRange) != 0 {
|
|
t.Fatalf("Expected a slice with 0 items, got %d", len(valueRange))
|
|
}
|
|
valueRange = series.ValueRange(0, 5) // Take the first 5 items.
|
|
if len(valueRange) != 5 {
|
|
t.Fatalf("Expected a slice with 5 items, got %d", len(valueRange))
|
|
}
|
|
for i := 0; i < 5; i++ {
|
|
if val := valueRange[i]; val != float64(10-i) {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, float64(10-i), val)
|
|
}
|
|
}
|
|
|
|
values := series.Values()
|
|
if len(values) != 10 {
|
|
t.Fatalf("Expected a slice with 10 items, got %d", len(values))
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
if val := values[i]; val != float64(10-i) {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, float64(10-i), val)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSeriesFunctional(t *testing.T) {
|
|
series := NewSeries("test", 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0)
|
|
doubled := series.Copy().Map(func(_ int, val any) any {
|
|
return val.(float64) * 2
|
|
})
|
|
if doubled.Len() != 10 {
|
|
t.Fatalf("Expected 10 rows, got %d", doubled.Len())
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
if val := doubled.Float(i); val != float64(i+1)*2 {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, float64(i+1)*2, val)
|
|
}
|
|
}
|
|
series.SetValue(0, 100.0)
|
|
if doubled.Float(0) == 100.0 {
|
|
t.Error("Expected data to be copied, not referenced")
|
|
}
|
|
series.SetValue(0, 1.0) // Reset the value.
|
|
|
|
evens := series.Copy().Filter(func(_ int, val any) bool {
|
|
return EqualApprox(math.Mod(val.(float64), 2), 0)
|
|
})
|
|
if evens.Len() != 5 {
|
|
t.Fatalf("Expected 5 rows, got %d", evens.Len())
|
|
}
|
|
for i := 0; i < 5; i++ {
|
|
if val := evens.Float(i); val != float64(i+1)*2 {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, float64(i+1)*2, val)
|
|
}
|
|
}
|
|
if series.Len() != 10 {
|
|
t.Fatalf("Expected series to still have 10 rows, got %d", series.Len())
|
|
}
|
|
|
|
diffed := series.Copy().Map(func(i int, v any) any {
|
|
if i == 0 {
|
|
return 0.0
|
|
}
|
|
return v.(float64) - series.Float(i-1)
|
|
})
|
|
if diffed.Len() != 10 {
|
|
t.Fatalf("Expected 10 rows, got %d", diffed.Len())
|
|
}
|
|
if diffed.Float(0) != 0.0 {
|
|
t.Errorf("Expected first value to be 0.0, got %v", diffed.Float(0))
|
|
}
|
|
for i := 1; i < 10; i++ {
|
|
if val := diffed.Float(i); val != 1.0 {
|
|
t.Errorf("(%d)\tExpected 1.0, got %v", i, val)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRollingSeries(t *testing.T) {
|
|
// Test rolling average.
|
|
series := NewSeries("test", 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0)
|
|
|
|
sma5Expected := []float64{1, 1.5, 2, 2.5, 3, 4, 5, 6, 7, 8}
|
|
sma5 := series.Copy().Rolling(5).Average() // Take the 5 period moving average and cast it to Series.
|
|
if sma5.Len() != 10 {
|
|
t.Fatalf("Expected 10 rows, got %d", sma5.Len())
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
// Calling Float instead of Value is very important. Value will call the AppliedSeries.Value method
|
|
// while Float calls Series.Float which is what most people will use and is the most likely to be
|
|
// problematic as it is supposed to route through the DataSeries.value method.
|
|
if val := sma5.Float(i); !EqualApprox(val, sma5Expected[i]) {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, sma5Expected[i], val)
|
|
}
|
|
}
|
|
|
|
ema5Expected := []float64{1, 1.3333333333333333, 1.8888888888888888, 2.5925925925925926, 3.3950617283950617, 4.395061728395062, 5.395061728395062, 6.395061728395062, 7.395061728395062, 8.395061728395062}
|
|
ema5 := series.Copy().Rolling(5).EMA() // Take the 5 period exponential moving average.
|
|
if ema5.Len() != 10 {
|
|
t.Fatalf("Expected 10 rows, got %d", ema5.Len())
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
if val := ema5.Float(i); !EqualApprox(val, ema5Expected[i]) {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, ema5Expected[i], val)
|
|
}
|
|
}
|
|
|
|
// Test min and max functions
|
|
minExpected := []float64{1, 1, 1, 1, 1, 2, 3, 4, 5, 6}
|
|
min := series.Copy().Rolling(5).Min()
|
|
if min.Len() != 10 {
|
|
t.Fatalf("Expected 10 rows, got %d", min.Len())
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
if val := min.Float(i); val != minExpected[i] {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, minExpected[i], val)
|
|
}
|
|
}
|
|
|
|
maxExpected := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
|
max := series.Copy().Rolling(5).Max()
|
|
if max.Len() != 10 {
|
|
t.Fatalf("Expected 10 rows, got %d", max.Len())
|
|
}
|
|
for i := 0; i < 10; i++ {
|
|
if val := max.Float(i); val != maxExpected[i] {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, maxExpected[i], val)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestIndexedSeriesInsert(t *testing.T) {
|
|
indexed := NewIndexedSeries("test", map[UnixTime]float64{
|
|
UnixTime(0): 1.0,
|
|
UnixTime(2): 2.0,
|
|
UnixTime(4): 3.0,
|
|
UnixTime(6): 4.0,
|
|
UnixTime(8): 5.0,
|
|
UnixTime(10): 6.0,
|
|
})
|
|
if indexed.Len() != 6 {
|
|
t.Fatalf("Expected 6 rows, got %d", indexed.Len())
|
|
}
|
|
for i := 0; i < 6; i++ {
|
|
if val := indexed.Float(i); val != float64(i+1) {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, float64(i+1), val)
|
|
}
|
|
}
|
|
for i := 0; i < 6; i++ {
|
|
index := UnixTime(i * 2)
|
|
if val := indexed.ValueIndex(index); val != float64(i+1) {
|
|
t.Errorf("(%v)\tExpected %f, got %v", index, float64(i+1), val)
|
|
}
|
|
}
|
|
indexed.Insert(UnixTime(5), -1.0)
|
|
if indexed.Len() != 7 {
|
|
t.Fatalf("Expected 7 rows, got %d", indexed.Len())
|
|
}
|
|
if indexed.ValueIndex(UnixTime(5)) != -1.0 {
|
|
t.Errorf("Expected value at index 5 to be -1.0, got %v", indexed.ValueIndex(UnixTime(5)))
|
|
}
|
|
}
|
|
|
|
func TestIndexedSeries(t *testing.T) {
|
|
intIndexed := NewIndexedSeries("test", map[int]float64{
|
|
0: 1.0,
|
|
2: 2.0,
|
|
4: 3.0,
|
|
6: 4.0,
|
|
8: 5.0,
|
|
10: 6.0,
|
|
})
|
|
|
|
if intIndexed.Len() != 6 {
|
|
t.Fatalf("Expected 6 rows, got %d", intIndexed.Len())
|
|
}
|
|
if intIndexed.ValueIndex(4).(float64) != 3.0 {
|
|
t.Errorf("Expected value at index 4 to be 3.0, got %v", intIndexed.ValueIndex(4))
|
|
}
|
|
|
|
floatIndexed := NewIndexedSeries("test", map[float64]float64{
|
|
0.0: 1.0,
|
|
2.0: 2.0,
|
|
4.0: 3.0,
|
|
})
|
|
if floatIndexed.Len() != 3 {
|
|
t.Fatalf("Expected 3 rows, got %d", floatIndexed.Len())
|
|
}
|
|
if floatIndexed.ValueIndex(4.0).(float64) != 3.0 {
|
|
t.Errorf("Expected value at index 4.0 to be 3.0, got %v", floatIndexed.ValueIndex(4.0))
|
|
}
|
|
|
|
timeIndexed := NewIndexedSeries("test", map[UnixTime]float64{
|
|
UnixTime(time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC).Unix()): 1.0,
|
|
UnixTime(time.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC).Unix()): 2.0,
|
|
UnixTime(time.Date(2018, 1, 3, 0, 0, 0, 0, time.UTC).Unix()): 3.0,
|
|
})
|
|
if timeIndexed.Len() != 3 {
|
|
t.Fatalf("Expected 3 rows, got %d", timeIndexed.Len())
|
|
}
|
|
if index := UnixTime(time.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC).Unix()); timeIndexed.ValueIndex(index).(float64) != 2.0 {
|
|
t.Errorf("Expected value at index 2018-01-02 to be 2.0, got %v", timeIndexed.ValueIndex(index))
|
|
}
|
|
|
|
doubledTimeIndexed := timeIndexed.Copy().Add(timeIndexed)
|
|
if doubledTimeIndexed.Len() != 3 {
|
|
t.Fatalf("Expected 3 rows, got %d", doubledTimeIndexed.Len())
|
|
}
|
|
if index := UnixTime(time.Date(2018, 1, 2, 0, 0, 0, 0, time.UTC).Unix()); doubledTimeIndexed.ValueIndex(index).(float64) != 4.0 {
|
|
t.Errorf("Expected value at index 2018-01-02 to be 4.0, got %v", doubledTimeIndexed.ValueIndex(index))
|
|
}
|
|
|
|
// Test that the Copy function works.
|
|
index := UnixTime(time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC).Unix())
|
|
doubledTimeIndexed.SetValueIndex(index, 100.0)
|
|
if timeIndexed.ValueIndex(index).(float64) != 1.0 {
|
|
t.Errorf("Expected value at index 2018-01-01 to be 1.0, got %v", timeIndexed.ValueIndex(index))
|
|
}
|
|
}
|
|
|
|
func TestIndexedOperations(t *testing.T) {
|
|
indexed := NewIndexedSeries("test", map[UnixTime]float64{
|
|
UnixTime(0): 1.0,
|
|
UnixTime(1): 2.0,
|
|
UnixTime(2): 3.0,
|
|
UnixTime(3): 4.0,
|
|
UnixTime(4): 5.0,
|
|
})
|
|
|
|
added := indexed.Copy().Add(indexed)
|
|
expected := []float64{2.0, 4.0, 6.0, 8.0, 10.0}
|
|
if added.Len() != 5 {
|
|
t.Fatalf("Expected 5 rows, got %d", added.Len())
|
|
}
|
|
for i := 0; i < 5; i++ {
|
|
if val := added.Float(i); val != expected[i] {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, expected[i], val)
|
|
}
|
|
}
|
|
|
|
added.DivFloat(2.0)
|
|
for i := 0; i < 5; i++ {
|
|
if val := added.Float(i); !EqualApprox(val, indexed.Float(i)) {
|
|
t.Errorf("(%d)\tExpected %f, got %v", i, indexed.Float(i), val)
|
|
}
|
|
}
|
|
}
|