diff --git a/binanceusdmfutures/api_klines_test.go b/binanceusdmfutures/api_klines_test.go new file mode 100644 index 0000000..1c83c17 --- /dev/null +++ b/binanceusdmfutures/api_klines_test.go @@ -0,0 +1,407 @@ +package binanceusdmfutures + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/marianogappa/signal-checker/common" +) + +func TestHappyToCandlesticks(t *testing.T) { + testCandlestick := `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]` + + sr := successfulResponse{} + err := json.Unmarshal([]byte(testCandlestick), &sr.ResponseCandlesticks) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + cs, err := sr.toCandlesticks() + if err != nil { + t.Fatalf("Candlestick should have converted successfully but returned: %v", err) + } + if len(cs) != 1 { + t.Fatalf("Should have converted 1 candlesticks but converted: %v", len(cs)) + } + expected := common.Candlestick{ + Timestamp: 1499040000, + OpenPrice: f(0.01634790), + ClosePrice: f(0.01577100), + LowestPrice: f(0.01575800), + HighestPrice: f(0.80000000), + Volume: f(148976.11427815), + NumberOfTrades: 308, + } + if cs[0] != expected { + t.Fatalf("Candlestick should have been %v but was %v", expected, cs[0]) + } +} + +func TestUnhappyToCandlesticks(t *testing.T) { + tests := []string{ + // candlestick %v has len != 12! Invalid syntax from Binance + `[ + [ + 1499040000000 + ] + ]`, + // candlestick %v has non-int open time! Invalid syntax from Binance + `[ + [ + "1499040000000", + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v has non-string open! Invalid syntax from Binance + `[ + [ + 1499040000000, + 0.01634790, + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v had open = %v! Invalid syntax from Binance + `[ + [ + 1499040000000, + "INVALID", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v has non-string high! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + 0.80000000, + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v had high = %v! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "INVALID", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v has non-string low! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + 0.01575800, + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v had low = %v! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "INVALID", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v has non-string close! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + 0.01577100, + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v had close = %v! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "INVALID", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v has non-string volume! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + 148976.11427815, + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v had volume = %v! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "INVALID", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v has non-int close time! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + "1499644799999", + "2434.19055334", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v has non-string quote asset volume! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + 2434.19055334, + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v had quote asset volume = %v! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "INVALID", + 308, + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v has non-int number of trades! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + "308", + "1756.87402397", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v has non-string taker base asset volume! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + 1756.87402397, + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v had taker base asset volume = %v! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "INVALID", + "28.46694368", + "17928899.62484339" + ] + ]`, + // candlestick %v has non-string taker quote asset volume! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + 28.46694368, + "17928899.62484339" + ] + ]`, + // candlestick %v had taker quote asset volume = %v! Invalid syntax from Binance + `[ + [ + 1499040000000, + "0.01634790", + "0.80000000", + "0.01575800", + "0.01577100", + "148976.11427815", + 1499644799999, + "2434.19055334", + 308, + "1756.87402397", + "INVALID", + "17928899.62484339" + ] + ]`, + } + + for i, ts := range tests { + t.Run(fmt.Sprintf("Unhappy toCandlesticks %v", i), func(t *testing.T) { + sr := successfulResponse{} + err := json.Unmarshal([]byte(ts), &sr.ResponseCandlesticks) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + cs, err := sr.toCandlesticks() + if err == nil { + t.Fatalf("Candlestick should have failed to convert but converted successfully to: %v", cs) + } + }) + } +} + +func f(fl float64) common.JsonFloat64 { + return common.JsonFloat64(fl) +} diff --git a/coinbase/api_klines_test.go b/coinbase/api_klines_test.go new file mode 100644 index 0000000..4b56278 --- /dev/null +++ b/coinbase/api_klines_test.go @@ -0,0 +1,69 @@ +package coinbase + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/marianogappa/signal-checker/common" +) + +func TestHappyToCandlesticks(t *testing.T) { + testCandlestick := `[[1626868560,31540.72,31584.3,31540.72,31576.13,0.08432516]]` + + sr := successResponse{} + err := json.Unmarshal([]byte(testCandlestick), &sr) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + cs, err := coinbaseToCandlesticks(sr) + if err != nil { + t.Fatalf("Candlestick should have converted successfully but returned: %v", err) + } + if len(cs) != 1 { + t.Fatalf("Should have converted 1 candlesticks but converted: %v", len(cs)) + } + expected := common.Candlestick{ + Timestamp: 1626868560, + OpenPrice: f(31540.72), + ClosePrice: f(31576.13), + LowestPrice: f(31540.72), + HighestPrice: f(31584.3), + Volume: f(0.08432516), + NumberOfTrades: 0, + } + if cs[0] != expected { + t.Fatalf("Candlestick should have been %v but was %v", expected, cs[0]) + } +} + +func TestUnhappyToCandlesticks(t *testing.T) { + tests := []string{ + `[["1626868560",31540.72,31584.3,31540.72,31576.13,0.08432516]]`, + `[[1626868560,"31540.72",31584.3,31540.72,31576.13,0.08432516]]`, + `[[1626868560,31540.72,"31584.3",31540.72,31576.13,0.08432516]]`, + `[[1626868560,31540.72,31584.3,"31540.72",31576.13,0.08432516]]`, + `[[1626868560,31540.72,31584.3,31540.72,"31576.13",0.08432516]]`, + `[[1626868560,31540.72,31584.3,31540.72,31576.13,"0.08432516"]]`, + } + + for i, ts := range tests { + t.Run(fmt.Sprintf("Unhappy toCandlesticks %v", i), func(t *testing.T) { + sr := successResponse{} + err := json.Unmarshal([]byte(ts), &sr) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + cs, err := coinbaseToCandlesticks(sr) + if err == nil { + t.Fatalf("Candlestick should have failed to convert but converted successfully to: %v", cs) + } + }) + } +} + +func f(fl float64) common.JsonFloat64 { + return common.JsonFloat64(fl) +} diff --git a/kraken/api_klines.go b/kraken/api_klines.go index c9d5c31..54c1f1b 100644 --- a/kraken/api_klines.go +++ b/kraken/api_klines.go @@ -127,7 +127,7 @@ func (r response) toCandlesticks() ([]common.Candlestick, error) { rawVWap, ok := raw[5].(string) if !ok { - return candlesticks, fmt.Errorf("candlestick %v has non-string volume! Invalid syntax from Kraken", i) + return candlesticks, fmt.Errorf("candlestick %v has non-string vwap! Invalid syntax from Kraken", i) } vwap, err := strconv.ParseFloat(rawVWap, 64) if err != nil { diff --git a/kraken/api_klines_test.go b/kraken/api_klines_test.go new file mode 100644 index 0000000..fe9c1ec --- /dev/null +++ b/kraken/api_klines_test.go @@ -0,0 +1,95 @@ +package kraken + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/marianogappa/signal-checker/common" +) + +func TestHappyToCandlesticks(t *testing.T) { + testCandlestick := `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","34221.6","34215.7","34215.7","34215.7","0.25998804",7]],"last":1626869340}}` + + sr := response{} + err := json.Unmarshal([]byte(testCandlestick), &sr) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + cs, err := sr.toCandlesticks() + if err != nil { + t.Fatalf("Candlestick should have converted successfully but returned: %v", err) + } + if len(cs) != 1 { + t.Fatalf("Should have converted 1 candlesticks but converted: %v", len(cs)) + } + expected := common.Candlestick{ + Timestamp: 1625623260, + OpenPrice: f(34221.6), + ClosePrice: f(34215.7), + LowestPrice: f(34215.7), + HighestPrice: f(34221.6), + Volume: f(0.25998804), + NumberOfTrades: 7, + } + if cs[0] != expected { + t.Fatalf("Candlestick should have been %v but was %v", expected, cs[0]) + } +} + +func TestUnhappyToCandlesticks(t *testing.T) { + tests := []string{ + // data key [%v] did not contain an array of datapoints + `{"error":[],"result":{"XBTUSDT":"INVALID","last":1626869340}}`, + // candlestick [%v] did not contain an array of data fields, instead: [%v] + `{"error":[],"result":{"XBTUSDT":["INVALID"],"last":1626869340}}`, + // candlestick %v has non-int open time! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[["INVALID","34221.6","34221.6","34215.7","34215.7","34215.7","0.25998804",7]],"last":1626869340}}`, + // candlestick %v has non-string open! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,34221.6,"34221.6","34215.7","34215.7","34215.7","0.25998804",7]],"last":1626869340}}`, + // candlestick %v had open = %v! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"INVALID","34221.6","34215.7","34215.7","34215.7","0.25998804",7]],"last":1626869340}}`, + // candlestick %v has non-string high! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6",34221.6,"34215.7","34215.7","34215.7","0.25998804",7]],"last":1626869340}}`, + // candlestick %v had high = %v! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","INVALID","34215.7","34215.7","34215.7","0.25998804",7]],"last":1626869340}}`, + // candlestick %v has non-string low! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","34221.6",34215.7,"34215.7","34215.7","0.25998804",7]],"last":1626869340}}`, + // candlestick %v had low = %v! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","34221.6","INVALID","34215.7","34215.7","0.25998804",7]],"last":1626869340}}`, + // candlestick %v has non-string close! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","34221.6","34215.7",34215.7,"34215.7","0.25998804",7]],"last":1626869340}}`, + // candlestick %v had close = %v! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","34221.6","34215.7","INVALID","34215.7","0.25998804",7]],"last":1626869340}}`, + // candlestick %v has non-string vwap! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","34221.6","34215.7","34215.7",34215.7,"0.25998804",7]],"last":1626869340}}`, + // candlestick %v had vwap = %v! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","34221.6","34215.7","34215.7","INVALID","0.25998804",7]],"last":1626869340}}`, + // candlestick %v has non-string volume! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","34221.6","34215.7","34215.7","34215.7",0.25998804,7]],"last":1626869340}}`, + // candlestick %v had volume = %v! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","34221.6","34215.7","34215.7","34215.7","INVALID",7]],"last":1626869340}}`, + // candlestick %v has non-int trade count! Invalid syntax from Kraken + `{"error":[],"result":{"XBTUSDT":[[1625623260,"34221.6","34221.6","34215.7","34215.7","34215.7","0.25998804","7"]],"last":1626869340}}`, + } + + for i, ts := range tests { + t.Run(fmt.Sprintf("Unhappy toCandlesticks %v", i), func(t *testing.T) { + sr := response{} + err := json.Unmarshal([]byte(ts), &sr) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + cs, err := sr.toCandlesticks() + if err == nil { + t.Fatalf("Candlestick should have failed to convert but converted successfully to: %v", cs) + } + }) + } +} + +func f(fl float64) common.JsonFloat64 { + return common.JsonFloat64(fl) +} diff --git a/kucoin/api_klines.go b/kucoin/api_klines.go index 32f659b..52e508f 100644 --- a/kucoin/api_klines.go +++ b/kucoin/api_klines.go @@ -36,7 +36,7 @@ func responseToCandlesticks(data [][]string) ([]common.Candlestick, error) { raw := data[i] candlestick := kucoinCandlestick{} if len(raw) != 7 { - return candlesticks, fmt.Errorf("candlestick %v has len != 12! Invalid syntax from Kucoin", i) + return candlesticks, fmt.Errorf("candlestick %v has len != 7! Invalid syntax from Kucoin", i) } rawOpenTime, err := strconv.Atoi(raw[0]) if err != nil { @@ -74,7 +74,7 @@ func responseToCandlesticks(data [][]string) ([]common.Candlestick, error) { } candlestick.Volume = rawVolume - rawTurnover, err := strconv.ParseFloat(raw[5], 64) + rawTurnover, err := strconv.ParseFloat(raw[6], 64) if err != nil { return candlesticks, fmt.Errorf("candlestick %v has non-float turnover! Err was %v. Invalid syntax from Kucoin", i, err) } diff --git a/kucoin/api_klines_test.go b/kucoin/api_klines_test.go new file mode 100644 index 0000000..d95aae6 --- /dev/null +++ b/kucoin/api_klines_test.go @@ -0,0 +1,79 @@ +package kucoin + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/marianogappa/signal-checker/common" +) + +func TestHappyToCandlesticks(t *testing.T) { + testCandlestick := `[["1566789720","10411.5","10401.9","10411.5","10396.3","29.11357276","302889.301529914"]]` + + sr := [][]string{} + err := json.Unmarshal([]byte(testCandlestick), &sr) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + cs, err := responseToCandlesticks(sr) + if err != nil { + t.Fatalf("Candlestick should have converted successfully but returned: %v", err) + } + if len(cs) != 1 { + t.Fatalf("Should have converted 1 candlesticks but converted: %v", len(cs)) + } + expected := common.Candlestick{ + Timestamp: 1566789720, + OpenPrice: f(10411.5), + ClosePrice: f(10401.9), + LowestPrice: f(10396.3), + HighestPrice: f(10411.5), + Volume: f(29.11357276), + NumberOfTrades: 0, + } + if cs[0] != expected { + t.Fatalf("Candlestick should have been %v but was %v", expected, cs[0]) + } +} + +func TestUnhappyToCandlesticks(t *testing.T) { + tests := []string{ + // candlestick %v has len != 7! Invalid syntax from Kucoin", i) + `[["1566789720"]]`, + // candlestick %v has non-int open time! Err was %v. Invalid syntax from Kucoin", i, err) + `[["INVALID","10411.5","10401.9","10411.5","10396.3","29.11357276","302889.301529914"]]`, + // candlestick %v has non-float open! Err was %v. Invalid syntax from Kucoin", i, err) + `[["1566789720","INVALID","10401.9","10411.5","10396.3","29.11357276","302889.301529914"]]`, + // candlestick %v has non-float close! Err was %v. Invalid syntax from Kucoin", i, err) + `[["1566789720","10411.5","INVALID","10411.5","10396.3","29.11357276","302889.301529914"]]`, + // candlestick %v has non-float high! Err was %v. Invalid syntax from Kucoin", i, err) + `[["1566789720","10411.5","10401.9","INVALID","10396.3","29.11357276","302889.301529914"]]`, + // candlestick %v has non-float low! Err was %v. Invalid syntax from Kucoin", i, err) + `[["1566789720","10411.5","10401.9","10411.5","INVALID","29.11357276","302889.301529914"]]`, + // candlestick %v has non-float volume! Err was %v. Invalid syntax from Kucoin", i, err) + `[["1566789720","10411.5","10401.9","10411.5","10396.3","INVALID","302889.301529914"]]`, + // candlestick %v has non-float turnover! Err was %v. Invalid syntax from Kucoin", i, err) + `[["1566789720","10411.5","10401.9","10411.5","10396.3","29.11357276","INVALID"]]`, + } + + for i, ts := range tests { + t.Run(fmt.Sprintf("Unhappy toCandlesticks %v", i), func(t *testing.T) { + sr := [][]string{} + err := json.Unmarshal([]byte(ts), &sr) + if err != nil { + t.Fatalf("Unmarshal failed: %v", err) + } + + cs, err := responseToCandlesticks(sr) + if err == nil { + t.Fatalf("Candlestick should have failed to convert but converted successfully to: %v", cs) + } + }) + } +} + +func f(fl float64) common.JsonFloat64 { + return common.JsonFloat64(fl) +}