mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-16 23:16:48 +00:00
* added mfi and example * renamed to moving average * converted to array return type and added obv and mfi * started work on test coverage * test coverage added for rsi & mfi * test coverage added for all indicators removed go mod replace moved to append helper method * moved all indicators to new appendTo and increased test coverage * added additional test and bumped go-talib to latest commi * go.mod update * linter fixes * go mod clean up * small fixes * reverted changes from previous attempt to rework as data is still incorrect now passing full OHLCV data back to script binding * testing new structure of passing full ohlcv data * started linking ohlcv to gctscript * OHCLV link up completed reworking passing back to indicators started * OHCLV link up completed reworking passing back to indicators started * added test coverage for tofloat * linter fixes (gofmt) * removed unused value * improved test coverage * added correct detection for 1w added ParseInterval test coverage moved OHCLV string to const * removed unused value * first round of changes addressed * all indicators have been split with packages named after each indicator and a new calculate() method added * linters * fixed tests * added check to check ta is running in validator for uploading * Added test data for OHLCV testing new indicator interface for wrapper * typed const to float64 * reworked validator data to generate previous timestamps * rewored macd to return slice of array * adding bbands linking and example * why didn't this pick it up before :D * bumped up total number of modules for test * moved parseIndicator to exchange added comments * test coverage added for ParseMAType & ParseIndicatorSelector * gofmt * WIP changes * updated tests for bbands & obv bumped to latest go-talib * move multiple use strong to const * reverted rpc.pb.go to master * added 4w option * removed selector from obv as unneeded * improved test coverage and reworked all indicator methods on how they pass errors back * order incoming OHCLV data * revert go.mod * removed verbose toggles * added spot asset type * removed 4w as its unused/uncommon * renamed * reworked further tests * converted all examples to use coinbasepro for consistency * updated all date ranges to 2019 + 6 months * backported binance OHLCV wrapper from #479 * removed o * rounded numbers * chnage requests addressed and attempt to fix MACD... today has been really unproctive code wise :D * Migrated to gct-ta library * Corrected test import * wording changes on test * removed TA lib from go.mod * PR changes addressed Removed parallel running from tests due to slight possibility in very extreme cases TestExecution might not be set to the expected value and will cause lower test coverage * removed pkg folder * bumped gct-ta version * gct-ta version bump
564 lines
12 KiB
Go
564 lines
12 KiB
Go
package indicators
|
|
|
|
import (
|
|
"errors"
|
|
"math/rand"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
objects "github.com/d5/tengo/v2"
|
|
"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 := 0; x < 100; x++ {
|
|
v := rand.Float64()
|
|
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 x := 0; x < 5; x++ {
|
|
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()
|
|
if err != nil {
|
|
if !errors.Is(err, objects.ErrWrongNumArguments) {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
v := &objects.String{Value: testString}
|
|
_, err = rsi(ohlcvData, v)
|
|
if err != nil {
|
|
if err.Error() != errFailedConversion {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
_, err = rsi(ohlcvData, &objects.Int{Value: 14})
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
_, err = rsi(v, &objects.Int{Value: 14})
|
|
if err == nil {
|
|
if err.Error() != "OHLCV data failed conversion" {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
_, err = rsi(ohlcvDataInvalid, &objects.Int{Value: 14})
|
|
if err == nil {
|
|
if err.Error() != "OHLCV data failed conversion" {
|
|
t.Error(err)
|
|
}
|
|
}
|
|
|
|
validator.IsTestExecution.Store(true)
|
|
ret, err := rsi(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 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 _, tests := range testCases {
|
|
test := tests
|
|
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 _, tests := range testCases {
|
|
test := tests
|
|
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)
|
|
}
|
|
})
|
|
}
|
|
}
|