test: add test cases for dema, hull, tema, till, vidya and zlema indicators

This commit is contained in:
zenix 2022-04-22 19:02:26 +09:00
parent 5dc69a6175
commit c18f684afd
10 changed files with 323 additions and 12 deletions

View File

@ -0,0 +1,55 @@
package indicator
import (
"encoding/json"
"testing"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/stretchr/testify/assert"
)
/*
python:
import pandas as pd
s = pd.Series(([0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9])
ma1 = s.ewm(span=16).mean()
ma2 = ma1.ewm(span=16).mean()
result = (2 * ma1 - ma2)
print(result)
*/
func Test_DEMA(t *testing.T) {
var Delta = 4e-2
var randomPrices = []byte(`[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`)
var input []fixedpoint.Value
if err := json.Unmarshal(randomPrices, &input); err != nil {
panic(err)
}
tests := []struct {
name string
kLines []types.KLine
want float64
next float64
all int
}{
{
name: "random_case",
kLines: buildKLines(input),
want: 6.420838,
next: 5.609367,
all: 50,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dema := DEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
dema.calculateAndUpdate(tt.kLines)
last := dema.Last()
assert.InDelta(t, tt.want, last, Delta)
assert.InDelta(t, tt.next, dema.Index(1), Delta)
assert.Equal(t, tt.all, dema.Length())
})
}
}

View File

@ -19,7 +19,7 @@ type HULL struct {
}
func (inc *HULL) Update(value float64) {
if inc.result.Length() == 0 {
if inc.result == nil {
inc.ma1 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window / 2}}
inc.ma2 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
inc.result = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, int(math.Sqrt(float64(inc.Window)))}}
@ -46,7 +46,7 @@ var _ types.Series = &HULL{}
// TODO: should we just ignore the possible overlapping?
func (inc *HULL) calculateAndUpdate(allKLines []types.KLine) {
doable := false
if inc.ma1.Length() == 0 {
if inc.ma1 == nil || inc.ma1.Length() == 0 {
doable = true
}
for _, k := range allKLines {

View File

@ -0,0 +1,55 @@
package indicator
import (
"encoding/json"
"testing"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/stretchr/testify/assert"
)
/*
python:
import pandas as pd
s = pd.Series(([0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9])
ma1 = s.ewm(span=8).mean()
ma2 = s.ewm(span=16).mean()
result = (2 * ma1 - ma2).ewm(span=4).mean()
print(result)
*/
func Test_HULL(t *testing.T) {
var Delta = 1.5e-2
var randomPrices = []byte(`[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`)
var input []fixedpoint.Value
if err := json.Unmarshal(randomPrices, &input); err != nil {
panic(err)
}
tests := []struct {
name string
kLines []types.KLine
want float64
next float64
all int
}{
{
name: "random_case",
kLines: buildKLines(input),
want: 6.002935,
next: 5.167056,
all: 50,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
hull := HULL{IntervalWindow: types.IntervalWindow{Window: 16}}
hull.calculateAndUpdate(tt.kLines)
last := hull.Last()
assert.InDelta(t, tt.want, last, Delta)
assert.InDelta(t, tt.next, hull.Index(1), Delta)
assert.Equal(t, tt.all, hull.Length())
})
}
}

View File

@ -0,0 +1,56 @@
package indicator
import (
"encoding/json"
"testing"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/stretchr/testify/assert"
)
/*
python:
import pandas as pd
s = pd.Series(([0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9])
ma1 = s.ewm(span=16).mean()
ma2 = ma1.ewm(span=16).mean()
ma3 = ma2.ewm(span=16).mean()
result = (3 * ma1 - 3 * ma2 + ma3)
print(result)
*/
func Test_TEMA(t *testing.T) {
var Delta = 4.3e-2
var randomPrices = []byte(`[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`)
var input []fixedpoint.Value
if err := json.Unmarshal(randomPrices, &input); err != nil {
panic(err)
}
tests := []struct {
name string
kLines []types.KLine
want float64
next float64
all int
}{
{
name: "random_case",
kLines: buildKLines(input),
want: 7.163145,
next: 6.106229,
all: 50,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tema := TEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
tema.calculateAndUpdate(tt.kLines)
last := tema.Last()
assert.InDelta(t, tt.want, last, Delta)
assert.InDelta(t, tt.next, tema.Index(1), Delta)
assert.Equal(t, tt.all, tema.Length())
})
}
}

View File

@ -41,7 +41,7 @@ func (inc *TILL) Update(value float64) {
inc.c1 = -cube
inc.c2 = 3.*square + 3.*cube
inc.c3 = -6.*square - 3*inc.VolumeFactor - 3*cube
inc.c4 = 1 + 3*inc.VolumeFactor + cube + 3*square
inc.c4 = 1. + 3.*inc.VolumeFactor + cube + 3.*square
}
inc.e1.Update(value)
@ -82,7 +82,7 @@ var _ types.Series = &TILL{}
func (inc *TILL) calculateAndUpdate(allKLines []types.KLine) {
doable := false
if inc.e1.Length() == 0 {
if inc.e1 == nil {
doable = true
}
for _, k := range allKLines {

View File

@ -0,0 +1,65 @@
package indicator
import (
"encoding/json"
"testing"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/stretchr/testify/assert"
)
/*
python:
import pandas as pd
s = pd.Series(([0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9])
ma1 = s.ewm(span=16).mean()
ma2 = ma1.ewm(span=16).mean()
ma3 = ma2.ewm(span=16).mean()
ma4 = ma3.ewm(span=16).mean()
ma5 = ma4.ewm(span=16).mean()
ma6 = ma5.ewm(span=16).mean()
square = 0.7 * 0.7
cube = 0.7 ** 3
c1 = -cube
c2 = 3 * square + 3 * cube
c3 = -6 * square - 3 * 0.7 - 3 * cube
c4 = 1 + 3 * 0.7 + cube + 3 * square
result = (c1 * ma6 + c2 * ma5 + c3 * ma4 + c4 * ma3)
print(result)
*/
func Test_TILL(t *testing.T) {
var Delta = 0.18
var randomPrices = []byte(`[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`)
var input []fixedpoint.Value
if err := json.Unmarshal(randomPrices, &input); err != nil {
panic(err)
}
tests := []struct {
name string
kLines []types.KLine
want float64
next float64
all int
}{
{
name: "random_case",
kLines: buildKLines(input),
want: 4.528608,
next: 4.457134,
all: 50,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
till := TILL{IntervalWindow: types.IntervalWindow{Window: 16}}
till.calculateAndUpdate(tt.kLines)
last := till.Last()
assert.InDelta(t, tt.want, last, Delta)
assert.InDelta(t, tt.next, till.Index(1), Delta)
assert.Equal(t, tt.all, till.Length())
})
}
}

View File

@ -27,7 +27,7 @@ func (inc *VIDYA) Update(value float64) {
if len(inc.input) > MaxNumOfEWMA {
inc.input = inc.input[MaxNumOfEWMATruncateSize-1:]
}
upsum := 0.
/*upsum := 0.
downsum := 0.
for i := 0; i < inc.Window; i++ {
if len(inc.input) <= i+1 {
@ -44,7 +44,9 @@ func (inc *VIDYA) Update(value float64) {
if upsum == 0 && downsum == 0 {
return
}
CMO := math.Abs((upsum - downsum) / (upsum + downsum))
CMO := math.Abs((upsum - downsum) / (upsum + downsum))*/
change := types.Change(&inc.input)
CMO := math.Abs(types.Sum(change, inc.Window) / types.Sum(types.Abs(change), inc.Window))
alpha := 2. / float64(inc.Window+1)
inc.Values.Push(value*alpha*CMO + inc.Values.Last()*(1.-alpha*CMO))
if inc.Values.Length() > MaxNumOfEWMA {

View File

@ -0,0 +1,19 @@
package indicator
import (
"testing"
"github.com/c9s/bbgo/pkg/types"
"github.com/stretchr/testify/assert"
)
func Test_VIDYA(t *testing.T) {
vidya := &VIDYA{IntervalWindow: types.IntervalWindow{Window: 16}}
vidya.Update(1)
assert.Equal(t, vidya.Last(), 1.)
vidya.Update(2)
newV := 2./17.*2. + 1.*(1.-2./17.)
assert.Equal(t, vidya.Last(), newV)
vidya.Update(1)
assert.Equal(t, vidya.Last(), vidya.Index(1))
}

View File

@ -11,7 +11,7 @@ import (
type ZLEMA struct {
types.IntervalWindow
data *EWMA
data types.Float64Slice
zlema *EWMA
lag int
@ -32,13 +32,17 @@ func (inc *ZLEMA) Length() int {
func (inc *ZLEMA) Update(value float64) {
if inc.lag == 0 || inc.zlema == nil {
inc.data = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
inc.zlema = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
inc.lag = (inc.Window - 1) / 2
inc.lag = int((float64(inc.Window)-1.)/2. + 0.5)
}
inc.data.Update(value)
data := inc.data.Last()
emaData := 2*data - inc.data.Index(inc.lag)
inc.data.Push(value)
if len(inc.data) > MaxNumOfEWMA {
inc.data = inc.data[MaxNumOfEWMATruncateSize-1:]
}
if inc.lag >= inc.data.Length() {
return
}
emaData := 2.*value - inc.data[len(inc.data)-1-inc.lag]
inc.zlema.Update(emaData)
}

View File

@ -0,0 +1,55 @@
package indicator
import (
"encoding/json"
"testing"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
"github.com/stretchr/testify/assert"
)
/*
python:
import pandas as pd
s = pd.Series(([0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9])
lag = int((16-1)/2)
emadata = s + (s - s.shift(lag))
result = emadata.ewm(span=16).mean()
print(result)
*/
func Test_ZLEMA(t *testing.T) {
var Delta = 6.5e-2
var randomPrices = []byte(`[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`)
var input []fixedpoint.Value
if err := json.Unmarshal(randomPrices, &input); err != nil {
panic(err)
}
tests := []struct {
name string
kLines []types.KLine
want float64
next float64
all int
}{
{
name: "random_case",
kLines: buildKLines(input),
want: 6.622881,
next: 5.231044,
all: 42,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
zlema := ZLEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
zlema.calculateAndUpdate(tt.kLines)
last := zlema.Last()
assert.InDelta(t, tt.want, last, Delta)
assert.InDelta(t, tt.next, zlema.Index(1), Delta)
assert.Equal(t, tt.all, zlema.Length())
})
}
}