Files
gocryptotrader/gctscript/modules/ta/indicators/indicators_test.go
Adrian Gallagher 225429bda6 CI/build: Update Go version, linters and fix minor issues (#1612)
* CI/build: Update Go version, linters and fix minor issues

* linters: Add intrange, copyloopvar, additional go vet linters to match gopls and fix issues
2024-08-16 17:41:11 +10:00

543 lines
12 KiB
Go

package indicators
import (
"errors"
"math/rand"
"os"
"reflect"
"testing"
"time"
objects "github.com/d5/tengo/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/thrasher-corp/gct-ta/indicators"
"github.com/thrasher-corp/gocryptotrader/gctscript/wrappers/validator"
)
const errFailedConversion = "0 failed conversion"
var (
ohlcvData = &objects.Array{}
ohlcvDataInvalid = &objects.Array{}
testString = "1D10TH0R53"
)
func TestMain(m *testing.M) {
for x := range 100 {
v := rand.Float64() //nolint:gosec // no need to import crypo/rand for testing
candle := &objects.Array{}
candle.Value = append(candle.Value, &objects.Time{Value: time.Now()},
&objects.Float{Value: v},
&objects.Float{Value: v + float64(x)},
&objects.Float{Value: v - float64(x)},
&objects.Float{Value: v},
&objects.Float{Value: v},
)
ohlcvData.Value = append(ohlcvData.Value, candle)
}
for range 5 {
candle := &objects.Array{}
candle.Value = append(candle.Value, &objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: testString},
)
ohlcvDataInvalid.Value = append(ohlcvDataInvalid.Value, candle)
}
os.Exit(m.Run())
}
func TestMfi(t *testing.T) {
_, err := mfi()
if err != nil {
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Error(err)
}
}
v := &objects.String{Value: testString}
_, err = mfi(ohlcvData, v)
if err != nil {
if err.Error() != errFailedConversion {
t.Error(err)
}
}
_, err = mfi(ohlcvDataInvalid, &objects.Int{Value: 14})
if err == nil {
t.Error("expected conversion failed error")
}
_, err = mfi(ohlcvData, &objects.Int{Value: 14})
if err != nil {
t.Error(err)
}
_, err = mfi(v, &objects.Int{Value: 14})
if err != nil {
if err.Error() != "OHLCV data failed conversion" {
t.Error(err)
}
}
validator.IsTestExecution.Store(true)
ret, err := mfi(ohlcvData, &objects.Int{Value: 10})
if err != nil {
t.Fatal(err)
}
if (ret == &objects.Array{}) {
t.Error("expected empty Array on test execution received data")
}
validator.IsTestExecution.Store(false)
}
func TestRsi(t *testing.T) {
_, err := rsi()
assert.ErrorIs(t, err, objects.ErrWrongNumArguments)
v := &objects.String{Value: testString}
_, err = rsi(ohlcvData, v)
assert.ErrorContains(t, err, errFailedConversion)
_, err = rsi(ohlcvData, &objects.Int{Value: 14})
assert.NoError(t, err, "rsi should not throw an error on valid input")
_, err = rsi(v, &objects.Int{Value: 14})
assert.ErrorContains(t, err, "failed conversion")
_, err = rsi(ohlcvDataInvalid, &objects.Int{Value: 14})
assert.ErrorContains(t, err, "failed conversion")
validator.IsTestExecution.Store(true)
ret, err := rsi(ohlcvData, &objects.Int{Value: 14})
require.NoError(t, err, "rsi should not throw an error")
assert.NotNil(t, ret)
validator.IsTestExecution.Store(false)
}
func TestEMA(t *testing.T) {
_, err := ema()
if err != nil {
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Error(err)
}
}
v := &objects.String{Value: testString}
_, err = ema(ohlcvData, v)
if err != nil {
if err.Error() != errFailedConversion {
t.Error(err)
}
}
_, err = ema(ohlcvData, &objects.Int{Value: 14})
if err != nil {
t.Error(err)
}
_, err = ema(ohlcvDataInvalid, &objects.String{Value: testString})
if err == nil {
t.Error("expected conversion failed error")
}
_, err = ema(&objects.String{Value: testString}, &objects.String{Value: testString})
if err == nil {
t.Error("expected conversion failed error")
}
validator.IsTestExecution.Store(true)
ret, err := ema(ohlcvData, &objects.Int{Value: 14})
if err != nil {
t.Fatal(err)
}
if (ret == &objects.Array{}) {
t.Error("expected empty Array on test execution received data")
}
validator.IsTestExecution.Store(false)
}
func TestSMA(t *testing.T) {
_, err := sma()
if err != nil {
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Error(err)
}
}
v := &objects.String{Value: testString}
_, err = sma(ohlcvData, v)
if err != nil {
if err.Error() != errFailedConversion {
t.Error(err)
}
}
_, err = sma(ohlcvData, &objects.Int{Value: 14})
if err != nil {
t.Error(err)
}
_, err = sma(ohlcvDataInvalid, &objects.String{Value: testString})
if err == nil {
t.Error("expected conversion failed error")
}
_, err = sma(&objects.String{Value: testString}, &objects.String{Value: testString})
if err == nil {
t.Error("expected conversion failed error")
}
validator.IsTestExecution.Store(true)
ret, err := sma(ohlcvData, &objects.Int{Value: 14})
if err != nil {
t.Fatal(err)
}
if (ret == &objects.Array{}) {
t.Error("expected empty Array on test execution received data")
}
validator.IsTestExecution.Store(false)
}
func TestMACD(t *testing.T) {
_, err := macd()
if err != nil {
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Error(err)
}
}
v := &objects.String{Value: testString}
_, err = macd(ohlcvData, &objects.Int{Value: 12}, &objects.Int{Value: 26}, v)
if err != nil {
if err.Error() != errFailedConversion {
t.Error(err)
}
}
_, err = macd(ohlcvData, &objects.Int{Value: 12}, &objects.Int{Value: 26}, &objects.Int{Value: 9})
if err != nil {
t.Error(err)
}
_, err = macd(ohlcvDataInvalid,
&objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: testString})
if err == nil {
t.Error("expected conversion failed error")
}
_, err = macd(&objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: testString})
if err == nil {
t.Error("expected conversion failed error")
}
validator.IsTestExecution.Store(true)
ret, err := macd(ohlcvData, &objects.Int{Value: 12}, &objects.Int{Value: 26}, &objects.Int{Value: 9})
if err != nil {
t.Fatal(err)
}
if (ret == &objects.Array{}) {
t.Error("expected empty Array on test execution received data")
}
validator.IsTestExecution.Store(false)
}
func TestAtr(t *testing.T) {
_, err := atr()
if err != nil {
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Error(err)
}
}
v := &objects.String{Value: testString}
_, err = atr(ohlcvData, v)
if err != nil {
if err.Error() != errFailedConversion {
t.Error(err)
}
}
_, err = atr(ohlcvData, &objects.Int{Value: 14})
if err != nil {
t.Error(err)
}
_, err = atr(v, &objects.Int{Value: 14})
if err == nil {
t.Error("expected conversion failed error")
}
_, err = atr(ohlcvDataInvalid, &objects.Int{Value: 14})
if err == nil {
t.Error("expected conversion failed error")
}
validator.IsTestExecution.Store(true)
ret, err := atr(ohlcvData, &objects.Int{Value: 14})
if err != nil {
t.Fatal(err)
}
if (ret == &objects.Array{}) {
t.Error("expected empty Array on test execution received data")
}
validator.IsTestExecution.Store(false)
}
func TestBbands(t *testing.T) {
_, err := bbands()
if err != nil {
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Error(err)
}
}
_, err = bbands(&objects.String{Value: testString}, ohlcvData,
&objects.Int{Value: 5},
&objects.Float{Value: 2.0},
&objects.Float{Value: 2.0},
&objects.String{Value: "sma"})
if err != nil {
if err != errInvalidSelector {
t.Error(err)
}
}
_, err = bbands(&objects.String{Value: "close"}, ohlcvData,
&objects.Int{Value: 5},
&objects.Float{Value: 2.0},
&objects.Float{Value: 2.0},
&objects.String{Value: "sma"})
if err != nil {
t.Error(err)
}
validator.IsTestExecution.Store(true)
ret, err := bbands(&objects.String{Value: "close"}, ohlcvData,
&objects.Int{Value: 5},
&objects.Float{Value: 2.0},
&objects.Float{Value: 2.0},
&objects.String{Value: "sma"})
if err != nil {
t.Error(err)
}
if (ret == &objects.Array{}) {
t.Error("expected empty Array on test execution received data")
}
validator.IsTestExecution.Store(false)
_, err = bbands(&objects.String{Value: "close"}, ohlcvDataInvalid,
&objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: testString},
objects.UndefinedValue)
if err == nil {
t.Error("expected conversion failed error")
}
_, err = bbands(&objects.String{Value: "close"}, &objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: testString},
&objects.String{Value: "ema"})
if err == nil {
t.Error("expected conversion failed error")
}
_, err = bbands(&objects.String{Value: "close"}, ohlcvData,
&objects.Int{Value: 5},
&objects.Float{Value: 2.0},
&objects.Float{Value: 2.0},
&objects.String{Value: testString})
if err != nil {
if !errors.Is(err, errInvalidSelector) {
t.Error(err)
}
}
_, err = bbands(objects.UndefinedValue, ohlcvData,
&objects.Int{Value: 5},
&objects.Float{Value: 2.0},
&objects.Float{Value: 2.0},
&objects.String{Value: testString})
if err == nil {
t.Error("expected conversion failed error")
}
}
func TestOBV(t *testing.T) {
_, err := obv()
if err != nil {
if !errors.Is(err, objects.ErrWrongNumArguments) {
t.Error(err)
}
}
_, err = obv(ohlcvData)
if err != nil {
t.Error(err)
}
_, err = obv(ohlcvDataInvalid)
if err == nil {
t.Error("expected conversion failed error")
}
_, err = obv(&objects.String{Value: testString})
if err == nil {
t.Error("expected conversion failed error")
}
validator.IsTestExecution.Store(true)
ret, err := obv(ohlcvData)
if err != nil {
t.Fatal(err)
}
if (ret == &objects.Array{}) {
t.Error("expected empty Array on test execution received data")
}
validator.IsTestExecution.Store(false)
}
func TestToFloat64(t *testing.T) {
value := 54.0
v, err := toFloat64(value)
if err != nil {
t.Fatal(err)
}
if reflect.TypeOf(v).Kind() != reflect.Float64 {
t.Fatalf("expected toFloat to return kind float64 received: %v", reflect.TypeOf(v).Kind())
}
v, err = toFloat64(int(value))
if err != nil {
t.Fatal(err)
}
if reflect.TypeOf(v).Kind() != reflect.Float64 {
t.Fatalf("expected toFloat to return kind float64 received: %v", reflect.TypeOf(v).Kind())
}
v, err = toFloat64(int32(value))
if err != nil {
t.Fatal(err)
}
if reflect.TypeOf(v).Kind() != reflect.Float64 {
t.Fatalf("expected toFloat to return kind float64 received: %v", reflect.TypeOf(v).Kind())
}
v, err = toFloat64(int64(value))
if err != nil {
t.Fatal(err)
}
if reflect.TypeOf(v).Kind() != reflect.Float64 {
t.Fatalf("expected toFloat to return kind float64 received: %v", reflect.TypeOf(v).Kind())
}
_, err = toFloat64("54")
if err == nil {
t.Fatalf("attempting to convert a string should fail but test passed")
}
}
func TestParseIndicatorSelector(t *testing.T) {
testCases := []struct {
name string
expected int
err error
}{
{
"open",
1,
nil,
},
{
"high",
2,
nil,
},
{
"low",
3,
nil,
},
{
"close",
4,
nil,
},
{
"vol",
5,
nil,
},
{
"invalid",
0,
errInvalidSelector,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
v, err := ParseIndicatorSelector(test.name)
if err != nil {
if err != test.err {
t.Fatal(err)
}
}
if v != test.expected {
t.Fatalf("expected %v received %v", test.expected, v)
}
})
}
}
func TestParseMAType(t *testing.T) {
testCases := []struct {
name string
expected indicators.MaType
err error
}{
{
"sma",
indicators.Sma,
nil,
},
{
"ema",
indicators.Ema,
nil,
},
{
"no",
indicators.Sma,
errInvalidSelector,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
v, err := ParseMAType(test.name)
if err != nil {
if err != test.err {
t.Fatal(err)
}
}
if v != test.expected {
t.Fatalf("expected %v received %v", test.expected, v)
}
})
}
}