mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 23:16:49 +00:00
* exchanges: add setTimeWindow boolean to GetKlineRequest params to differentiate between a set time period return from endpoint. * glorious: nits * exchange: conjugation * Update exchanges/exchange.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * glorious: nits and an assortment of differences * exchanges: remove some comments * glorious: nits * cleanup * tests: fix * Update exchanges/hitbtc/hitbtc_wrapper.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * Update exchanges/kline/kline.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * Update exchanges/kline/kline_test.go Co-authored-by: Scott <gloriousCode@users.noreply.github.com> * glorious: nits * kline: fix test * rm unused variables * almost: nits * glorious: nits * linter: fix * rm unused variable * Refactored comment in the okex tests to ensure that it accurately reflects the variable name and the issue related to the time window, as requested by GloriousCode. The previous comment did not align with the identifier assigned to the property, which could cause confusion and misunderstanding among other programmers or stakeholders. The updated comment will improve the clarity and readability of the codebase and make it easier to understand the intended purpose of the associated variables. The change was made with the aim of improving the overall quality and maintainability of the code. --------- Co-authored-by: Ryan O'Hara-Reid <ryan.oharareid@thrasher.io> Co-authored-by: Scott <gloriousCode@users.noreply.github.com>
409 lines
12 KiB
Go
409 lines
12 KiB
Go
package kline
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
|
)
|
|
|
|
func TestCreateKlineRequest(t *testing.T) {
|
|
t.Parallel()
|
|
_, err := CreateKlineRequest("", currency.EMPTYPAIR, currency.EMPTYPAIR, 0, 0, 0, time.Time{}, time.Time{}, 0)
|
|
if !errors.Is(err, ErrUnsetName) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, ErrUnsetName)
|
|
}
|
|
|
|
_, err = CreateKlineRequest("name", currency.EMPTYPAIR, currency.EMPTYPAIR, 0, 0, 0, time.Time{}, time.Time{}, 0)
|
|
if !errors.Is(err, currency.ErrCurrencyPairEmpty) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, currency.ErrCurrencyPairEmpty)
|
|
}
|
|
|
|
pair := currency.NewPair(currency.BTC, currency.USDT)
|
|
_, err = CreateKlineRequest("name", pair, currency.EMPTYPAIR, 0, 0, 0, time.Time{}, time.Time{}, 0)
|
|
if !errors.Is(err, currency.ErrCurrencyPairEmpty) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, currency.ErrCurrencyPairEmpty)
|
|
}
|
|
|
|
pair2 := pair.Upper()
|
|
_, err = CreateKlineRequest("name", pair, pair2, 0, 0, 0, time.Time{}, time.Time{}, 0)
|
|
if !errors.Is(err, asset.ErrNotSupported) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, asset.ErrNotSupported)
|
|
}
|
|
|
|
_, err = CreateKlineRequest("name", pair, pair2, asset.Spot, 0, 0, time.Time{}, time.Time{}, 0)
|
|
if !errors.Is(err, ErrInvalidInterval) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, ErrInvalidInterval)
|
|
}
|
|
|
|
_, err = CreateKlineRequest("name", pair, pair2, asset.Spot, OneHour, 0, time.Time{}, time.Time{}, 0)
|
|
if !errors.Is(err, ErrInvalidInterval) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, ErrInvalidInterval)
|
|
}
|
|
|
|
_, err = CreateKlineRequest("name", pair, pair2, asset.Spot, OneHour, OneMin, time.Time{}, time.Time{}, 0)
|
|
if !errors.Is(err, common.ErrDateUnset) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, common.ErrDateUnset)
|
|
}
|
|
|
|
start := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
_, err = CreateKlineRequest("name", pair, pair2, asset.Spot, OneHour, OneMin, start, time.Time{}, 0)
|
|
if !errors.Is(err, common.ErrDateUnset) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, common.ErrDateUnset)
|
|
}
|
|
|
|
end := start.AddDate(0, 0, 1)
|
|
_, err = CreateKlineRequest("name", pair, pair2, asset.Spot, OneHour, OneMin, start, end, 0)
|
|
if !errors.Is(err, errInvalidSpecificEndpointLimit) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, errInvalidSpecificEndpointLimit)
|
|
}
|
|
|
|
r, err := CreateKlineRequest("name", pair, pair2, asset.Spot, OneHour, OneMin, start, end, 1)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if r.Exchange != "name" {
|
|
t.Fatalf("received: '%v' but expected: '%v'", r.Exchange, "name")
|
|
}
|
|
|
|
if !r.Pair.Equal(pair) {
|
|
t.Fatalf("received: '%v' but expected: '%v'", r.Pair, pair)
|
|
}
|
|
|
|
if r.Asset != asset.Spot {
|
|
t.Fatalf("received: '%v' but expected: '%v'", r.Asset, asset.Spot)
|
|
}
|
|
|
|
if r.ExchangeInterval != OneMin {
|
|
t.Fatalf("received: '%v' but expected: '%v'", r.ExchangeInterval, OneMin)
|
|
}
|
|
|
|
if r.ClientRequired != OneHour {
|
|
t.Fatalf("received: '%v' but expected: '%v'", r.ClientRequired, OneHour)
|
|
}
|
|
|
|
if r.Start != start {
|
|
t.Fatalf("received: '%v' but expected: '%v'", r.Start, start)
|
|
}
|
|
|
|
if r.End != end {
|
|
t.Fatalf("received: '%v' but expected: '%v'", r.End, end)
|
|
}
|
|
|
|
if r.RequestFormatted.String() != "BTCUSDT" {
|
|
t.Fatalf("received: '%v' but expected: '%v'", r.RequestFormatted.String(), "BTCUSDT")
|
|
}
|
|
|
|
// Check end date/time shift if the request time is mid candle and not
|
|
// aligned correctly.
|
|
end = end.Round(0)
|
|
end = end.Add(time.Second * 30)
|
|
r, err = CreateKlineRequest("name", pair, pair2, asset.Spot, OneHour, OneMin, start, end, 1)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if !r.End.Equal(end.Add(OneHour.Duration() - (time.Second * 30))) {
|
|
t.Fatalf("received: '%v', but expected '%v'", r.End, end.Add(OneHour.Duration()-(time.Second*30)))
|
|
}
|
|
}
|
|
|
|
func TestGetRanges(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
start := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
end := start.AddDate(0, 0, 1)
|
|
pair := currency.NewPair(currency.BTC, currency.USDT)
|
|
|
|
var r *Request
|
|
_, err := r.GetRanges(100)
|
|
if !errors.Is(err, errNilRequest) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, errNilRequest)
|
|
}
|
|
|
|
r, err = CreateKlineRequest("name", pair, pair, asset.Spot, OneHour, OneMin, start, end, 1)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
holder, err := r.GetRanges(100)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if len(holder.Ranges) != 15 {
|
|
t.Fatalf("received: '%v', but expected '%v'", len(holder.Ranges), 15)
|
|
}
|
|
}
|
|
|
|
var protecThyCandles sync.Mutex
|
|
|
|
func getOneMinute() []Candle {
|
|
protecThyCandles.Lock()
|
|
candles := make([]Candle, len(oneMinuteCandles))
|
|
copy(candles, oneMinuteCandles)
|
|
protecThyCandles.Unlock()
|
|
return candles
|
|
}
|
|
|
|
var oneMinuteCandles = func() []Candle {
|
|
var candles []Candle
|
|
start := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
for x := 0; x < 1442; x++ { // two extra candles.
|
|
candles = append(candles, Candle{
|
|
Time: start,
|
|
Volume: 1,
|
|
Open: 1,
|
|
High: float64(1 + x),
|
|
Low: float64(-(1 + x)),
|
|
Close: 1,
|
|
})
|
|
start = start.Add(time.Minute)
|
|
}
|
|
return candles
|
|
}()
|
|
|
|
func getOneHour() []Candle {
|
|
protecThyCandles.Lock()
|
|
candles := make([]Candle, len(oneHourCandles))
|
|
copy(candles, oneHourCandles)
|
|
protecThyCandles.Unlock()
|
|
return candles
|
|
}
|
|
|
|
var oneHourCandles = func() []Candle {
|
|
var candles []Candle
|
|
start := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
for x := 0; x < 24; x++ {
|
|
candles = append(candles, Candle{
|
|
Time: start,
|
|
Volume: 1,
|
|
Open: 1,
|
|
High: float64(1 + x),
|
|
Low: float64(-(1 + x)),
|
|
Close: 1,
|
|
})
|
|
start = start.Add(time.Hour)
|
|
}
|
|
return candles
|
|
}()
|
|
|
|
func TestRequest_ProcessResponse(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
start := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
end := start.AddDate(0, 0, 1)
|
|
pair := currency.NewPair(currency.BTC, currency.USDT)
|
|
|
|
var r *Request
|
|
_, err := r.ProcessResponse(nil)
|
|
if !errors.Is(err, errNilRequest) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, errNilRequest)
|
|
}
|
|
|
|
r = &Request{}
|
|
_, err = r.ProcessResponse(nil)
|
|
if !errors.Is(err, errNoTimeSeriesDataToConvert) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, errNoTimeSeriesDataToConvert)
|
|
}
|
|
|
|
_, err = CreateKlineRequest("name", pair, pair, asset.Spot, OneHour, OneHour, start, end, 0)
|
|
if !errors.Is(err, errInvalidSpecificEndpointLimit) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
// no conversion
|
|
r, err = CreateKlineRequest("name", pair, pair, asset.Spot, OneHour, OneHour, start, end, 1)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
holder, err := r.ProcessResponse(getOneHour())
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if len(holder.Candles) != 24 {
|
|
t.Fatalf("received: '%v', but expected '%v'", len(holder.Candles), 24)
|
|
}
|
|
|
|
// with conversion
|
|
r, err = CreateKlineRequest("name", pair, pair, asset.Spot, OneHour, OneMin, start, end, 1)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
holder, err = r.ProcessResponse(getOneMinute())
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if len(holder.Candles) != 24 {
|
|
t.Fatalf("received: '%v', but expected '%v'", len(holder.Candles), 24)
|
|
}
|
|
|
|
// Potential partial candle
|
|
end = time.Now().UTC()
|
|
start = end.AddDate(0, 0, -5).Truncate(time.Duration(OneDay))
|
|
r, err = CreateKlineRequest("name", pair, pair, asset.Spot, OneDay, OneDay, start, end, 1)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if !r.PartialCandle {
|
|
t.Fatalf("received: '%v', but expected '%v'", r.PartialCandle, true)
|
|
}
|
|
|
|
hasIncomplete := []Candle{
|
|
{Time: start, Close: 1},
|
|
{Time: start.Add(OneDay.Duration()), Close: 2},
|
|
{Time: start.Add(OneDay.Duration() * 2), Close: 3},
|
|
{Time: start.Add(OneDay.Duration() * 3), Close: 4},
|
|
{Time: start.Add(OneDay.Duration() * 4), Close: 5},
|
|
{Time: start.Add(OneDay.Duration() * 5), Close: 5.5},
|
|
}
|
|
|
|
sweetItem, err := r.ProcessResponse(hasIncomplete)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if sweetItem.Candles[len(sweetItem.Candles)-1].ValidationIssues != PartialCandle {
|
|
t.Fatalf("received: '%v', but expected '%v'", "no issues", PartialCandle)
|
|
}
|
|
|
|
missingIncomplete := []Candle{
|
|
{Time: start, Close: 1},
|
|
{Time: start.Add(OneDay.Duration()), Close: 2},
|
|
{Time: start.Add(OneDay.Duration() * 2), Close: 3},
|
|
{Time: start.Add(OneDay.Duration() * 3), Close: 4},
|
|
{Time: start.Add(OneDay.Duration() * 4), Close: 5},
|
|
}
|
|
|
|
sweetItem, err = r.ProcessResponse(missingIncomplete)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if sweetItem.Candles[len(sweetItem.Candles)-1].ValidationIssues == PartialCandle {
|
|
t.Fatalf("received: '%v', but expected '%v'", sweetItem.Candles[len(sweetItem.Candles)-1].ValidationIssues, "no issues")
|
|
}
|
|
|
|
// end date far into the dark depths of future reality
|
|
r, err = CreateKlineRequest("name", pair, pair, asset.Spot, OneDay, OneDay, start, end.AddDate(1, 0, 0), 1)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
sweetItem, err = r.ProcessResponse(hasIncomplete)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if sweetItem.Candles[len(sweetItem.Candles)-1].ValidationIssues != PartialCandle {
|
|
t.Fatalf("received: '%v', but expected '%v'", "no issues", PartialCandle)
|
|
}
|
|
|
|
sweetItem, err = r.ProcessResponse(missingIncomplete)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if len(sweetItem.Candles) != 5 {
|
|
t.Fatalf("received: '%v', but expected '%v'", len(sweetItem.Candles), 5)
|
|
}
|
|
|
|
if sweetItem.Candles[len(sweetItem.Candles)-1].ValidationIssues == PartialCandle {
|
|
t.Fatalf("received: '%v', but expected '%v'", sweetItem.Candles[len(sweetItem.Candles)-1].ValidationIssues, "no issues")
|
|
}
|
|
|
|
laterEndDate := end.AddDate(1, 0, 0).UTC().Truncate(time.Duration(OneDay)).Add(-time.Duration(OneDay))
|
|
if sweetItem.Candles[len(sweetItem.Candles)-1].Time.Equal(laterEndDate) {
|
|
t.Fatalf("received: '%v', but expected '%v'", sweetItem.Candles[len(sweetItem.Candles)-1].Time, "should not equal")
|
|
}
|
|
}
|
|
|
|
func TestExtendedRequest_ProcessResponse(t *testing.T) {
|
|
t.Parallel()
|
|
ohc := getOneHour()
|
|
start := ohc[0].Time
|
|
end := ohc[len(ohc)-1].Time.Add(OneHour.Duration())
|
|
pair := currency.NewPair(currency.BTC, currency.USDT)
|
|
|
|
var rExt *ExtendedRequest
|
|
_, err := rExt.ProcessResponse(nil)
|
|
if !errors.Is(err, errNilRequest) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, errNilRequest)
|
|
}
|
|
|
|
rExt = &ExtendedRequest{}
|
|
_, err = rExt.ProcessResponse(nil)
|
|
if !errors.Is(err, errNoTimeSeriesDataToConvert) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, errNoTimeSeriesDataToConvert)
|
|
}
|
|
|
|
// no conversion
|
|
r, err := CreateKlineRequest("name", pair, pair, asset.Spot, OneHour, OneHour, start, end, 1)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
r.ProcessedCandles = ohc
|
|
dates, err := r.GetRanges(100)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
rExt = &ExtendedRequest{r, dates}
|
|
|
|
holder, err := rExt.ProcessResponse(ohc)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if len(holder.Candles) != 24 {
|
|
t.Fatalf("received: '%v', but expected '%v'", len(holder.Candles), 24)
|
|
}
|
|
|
|
// with conversion
|
|
ohc = getOneMinute()
|
|
r, err = CreateKlineRequest("name", pair, pair, asset.Spot, OneHour, OneMin, start, end, 1)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
dates, err = r.GetRanges(100)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
r.IsExtended = true
|
|
rExt = &ExtendedRequest{r, dates}
|
|
holder, err = rExt.ProcessResponse(ohc)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("received: '%v', but expected '%v'", err, nil)
|
|
}
|
|
|
|
if len(holder.Candles) != 24 {
|
|
t.Fatalf("received: '%v', but expected '%v'", len(holder.Candles), 24)
|
|
}
|
|
}
|
|
|
|
func TestExtendedRequest_Size(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var rExt *ExtendedRequest
|
|
if rExt.Size() != 0 {
|
|
t.Fatalf("received: '%v', but expected '%v'", rExt.Size(), 0)
|
|
}
|
|
|
|
rExt = &ExtendedRequest{RangeHolder: &IntervalRangeHolder{Limit: 100, Ranges: []IntervalRange{{}, {}}}}
|
|
if rExt.Size() != 200 {
|
|
t.Fatalf("received: '%v', but expected '%v'", rExt.Size(), 200)
|
|
}
|
|
}
|