diff --git a/.appveyor.yml b/.appveyor.yml index fabc0659..411f138b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,5 +1,7 @@ build: off +image: Visual Studio 2019 + clone_folder: c:\gopath\src\github.com\thrasher-corp\gocryptotrader cache: @@ -26,7 +28,7 @@ environment: PSQL_SSLMODE: disable PSQL_SKIPSQLCMD: true PSQL_TESTDBNAME: gct_dev_ci -stack: go 1.16.x +stack: go 1.17.x services: - postgresql96 @@ -45,11 +47,13 @@ build_script: before_test: - cd c:\gopath\src\github.com\thrasher-corp\gocryptotrader - - go get + - go env + - go version + - go install test_script: # test back-end - - go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.31.0 + - go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.42.1 - '%GOPATH%\bin\golangci-lint.exe run --verbose' - ps: >- if($env:APPVEYOR_SCHEDULED_BUILD -eq 'true') { diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 830a6c37..08eb5e8c 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -9,4 +9,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: - version: v1.31 + version: v1.42.1 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 83568ede..45edfaf2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,7 +1,7 @@ on: [push, pull_request] name: CI env: - GO_VERSION: 1.16.x + GO_VERSION: 1.17.x jobs: backend-psql: name: GoCryptoTrader back-end with PSQL diff --git a/.golangci.yml b/.golangci.yml index 2283b0f5..3adb168a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,5 +1,5 @@ run: - timeout: 2m30s + timeout: 3m issues-exit-code: 1 tests: true skip-dirs: @@ -25,11 +25,18 @@ linters: # disabled by default linters - asciicheck - bodyclose +# - cyclop - depguard - dogsled # - dupl + - durationcheck + - errname +# - errorlint # - exhaustive +# - exhaustivestruct - exportloopref +# - forbidigo + - forcetypeassert # - funlen - gci # - gochecknoglobals @@ -43,31 +50,45 @@ linters: # - goerr113 - gofmt # - gofumpt - - goheader +# - goheader - goimports - - golint +# - golint // deprecated since 1.41.0, replaced by revive # - gomnd + - gomoddirectives - gomodguard - goprintffuncname - gosec -# - interfacer -# - lll + - ifshort +# - importas +# - interfacer // deprecated by its owner +# - lll + - makezero # - maligned - misspell - nakedret # - nestif + - nilerr # - nlreturn - noctx - nolintlint +# - paralleltest # - prealloc + - predeclared +# - promlinter + - revive - rowserrcheck - - scopelint - - sqlclosecheck +# - scopelint // deprecated since v1.39.0, replaced by exportloopref +# - sqlclosecheck - stylecheck +# - tagliatelle # - testpackage + - thelper + - tparallel - unconvert - unparam + - wastedassign - whitespace +# - wrapcheck # - wsl linters-settings: diff --git a/.travis.yml b/.travis.yml index 39fe2a57..b4d6ef1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ matrix: dist: xenial name: 'GoCryptoTrader [back-end] [linux] [64-bit]' go: - - 1.15.x + - 1.17.x env: - GO111MODULE=on - PSQL_USER=postgres @@ -43,7 +43,7 @@ matrix: dist: xenial name: 'GoCryptoTrader [back-end] [linux] [32-bit]' go: - - 1.15.x + - 1.17.x env: - GO111MODULE=on - NO_RACE_TEST=1 @@ -72,7 +72,7 @@ matrix: os: osx name: 'GoCryptoTrader [back-end] [darwin]' go: - - 1.15.x + - 1.17.x env: - GO111MODULE=on - PSQL_USER=postgres diff --git a/Dockerfile b/Dockerfile index 29350175..9687cb5b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.15 as build +FROM golang:1.17 as build WORKDIR /go/src/github.com/thrasher-corp/gocryptotrader COPY . . RUN GO111MODULE=on go mod vendor diff --git a/Makefile b/Makefile index c93eae84..839919bd 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ LDFLAGS = -ldflags "-w -s" GCTPKG = github.com/thrasher-corp/gocryptotrader -LINTPKG = github.com/golangci/golangci-lint/cmd/golangci-lint@v1.31.0 +LINTPKG = github.com/golangci/golangci-lint/cmd/golangci-lint@v1.42.1 LINTBIN = $(GOPATH)/bin/golangci-lint GCTLISTENPORT=9050 GCTPROFILERLISTENPORT=8085 @@ -14,11 +14,11 @@ CONFIG_FLAG = $(if $(CONFIG),-config $(CONFIG),) all: check build get: - GO111MODULE=on go get $(GCTPKG) + go install $(GCTPKG) linter: - GO111MODULE=on go get $(GCTPKG) - GO111MODULE=on go get $(LINTPKG) + go install $(GCTPKG) + go install $(LINTPKG) test -z "$$($(LINTBIN) run --verbose | tee /dev/stderr)" check: linter test @@ -31,19 +31,19 @@ else endif build: - GO111MODULE=on go build $(LDFLAGS) + go build $(LDFLAGS) install: - GO111MODULE=on go install $(LDFLAGS) + go install $(LDFLAGS) fmt: gofmt -l -w -s $(shell find . -type f -name '*.go') update_deps: - GO111MODULE=on go mod verify - GO111MODULE=on go mod tidy + go mod verify + go mod tidy rm -rf vendor - GO111MODULE=on go mod vendor + go mod vendor .PHONY: profile_heap profile_heap: diff --git a/backtester/config/config_test.go b/backtester/config/config_test.go index 26bb8162..79977b41 100644 --- a/backtester/config/config_test.go +++ b/backtester/config/config_test.go @@ -1208,8 +1208,7 @@ func TestValidate(t *testing.T) { }, }, } - err := c.Validate() - if !errors.Is(err, nil) { + if err := c.Validate(); !errors.Is(err, nil) { t.Errorf("received %v expected %v", err, nil) } } diff --git a/backtester/config/configbuilder/main.go b/backtester/config/configbuilder/main.go index 6c65feac..75f25cb3 100644 --- a/backtester/config/configbuilder/main.go +++ b/backtester/config/configbuilder/main.go @@ -399,8 +399,7 @@ func parseDatabase(reader *bufio.Reader, cfg *config.Config) error { } fmt.Printf("What is the end date? Leave blank for \"%v\"\n", defaultStart.Format(gctcommon.SimpleTimeFormat)) - endDate := quickParse(reader) - if endDate != "" { + if endDate := quickParse(reader); endDate != "" { cfg.DataSettings.DatabaseData.EndDate, err = time.Parse(endDate, gctcommon.SimpleTimeFormat) if err != nil { return err diff --git a/backtester/data/data_test.go b/backtester/data/data_test.go index ab76211d..147c6c3e 100644 --- a/backtester/data/data_test.go +++ b/backtester/data/data_test.go @@ -19,8 +19,7 @@ type fakeDataHandler struct { func TestBaseDataFunctions(t *testing.T) { t.Parallel() var d Base - latest := d.Latest() - if latest != nil { + if latest := d.Latest(); latest != nil { t.Error("expected nil") } d.Next() @@ -37,17 +36,14 @@ func TestBaseDataFunctions(t *testing.T) { if o != 0 { t.Error("expected 0") } - list := d.List() - if list != nil { + if list := d.List(); list != nil { t.Error("expected nil") } - history := d.History() - if history != nil { + if history := d.History(); history != nil { t.Error("expected nil") } d.SetStream(nil) - st := d.GetStream() - if st != nil { + if st := d.GetStream(); st != nil { t.Error("expected nil") } d.Reset() @@ -91,24 +87,24 @@ func TestStream(t *testing.T) { d.SortStream() - f = d.Next().(fakeDataHandler) - if f.time != 1 { + f, ok := d.Next().(fakeDataHandler) + if f.time != 1 || !ok { t.Error("expected 1") } - f = d.Next().(fakeDataHandler) - if f.time != 2 { + f, ok = d.Next().(fakeDataHandler) + if f.time != 2 || !ok { t.Error("expected 2") } - f = d.Next().(fakeDataHandler) - if f.time != 4 { + f, ok = d.Next().(fakeDataHandler) + if f.time != 4 || !ok { t.Error("expected 4") } - f = d.Next().(fakeDataHandler) - if f.time != 10 { + f, ok = d.Next().(fakeDataHandler) + if f.time != 10 || !ok { t.Error("expected 10") } - f = d.Next().(fakeDataHandler) - if f.time != 20 { + f, ok = d.Next().(fakeDataHandler) + if f.time != 20 || !ok { t.Error("expected 20") } } diff --git a/backtester/data/kline/kline_test.go b/backtester/data/kline/kline_test.go index 6489857a..33c0066a 100644 --- a/backtester/data/kline/kline_test.go +++ b/backtester/data/kline/kline_test.go @@ -81,8 +81,7 @@ func TestHasDataAtTime(t *testing.T) { }, }, } - err := d.Load() - if err != nil { + if err := d.Load(); err != nil { t.Error(err) } diff --git a/backtester/eventhandlers/eventholder/eventholder_test.go b/backtester/eventhandlers/eventholder/eventholder_test.go index 3ccbe85b..912b5c86 100644 --- a/backtester/eventhandlers/eventholder/eventholder_test.go +++ b/backtester/eventhandlers/eventholder/eventholder_test.go @@ -28,8 +28,7 @@ func TestAppendEvent(t *testing.T) { func TestNextEvent(t *testing.T) { t.Parallel() e := Holder{Queue: []common.EventHandler{}} - ev := e.NextEvent() - if ev != nil { + if ev := e.NextEvent(); ev != nil { t.Error("expected not ok") } diff --git a/backtester/eventhandlers/portfolio/holdings/holdings.go b/backtester/eventhandlers/portfolio/holdings/holdings.go index e5a23720..28428b24 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings.go @@ -58,8 +58,7 @@ func (h *Holding) HasFunds() bool { func (h *Holding) update(e fill.Event, f funding.IPairReader) { direction := e.GetDirection() - o := e.GetOrder() - if o != nil { + if o := e.GetOrder(); o != nil { amount := decimal.NewFromFloat(o.Amount) fee := decimal.NewFromFloat(o.Fee) price := decimal.NewFromFloat(o.Price) diff --git a/backtester/eventhandlers/portfolio/holdings/holdings_test.go b/backtester/eventhandlers/portfolio/holdings/holdings_test.go index efa96ca0..8d02633a 100644 --- a/backtester/eventhandlers/portfolio/holdings/holdings_test.go +++ b/backtester/eventhandlers/portfolio/holdings/holdings_test.go @@ -60,7 +60,7 @@ func TestUpdate(t *testing.T) { if err != nil { t.Error(err) } - t1 := h.Timestamp + t1 := h.Timestamp // nolint:ifshort,nolintlint // false positive and triggers only on Windows h.Update(&fill.Fill{ Base: event.Base{ Time: time.Now(), diff --git a/backtester/eventhandlers/portfolio/portfolio.go b/backtester/eventhandlers/portfolio/portfolio.go index 27ee520a..8d268bf6 100644 --- a/backtester/eventhandlers/portfolio/portfolio.go +++ b/backtester/eventhandlers/portfolio/portfolio.go @@ -262,8 +262,7 @@ func (p *Portfolio) addComplianceSnapshot(fillEvent fill.Event) error { return err } prevSnap := complianceManager.GetLatestSnapshot() - fo := fillEvent.GetOrder() - if fo != nil { + if fo := fillEvent.GetOrder(); fo != nil { price := decimal.NewFromFloat(fo.Price) amount := decimal.NewFromFloat(fo.Amount) fee := decimal.NewFromFloat(fo.Fee) diff --git a/backtester/eventhandlers/statistics/statistics_test.go b/backtester/eventhandlers/statistics/statistics_test.go index 5cf59157..ac799d4b 100644 --- a/backtester/eventhandlers/statistics/statistics_test.go +++ b/backtester/eventhandlers/statistics/statistics_test.go @@ -376,8 +376,7 @@ func TestAddComplianceSnapshotForTime(t *testing.T) { func TestSerialise(t *testing.T) { t.Parallel() s := Statistic{} - _, err := s.Serialise() - if err != nil { + if _, err := s.Serialise(); err != nil { t.Error(err) } } diff --git a/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go b/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go index 7771b94e..cb06e04f 100644 --- a/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go +++ b/backtester/eventhandlers/strategies/dollarcostaverage/dollarcostaverage_test.go @@ -21,8 +21,7 @@ import ( func TestName(t *testing.T) { d := Strategy{} - n := d.Name() - if n != Name { + if n := d.Name(); n != Name { t.Errorf("expected %v", Name) } } diff --git a/backtester/eventhandlers/strategies/rsi/rsi.go b/backtester/eventhandlers/strategies/rsi/rsi.go index 2478a66b..07f9f209 100644 --- a/backtester/eventhandlers/strategies/rsi/rsi.go +++ b/backtester/eventhandlers/strategies/rsi/rsi.go @@ -55,9 +55,8 @@ func (s *Strategy) OnSignal(d data.Handler, _ funding.IFundTransferer) (signal.E return nil, err } es.SetPrice(d.Latest().ClosePrice()) - offset := d.Offset() - if offset <= int(s.rsiPeriod.IntPart()) { + if offset := d.Offset(); offset <= int(s.rsiPeriod.IntPart()) { es.AppendReason("Not enough data for signal generation") es.SetDirection(common.DoNothing) return &es, nil diff --git a/backtester/eventhandlers/strategies/rsi/rsi_test.go b/backtester/eventhandlers/strategies/rsi/rsi_test.go index dd5509e2..16e0f6a6 100644 --- a/backtester/eventhandlers/strategies/rsi/rsi_test.go +++ b/backtester/eventhandlers/strategies/rsi/rsi_test.go @@ -22,8 +22,7 @@ import ( func TestName(t *testing.T) { t.Parallel() d := Strategy{} - n := d.Name() - if n != Name { + if n := d.Name(); n != Name { t.Errorf("expected %v", Name) } } diff --git a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go index 99ee0234..61740d9e 100644 --- a/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go +++ b/backtester/eventhandlers/strategies/top2bottom2/top2bottom2_test.go @@ -23,8 +23,7 @@ import ( func TestName(t *testing.T) { t.Parallel() d := Strategy{} - n := d.Name() - if n != Name { + if n := d.Name(); n != Name { t.Errorf("expected %v", Name) } } @@ -94,8 +93,7 @@ func TestSetCustomSettings(t *testing.T) { func TestOnSignal(t *testing.T) { t.Parallel() s := Strategy{} - _, err := s.OnSignal(nil, nil) - if !errors.Is(err, errStrategyOnlySupportsSimultaneousProcessing) { + if _, err := s.OnSignal(nil, nil); !errors.Is(err, errStrategyOnlySupportsSimultaneousProcessing) { t.Errorf("received: %v, expected: %v", err, errStrategyOnlySupportsSimultaneousProcessing) } } diff --git a/backtester/eventtypes/event/event_test.go b/backtester/eventtypes/event/event_test.go index 8f9b184e..70fdf984 100644 --- a/backtester/eventtypes/event/event_test.go +++ b/backtester/eventtypes/event/event_test.go @@ -25,8 +25,7 @@ func TestEvent_GetAssetType(t *testing.T) { e := &Base{ AssetType: asset.Spot, } - y := e.GetAssetType() - if y != asset.Spot { + if y := e.GetAssetType(); y != asset.Spot { t.Error("expected spot") } } @@ -36,8 +35,7 @@ func TestEvent_GetExchange(t *testing.T) { e := &Base{ Exchange: "test", } - y := e.GetExchange() - if y != "test" { + if y := e.GetExchange(); y != "test" { t.Error("expected test") } } @@ -47,8 +45,7 @@ func TestEvent_GetInterval(t *testing.T) { e := &Base{ Interval: gctkline.OneMin, } - y := e.GetInterval() - if y != gctkline.OneMin { + if y := e.GetInterval(); y != gctkline.OneMin { t.Error("expected one minute") } } diff --git a/cmd/apichecker/apicheck.go b/cmd/apichecker/apicheck.go index 2620f67e..9ba5aa8d 100644 --- a/cmd/apichecker/apicheck.go +++ b/cmd/apichecker/apicheck.go @@ -565,7 +565,10 @@ func addExch(exchName, checkType string, data interface{}, isUpdate bool) error func fillData(exchName, checkType string, data interface{}) (ExchangeInfo, error) { switch checkType { case github: - tempData := data.(GithubData) + tempData, ok := data.(GithubData) + if !ok { + return ExchangeInfo{}, errors.New("unable to type assert GithubData") + } tempSha, err := getSha(path) if err != nil { return ExchangeInfo{}, err @@ -579,7 +582,10 @@ func fillData(exchName, checkType string, data interface{}) (ExchangeInfo, error }, }, nil case htmlScrape: - tempData := data.(HTMLScrapingData) + tempData, ok := data.(HTMLScrapingData) + if !ok { + return ExchangeInfo{}, errors.New("unable to type assert HTMLScrapingData") + } checkStr, err := checkChangeLog(&tempData) if err != nil { return ExchangeInfo{}, err diff --git a/cmd/apichecker/apicheck_test.go b/cmd/apichecker/apicheck_test.go index f0302a00..5fb20214 100644 --- a/cmd/apichecker/apicheck_test.go +++ b/cmd/apichecker/apicheck_test.go @@ -142,8 +142,7 @@ func TestCheckChangeLog(t *testing.T) { DateFormat: "2006/01/02", RegExp: `^20(\d){2}/(\d){2}/(\d){2}$`, Path: "https://docs.gemini.com/rest-api/#revision-history"} - _, err := checkChangeLog(&data) - if err != nil { + if _, err := checkChangeLog(&data); err != nil { t.Error(err) } } @@ -236,8 +235,7 @@ func TestHTMLScrapeBitmex(t *testing.T) { DateFormat: "Jan-2-2006", RegExp: `([A-Z]{1}[a-z]{2}-\d{1,2}-2\d{3})`, Path: "https://www.bitmex.com/static/md/en-US/apiChangelog"} - _, err := htmlScrapeBitmex(&data) - if err != nil { + if _, err := htmlScrapeBitmex(&data); err != nil { t.Error(err) } } @@ -246,8 +244,7 @@ func TestHTMLScrapeHitBTC(t *testing.T) { t.Parallel() data := HTMLScrapingData{RegExp: `newest version \d{1}.\d{1}`, Path: "https://api.hitbtc.com/"} - _, err := htmlScrapeHitBTC(&data) - if err != nil { + if _, err := htmlScrapeHitBTC(&data); err != nil { t.Error(err) } } @@ -273,8 +270,7 @@ func TestHTMLScrapeBTSE(t *testing.T) { t.Parallel() data := HTMLScrapingData{RegExp: `^version: \d{1}.\d{1}.\d{1}`, Path: "https://api.btcmarkets.net/openapi/info/index.yaml"} - _, err := htmlScrapeBTSE(&data) - if err != nil { + if _, err := htmlScrapeBTSE(&data); err != nil { t.Error(err) } } @@ -283,8 +279,7 @@ func TestHTMLScrapeBTCMarkets(t *testing.T) { t.Parallel() data := HTMLScrapingData{RegExp: `^version: \d{1}.\d{1}.\d{1}`, Path: "https://api.btcmarkets.net/openapi/info/index.yaml"} - _, err := htmlScrapeBTCMarkets(&data) - if err != nil { + if _, err := htmlScrapeBTCMarkets(&data); err != nil { t.Error(err) } } @@ -296,8 +291,7 @@ func TestHTMLScrapeBitflyer(t *testing.T) { TextTokenData: "code", RegExp: `^https://api.bitflyer.com/v\d{1}/$`, Path: "https://lightning.bitflyer.com/docs?lang=en"} - _, err := htmlScrapeBitflyer(&data) - if err != nil { + if _, err := htmlScrapeBitflyer(&data); err != nil { t.Error(err) } } @@ -306,8 +300,7 @@ func TestHTMLScrapeANX(t *testing.T) { t.Parallel() data := HTMLScrapingData{RegExp: `ANX Exchange API v\d{1}`, Path: "https://anxv3.docs.apiary.io/#reference/quickstart-catalog"} - _, err := htmlScrapeANX(&data) - if err != nil { + if _, err := htmlScrapeANX(&data); err != nil { t.Error(err) } } @@ -322,8 +315,7 @@ func TestHTMLPoloniex(t *testing.T) { DateFormat: "2006-01-02", RegExp: `(2\d{3}-\d{1,2}-\d{1,2})`, Path: "https://docs.poloniex.com/#changelog"} - _, err := htmlScrapePoloniex(&data) - if err != nil { + if _, err := htmlScrapePoloniex(&data); err != nil { t.Error(err) } } @@ -338,8 +330,7 @@ func TestHTMLItBit(t *testing.T) { DateFormat: "2006-01-02", RegExp: `^https://api.itbit.com/v\d{1}/$`, Path: "https://api.itbit.com/docs"} - _, err := htmlScrapeItBit(&data) - if err != nil { + if _, err := htmlScrapeItBit(&data); err != nil { t.Error(err) } } @@ -348,8 +339,7 @@ func TestHTMLScrapeExmo(t *testing.T) { t.Parallel() data := HTMLScrapingData{RegExp: `Last updated on [\s\S]*, 20\d{2}`, Path: "https://exmo.com/en/api/"} - _, err := htmlScrapeExmo(&data) - if err != nil { + if _, err := htmlScrapeExmo(&data); err != nil { t.Error(err) } } @@ -358,8 +348,7 @@ func TestHTMLBitstamp(t *testing.T) { t.Parallel() data := HTMLScrapingData{RegExp: `refer to the v\d{1} API for future references.`, Path: "https://www.bitstamp.net/api/"} - _, err := htmlScrapeBitstamp(&data) - if err != nil { + if _, err := htmlScrapeBitstamp(&data); err != nil { t.Error(err) } } @@ -371,8 +360,7 @@ func TestHTMLKraken(t *testing.T) { TextTokenData: "p", RegExp: `URL: https://api.kraken.com/\d{1}/private/Balance`, Path: "https://www.kraken.com/features/api"} - _, err := htmlScrapeKraken(&data) - if err != nil { + if _, err := htmlScrapeKraken(&data); err != nil { t.Error(err) } } @@ -386,8 +374,7 @@ func TestHTMLAlphaPoint(t *testing.T) { TextTokenData: "h3", RegExp: `revised-calls-\d{1}-\d{1}-\d{1}-gt-\d{1}-\d{1}-\d{1}`, Path: "https://alphapoint.github.io/slate/#introduction"} - _, err := htmlScrapeAlphaPoint(&data) - if err != nil { + if _, err := htmlScrapeAlphaPoint(&data); err != nil { t.Error(err) } } @@ -397,8 +384,7 @@ func TestHTMLYobit(t *testing.T) { data := HTMLScrapingData{TokenData: "h2", Key: "id", Path: "https://www.yobit.net/en/api/"} - _, err := htmlScrapeYobit(&data) - if err != nil { + if _, err := htmlScrapeYobit(&data); err != nil { t.Error(err) } } @@ -408,8 +394,7 @@ func TestHTMLScrapeLocalBitcoins(t *testing.T) { data := HTMLScrapingData{TokenData: "div", RegExp: `col-md-12([\s\S]*?)clearfix`, Path: "https://localbitcoins.com/api-docs/"} - _, err := htmlScrapeLocalBitcoins(&data) - if err != nil { + if _, err := htmlScrapeLocalBitcoins(&data); err != nil { t.Error(err) } } @@ -422,8 +407,7 @@ func TestHTMLScrapeOk(t *testing.T) { TokenDataEnd: "./#change-", RegExp: `./#change-\d{8}`, Path: "https://www.okex.com/docs/en/"} - _, err := htmlScrapeOk(&data) - if err != nil { + if _, err := htmlScrapeOk(&data); err != nil { t.Error(err) } } @@ -547,8 +531,7 @@ func TestTrelloGetLists(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := trelloGetLists() - if err != nil { + if _, err := trelloGetLists(); err != nil { t.Error(err) } } @@ -557,8 +540,7 @@ func TestGetAllCards(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := trelloGetAllCards() - if err != nil { + if _, err := trelloGetAllCards(); err != nil { t.Error(err) } } @@ -567,8 +549,7 @@ func TestGetAllChecklists(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - _, err := trelloGetAllChecklists() - if err != nil { + if _, err := trelloGetAllChecklists(); err != nil { t.Error(err) } } @@ -580,8 +561,7 @@ func TestTrelloGetAllBoards(t *testing.T) { if trelloBoardID != "" || testBoardName != "" { t.Skip() } - _, err := trelloGetBoardID() - if err != nil { + if _, err := trelloGetBoardID(); err != nil { t.Error(err) } } @@ -590,8 +570,7 @@ func TestCreateNewList(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - err := trelloCreateNewList() - if err != nil { + if err := trelloCreateNewList(); err != nil { t.Error(err) } } @@ -600,8 +579,7 @@ func TestTrelloCreateNewCard(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - err := trelloCreateNewCard() - if err != nil { + if err := trelloCreateNewCard(); err != nil { t.Error(err) } } @@ -610,8 +588,7 @@ func TestCreateNewChecklist(t *testing.T) { if !areTestAPIKeysSet() { t.Skip() } - err := trelloCreateNewChecklist() - if err != nil { + if err := trelloCreateNewChecklist(); err != nil { t.Error(err) } } @@ -619,8 +596,7 @@ func TestCreateNewChecklist(t *testing.T) { func TestWriteAuthVars(t *testing.T) { if canTestMainFile { trelloCardID = "jdsfl" - err := writeAuthVars(testMode) - if err != nil { + if err := writeAuthVars(testMode); err != nil { t.Error(err) } } diff --git a/cmd/dbseed/dbseed_test.go b/cmd/dbseed/dbseed_test.go index 178080dd..a0232820 100644 --- a/cmd/dbseed/dbseed_test.go +++ b/cmd/dbseed/dbseed_test.go @@ -38,8 +38,7 @@ func TestLoad(t *testing.T) { fs := &flag.FlagSet{} fs.String("config", testConfig, "") newCtx := cli.NewContext(testApp, fs, &cli.Context{}) - err := load(newCtx) - if err != nil { + if err := load(newCtx); err != nil { t.Fatal(err) } } diff --git a/cmd/exchange_wrapper_issues/main.go b/cmd/exchange_wrapper_issues/main.go index 8550fb99..a1d3236a 100644 --- a/cmd/exchange_wrapper_issues/main.go +++ b/cmd/exchange_wrapper_issues/main.go @@ -951,12 +951,10 @@ func outputToConsole(exchangeResponses []ExchangeResponses) { // disruptFormatting adds in an unused delimiter and strange casing features to // ensure format currency pair is used throughout the code base. func disruptFormatting(p currency.Pair) (currency.Pair, error) { - base := p.Base.String() - if base == "" { + if p.Base.IsEmpty() { return currency.Pair{}, errors.New("cannot disrupt formatting as base is not populated") } - quote := p.Quote.String() - if quote == "" { + if p.Quote.IsEmpty() { return currency.Pair{}, errors.New("cannot disrupt formatting as quote is not populated") } diff --git a/cmd/gctcli/helpers.go b/cmd/gctcli/helpers.go index 57c277b7..92bc2d01 100644 --- a/cmd/gctcli/helpers.go +++ b/cmd/gctcli/helpers.go @@ -24,8 +24,7 @@ func clearScreen() error { } func closeConn(conn *grpc.ClientConn, cancel context.CancelFunc) { - err := conn.Close() - if err != nil { + if err := conn.Close(); err != nil { fmt.Println(err) } if cancel != nil { diff --git a/common/cache/cache_test.go b/common/cache/cache_test.go index 6ac4cd9a..766d77e5 100644 --- a/common/cache/cache_test.go +++ b/common/cache/cache_test.go @@ -130,16 +130,14 @@ func TestRemove(t *testing.T) { func TestGetNewest(t *testing.T) { lruCache := New(2) - k, _ := lruCache.getNewest() - if k != nil { + if k, _ := lruCache.getNewest(); k != nil { t.Fatal("expected GetNewest() on empty cache to return nil") } } func TestGetOldest(t *testing.T) { lruCache := New(2) - k, _ := lruCache.getOldest() - if k != nil { + if k, _ := lruCache.getOldest(); k != nil { t.Fatal("expected GetOldest() on empty cache to return nil") } } diff --git a/common/cache/lru.go b/common/cache/lru.go index 995d05b4..06cdd8e9 100644 --- a/common/cache/lru.go +++ b/common/cache/lru.go @@ -47,8 +47,7 @@ func (l *LRU) Get(key interface{}) interface{} { // GetOldest returns the oldest entry func (l *LRU) getOldest() (key, value interface{}) { - x := l.l.Back() - if x != nil { + if x := l.l.Back(); x != nil { return x.Value.(*item).key, x.Value.(*item).value } return @@ -56,8 +55,7 @@ func (l *LRU) getOldest() (key, value interface{}) { // GetNewest returns the newest entry func (l *LRU) getNewest() (key, value interface{}) { - x := l.l.Front() - if x != nil { + if x := l.l.Front(); x != nil { return x.Value.(*item).key, x.Value.(*item).value } return @@ -93,8 +91,7 @@ func (l *LRU) Len() uint64 { // removeOldest removes the oldest item from the cache. func (l *LRU) removeOldestEntry() { - i := l.l.Back() - if i != nil { + if i := l.l.Back(); i != nil { l.removeElement(i) } } diff --git a/common/common_test.go b/common/common_test.go index a4af1d4d..200c107f 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -248,15 +248,13 @@ func TestStringDataContains(t *testing.T) { originalHaystack := []string{"hello", "world", "USDT", "Contains", "string"} originalNeedle := "USD" anotherNeedle := "thing" - expectedOutput := true - expectedOutputTwo := false actualResult := StringDataContains(originalHaystack, originalNeedle) - if actualResult != expectedOutput { + if expectedOutput := true; actualResult != expectedOutput { t.Errorf("Expected '%v'. Actual '%v'", expectedOutput, actualResult) } actualResult = StringDataContains(originalHaystack, anotherNeedle) - if actualResult != expectedOutputTwo { + if expectedOutput := false; actualResult != expectedOutput { t.Errorf("Expected '%v'. Actual '%v'", expectedOutput, actualResult) } @@ -267,15 +265,13 @@ func TestStringDataCompare(t *testing.T) { originalHaystack := []string{"hello", "WoRld", "USDT", "Contains", "string"} originalNeedle := "WoRld" anotherNeedle := "USD" - expectedOutput := true - expectedOutputTwo := false actualResult := StringDataCompare(originalHaystack, originalNeedle) - if actualResult != expectedOutput { + if expectedOutput := true; actualResult != expectedOutput { t.Errorf("Expected '%v'. Actual '%v'", expectedOutput, actualResult) } actualResult = StringDataCompare(originalHaystack, anotherNeedle) - if actualResult != expectedOutputTwo { + if expectedOutput := false; actualResult != expectedOutput { t.Errorf("Expected '%v'. Actual '%v'", expectedOutput, actualResult) } @@ -286,16 +282,14 @@ func TestStringDataCompareUpper(t *testing.T) { originalHaystack := []string{"hello", "WoRld", "USDT", "Contains", "string"} originalNeedle := "WoRld" anotherNeedle := "WoRldD" - expectedOutput := true - expectedOutputTwo := false actualResult := StringDataCompareInsensitive(originalHaystack, originalNeedle) - if actualResult != expectedOutput { + if expectedOutput := true; actualResult != expectedOutput { t.Errorf("Expected '%v'. Actual '%v'", expectedOutput, actualResult) } actualResult = StringDataCompareInsensitive(originalHaystack, anotherNeedle) - if actualResult != expectedOutputTwo { + if expectedOutput := false; actualResult != expectedOutput { t.Errorf("Expected '%v'. Actual '%v'", expectedOutput, actualResult) } @@ -306,15 +300,13 @@ func TestStringDataContainsUpper(t *testing.T) { originalHaystack := []string{"bLa", "BrO", "sUp"} originalNeedle := "Bla" anotherNeedle := "ning" - expectedOutput := true - expectedOutputTwo := false actualResult := StringDataContainsInsensitive(originalHaystack, originalNeedle) - if actualResult != expectedOutput { + if expectedOutput := true; actualResult != expectedOutput { t.Errorf("Expected '%v'. Actual '%v'", expectedOutput, actualResult) } actualResult = StringDataContainsInsensitive(originalHaystack, anotherNeedle) - if actualResult != expectedOutputTwo { + if expectedOutput := false; actualResult != expectedOutput { t.Errorf("Expected '%v'. Actual '%v'", expectedOutput, actualResult) } @@ -410,8 +402,7 @@ func TestGetURIPath(t *testing.T) { func TestGetExecutablePath(t *testing.T) { t.Parallel() - _, err := GetExecutablePath() - if err != nil { + if _, err := GetExecutablePath(); err != nil { t.Errorf("Common GetExecutablePath. Error: %s", err) } } diff --git a/common/convert/convert.go b/common/convert/convert.go index 760f522d..78802e3e 100644 --- a/common/convert/convert.go +++ b/common/convert/convert.go @@ -52,7 +52,7 @@ func TimeFromUnixTimestampFloat(raw interface{}) (time.Time, error) { if !ok { return time.Time{}, fmt.Errorf("unable to parse, value not float64: %T", raw) } - return time.Unix(0, int64(ts)*int64(time.Millisecond)), nil + return time.UnixMilli(int64(ts)), nil } // TimeFromUnixTimestampDecimal converts a unix timestamp in decimal form to @@ -76,23 +76,8 @@ func UnixTimestampStrToTime(timeStr string) (time.Time, error) { return time.Unix(i, 0), nil } -// UnixMillis converts a UnixNano timestamp to milliseconds -func UnixMillis(t time.Time) int64 { - return t.UnixNano() / int64(time.Millisecond) -} - -// RecvWindow converts a supplied time.Duration to milliseconds -func RecvWindow(d time.Duration) int64 { - return int64(d) / int64(time.Millisecond) -} - // BoolPtr takes in boolen condition and returns pointer version of it func BoolPtr(condition bool) *bool { b := condition return &b } - -// UnixMillisToNano converts Unix milli time to UnixNano -func UnixMillisToNano(milli int64) int64 { - return milli * int64(time.Millisecond) -} diff --git a/common/convert/convert_test.go b/common/convert/convert_test.go index 28312d66..299911ba 100644 --- a/common/convert/convert_test.go +++ b/common/convert/convert_test.go @@ -32,10 +32,8 @@ func TestFloatFromString(t *testing.T) { func TestIntFromString(t *testing.T) { t.Parallel() testString := "1337" - expectedOutput := 1337 - actualOutput, err := IntFromString(testString) - if actualOutput != expectedOutput || err != nil { + if expectedOutput := 1337; actualOutput != expectedOutput || err != nil { t.Errorf("Common IntFromString. Expected '%v'. Actual '%v'. Error: %s", expectedOutput, actualOutput, err) } @@ -136,33 +134,9 @@ func TestUnixTimestampStrToTime(t *testing.T) { t.Errorf( "Expected '%s'. Actual '%s'.", expectedOutput, actualResult) } - actualResult, err = UnixTimestampStrToTime(incorrectTime) + _, err = UnixTimestampStrToTime(incorrectTime) if err == nil { - t.Error("Common UnixTimestampStrToTime error") - } -} - -func TestUnixMillis(t *testing.T) { - t.Parallel() - testTime := time.Date(2014, time.October, 28, 0, 32, 0, 0, time.UTC) - expectedOutput := int64(1414456320000) - - actualOutput := UnixMillis(testTime) - if actualOutput != expectedOutput { - t.Errorf("Common UnixMillis. Expected '%d'. Actual '%d'.", - expectedOutput, actualOutput) - } -} - -func TestRecvWindow(t *testing.T) { - t.Parallel() - testTime := time.Duration(24760000) - expectedOutput := int64(24) - - actualOutput := RecvWindow(testTime) - if actualOutput != expectedOutput { - t.Errorf("Common RecvWindow. Expected '%d'. Actual '%d'", - expectedOutput, actualOutput) + t.Error("should throw an error") } } @@ -176,10 +150,3 @@ func TestBoolPtr(t *testing.T) { t.Fatal("false expected received true") } } - -func TestUnixMillisToNano(t *testing.T) { - v := UnixMillisToNano(1588653603424) - if v != 1588653603424000000 { - t.Fatalf("unexpected result received %v", v) - } -} diff --git a/common/crypto/crypto_test.go b/common/crypto/crypto_test.go index a5693e27..3931a2e6 100644 --- a/common/crypto/crypto_test.go +++ b/common/crypto/crypto_test.go @@ -35,9 +35,8 @@ func TestBase64Decode(t *testing.T) { func TestBase64Encode(t *testing.T) { t.Parallel() originalInput := []byte("hello") - expectedOutput := "aGVsbG8=" actualResult := Base64Encode(originalInput) - if actualResult != expectedOutput { + if expectedOutput := "aGVsbG8="; actualResult != expectedOutput { t.Errorf("Expected '%s'. Actual '%s'", expectedOutput, actualResult) } diff --git a/common/file/archive/zip_test.go b/common/file/archive/zip_test.go index ca521ac6..050b67d7 100644 --- a/common/file/archive/zip_test.go +++ b/common/file/archive/zip_test.go @@ -80,8 +80,7 @@ func TestZip(t *testing.T) { if filepath.Base(o[0]) != "binance.json" || filepath.Base(o[4]) != "localbitcoins.json" { t.Fatal("unexpected archive result received") } - expected := 7 - if len(o) != expected { + if expected := 7; len(o) != expected { t.Fatalf("expected %v files to be extracted received: %v ", expected, len(o)) } diff --git a/common/file/file.go b/common/file/file.go index 982a498e..5855dee2 100644 --- a/common/file/file.go +++ b/common/file/file.go @@ -116,8 +116,7 @@ func WriteAsCSV(filename string, records [][]string) error { w.Flush() - err := w.Error() - if err != nil { + if err := w.Error(); err != nil { return err } return Write(filename, buf.Bytes()) diff --git a/common/math/math_test.go b/common/math/math_test.go index 43e1abb9..3a2fc1f0 100644 --- a/common/math/math_test.go +++ b/common/math/math_test.go @@ -12,9 +12,7 @@ func TestCalculateFee(t *testing.T) { t.Parallel() originalInput := float64(1) fee := float64(1) - expectedOutput := 0.01 - actualResult := CalculateFee(originalInput, fee) - if expectedOutput != actualResult { + if expectedOutput, actualResult := 0.01, CalculateFee(originalInput, fee); expectedOutput != actualResult { t.Errorf( "Expected '%f'. Actual '%f'.", expectedOutput, actualResult) } @@ -24,9 +22,7 @@ func TestCalculateAmountWithFee(t *testing.T) { t.Parallel() originalInput := float64(1) fee := float64(1) - expectedOutput := 1.01 - actualResult := CalculateAmountWithFee(originalInput, fee) - if expectedOutput != actualResult { + if actualResult, expectedOutput := CalculateAmountWithFee(originalInput, fee), 1.01; expectedOutput != actualResult { t.Errorf( "Expected '%f'. Actual '%f'.", expectedOutput, actualResult) } @@ -62,9 +58,8 @@ func TestCalculateNetProfit(t *testing.T) { priceThen := float64(1) priceNow := float64(10) costs := float64(1) - expectedOutput := float64(44) actualResult := CalculateNetProfit(amount, priceThen, priceNow, costs) - if expectedOutput != actualResult { + if expectedOutput := float64(44); expectedOutput != actualResult { t.Errorf( "Expected '%f'. Actual '%f'.", expectedOutput, actualResult) } @@ -164,8 +159,7 @@ func TestSortinoRatio(t *testing.T) { if err != nil { t.Error(err) } - rr := math.Round(r*10) / 10 - if rr != 0.2 { + if rr := math.Round(r*10) / 10; rr != 0.2 { t.Errorf("expected 0.2, received %v", rr) } } @@ -538,8 +532,7 @@ func TestDecimalSortinoRatio(t *testing.T) { if err != nil && !errors.Is(err, ErrInexactConversion) { t.Error(err) } - rr := r.Round(1) - if !rr.Equal(decimal.NewFromFloat(0.2)) { + if rr := r.Round(1); !rr.Equal(decimal.NewFromFloat(0.2)) { t.Errorf("expected 0.2, received %v", rr) } } diff --git a/common/timeperiods/timeperiods.go b/common/timeperiods/timeperiods.go index 2879fb24..6e118ff1 100644 --- a/common/timeperiods/timeperiods.go +++ b/common/timeperiods/timeperiods.go @@ -100,7 +100,6 @@ func (t *TimePeriodCalculator) calculateRanges() { } tr.HasDataInRange = t.TimePeriods[len(t.TimePeriods)-1].dataInRange t.TimeRanges = append(t.TimeRanges, tr) - tr = TimeRange{} } } diff --git a/communications/communications_test.go b/communications/communications_test.go index 053239c4..9d2dd191 100644 --- a/communications/communications_test.go +++ b/communications/communications_test.go @@ -8,8 +8,7 @@ import ( func TestNewComm(t *testing.T) { var cfg base.CommunicationsConfig - _, err := NewComm(&cfg) - if err == nil { + if _, err := NewComm(&cfg); err == nil { t.Error("NewComm should have failed on no enabled communication mediums") } diff --git a/communications/slack/slack.go b/communications/slack/slack.go index 91382cd8..22677a65 100644 --- a/communications/slack/slack.go +++ b/communications/slack/slack.go @@ -67,8 +67,7 @@ func (s *Slack) Setup(cfg *base.CommunicationsConfig) { // Connect connects to the service func (s *Slack) Connect() error { - err := s.NewConnection() - if err != nil { + if err := s.NewConnection(); err != nil { return err } diff --git a/communications/slack/slack_test.go b/communications/slack/slack_test.go index 352cc89c..b9f33a48 100644 --- a/communications/slack/slack_test.go +++ b/communications/slack/slack_test.go @@ -25,8 +25,7 @@ func TestSetup(t *testing.T) { func TestConnect(t *testing.T) { t.Parallel() var s Slack - err := s.Connect() - if err == nil { + if err := s.Connect(); err == nil { t.Error("slack Connect() error cannot be nil") } } @@ -43,8 +42,7 @@ func TestPushEvent(t *testing.T) { func TestBuildURL(t *testing.T) { t.Parallel() var s Slack - v := s.BuildURL("lol123") - if v != "https://slack.com/api/rtm.start?token=lol123" { + if v := s.BuildURL("lol123"); v != "https://slack.com/api/rtm.start?token=lol123" { t.Error("slack BuildURL() error") } } @@ -183,8 +181,7 @@ func TestGetUsersInGroup(t *testing.T) { func TestNewConnection(t *testing.T) { t.Parallel() var s Slack - err := s.NewConnection() - if err == nil { + if err := s.NewConnection(); err == nil { t.Error("slack NewConnection() error") } } @@ -192,8 +189,7 @@ func TestNewConnection(t *testing.T) { func TestWebsocketConnect(t *testing.T) { t.Parallel() var s Slack - err := s.WebsocketConnect() - if err == nil { + if err := s.WebsocketConnect(); err == nil { t.Error("slack WebsocketConnect() error") } } diff --git a/communications/smsglobal/smsglobal_test.go b/communications/smsglobal/smsglobal_test.go index 26743c29..26fe85a7 100644 --- a/communications/smsglobal/smsglobal_test.go +++ b/communications/smsglobal/smsglobal_test.go @@ -18,9 +18,8 @@ func TestSetup(t *testing.T) { func TestConnect(t *testing.T) { t.Parallel() var s SMSGlobal - err := s.Connect() - if err != nil { - t.Error("SMSGlobal Connect() error", err) + if err := s.Connect(); err != nil { + t.Error(err) } } @@ -43,9 +42,8 @@ func TestGetEnabledContacts(t *testing.T) { }, }, } - v := s.GetEnabledContacts() - if v != 1 { - t.Error("SMSGlobal GetEnabledContacts() error") + if v := s.GetEnabledContacts(); v != 1 { + t.Error("expected one enabled contact") } } diff --git a/communications/smtpservice/smtpservice_test.go b/communications/smtpservice/smtpservice_test.go index af05ffdf..148685d6 100644 --- a/communications/smtpservice/smtpservice_test.go +++ b/communications/smtpservice/smtpservice_test.go @@ -17,8 +17,7 @@ func TestSetup(t *testing.T) { } func TestConnect(t *testing.T) { - err := s.Connect() - if err != nil { + if err := s.Connect(); err != nil { t.Error("smtpservice Connect() error", err) } } diff --git a/communications/telegram/telegram_test.go b/communications/telegram/telegram_test.go index 5a0bf396..400f0152 100644 --- a/communications/telegram/telegram_test.go +++ b/communications/telegram/telegram_test.go @@ -36,9 +36,8 @@ func TestSetup(t *testing.T) { func TestConnect(t *testing.T) { t.Parallel() var T Telegram - err := T.Connect() - if err == nil { - t.Error("telegram Connect() error") + if err := T.Connect(); err == nil { + t.Error("expected error") } } @@ -91,8 +90,7 @@ func TestHandleMessages(t *testing.T) { func TestGetUpdates(t *testing.T) { t.Parallel() var T Telegram - _, err := T.GetUpdates() - if err != nil { + if _, err := T.GetUpdates(); err != nil { t.Error("telegram GetUpdates() error", err) } } @@ -100,10 +98,8 @@ func TestGetUpdates(t *testing.T) { func TestTestConnection(t *testing.T) { t.Parallel() var T Telegram - err := T.TestConnection() - if err.Error() != testErrNotFound { - t.Errorf("telegram TestConnection() error, expected 'Not found' got '%s'", - err) + if err := T.TestConnection(); err.Error() != testErrNotFound { + t.Errorf("received %s, expected: %s", err, testErrNotFound) } } diff --git a/config/config.go b/config/config.go index cf84952c..bea65115 100644 --- a/config/config.go +++ b/config/config.go @@ -1870,7 +1870,7 @@ func (c *Config) AssetTypeEnabled(a asset.Item, exch string) (bool, error) { err = cfg.CurrencyPairs.IsAssetEnabled(a) if err != nil { - return false, nil + return false, nil // nolint:nilerr // non-fatal error } return true, nil } diff --git a/config/config_encryption.go b/config/config_encryption.go index 4ab2a5fa..821584aa 100644 --- a/config/config_encryption.go +++ b/config/config_encryption.go @@ -34,8 +34,7 @@ func promptForConfigEncryption() (bool, error) { log.Println("Would you like to encrypt your config file (y/n)?") input := "" - _, err := fmt.Scanln(&input) - if err != nil { + if _, err := fmt.Scanln(&input); err != nil { return false, err } @@ -194,8 +193,7 @@ func ConfirmECS(file []byte) bool { // or errors, if the prefix wasn't found func skipECS(file io.Reader) error { buf := make([]byte, len(EncryptConfirmString)) - _, err := io.ReadFull(file, buf) - if err != nil { + if _, err := io.ReadFull(file, buf); err != nil { return err } if string(buf) != EncryptConfirmString { diff --git a/config/config_encryption_test.go b/config/config_encryption_test.go index bd5f18ec..58e022b6 100644 --- a/config/config_encryption_test.go +++ b/config/config_encryption_test.go @@ -130,8 +130,7 @@ func TestRemoveECS(t *testing.T) { func TestMakeNewSessionDK(t *testing.T) { t.Parallel() - _, _, err := makeNewSessionDK(nil) - if err == nil { + if _, _, err := makeNewSessionDK(nil); err == nil { t.Fatal("makeNewSessionDK passed with nil key") } } @@ -238,6 +237,7 @@ func TestSaveAndReopenEncryptedConfig(t *testing.T) { // setAnswersFile sets the given file as the current stdin // returns the close function to defer for reverting the stdin func setAnswersFile(t *testing.T, answerFile string) func() { + t.Helper() oldIn := os.Stdin inputFile, err := os.Open(answerFile) @@ -359,6 +359,7 @@ func TestSaveConfigToFileWithErrorInPasswordPrompt(t *testing.T) { } func withInteractiveResponse(t *testing.T, response string, body func() error) error { + t.Helper() // Answers to the prompt responseFile, err := ioutil.TempFile("", "*.in") if err != nil { diff --git a/config/config_test.go b/config/config_test.go index 26d15bd3..0d864e17 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -35,8 +35,7 @@ func TestGetNonExistentDefaultFilePathDoesNotCreateDefaultDir(t *testing.T) { if file.Exists(dir) { t.Skip("The default directory already exists before running the test") } - _, _, err := GetFilePath("") - if err != nil { + if _, _, err := GetFilePath(""); err != nil { t.Fatal(err) } if file.Exists(dir) { @@ -921,7 +920,7 @@ func TestSupportsPair(t *testing.T) { }, }, } - assetType := asset.Spot + assetType := asset.Spot // nolint // ifshort false positive if cfg.SupportsPair("asdf", currency.NewPair(currency.BTC, currency.USD), assetType) { t.Error( @@ -1284,7 +1283,7 @@ func TestGetForexProviders(t *testing.T) { func TestGetPrimaryForexProvider(t *testing.T) { t.Parallel() - fxr := "Fixer" + fxr := "Fixer" // nolint:ifshort,nolintlint // false positive and triggers only on Windows cfg := &Config{ Currency: CurrencyConfig{ ForexProviders: []currency.FXSettings{ @@ -1937,8 +1936,7 @@ func TestCheckConfig(t *testing.T) { }, }, } - err := cfg.CheckConfig() - if err != nil { + if err := cfg.CheckConfig(); err != nil { t.Fatal(err) } } @@ -2209,6 +2207,7 @@ func TestRemoveExchange(t *testing.T) { } func TestGetDataPath(t *testing.T) { + t.Parallel() tests := []struct { name string dir string @@ -2238,6 +2237,7 @@ func TestGetDataPath(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() + t.Helper() c := &Config{ DataDirectory: tt.dir, } @@ -2278,6 +2278,7 @@ func TestMigrateConfig(t *testing.T) { { name: "source present, no target dir", setup: func(t *testing.T) { + t.Helper() test, err := os.Create("test.json") if err != nil { t.Fatal(err) @@ -2285,6 +2286,7 @@ func TestMigrateConfig(t *testing.T) { test.Close() }, cleanup: func(t *testing.T) { + t.Helper() os.Remove("test.json") }, args: args{ @@ -2297,6 +2299,7 @@ func TestMigrateConfig(t *testing.T) { { name: "source same as target", setup: func(t *testing.T) { + t.Helper() err := file.Write(filepath.Join(dir, File), nil) if err != nil { t.Fatal(err) @@ -2312,6 +2315,7 @@ func TestMigrateConfig(t *testing.T) { { name: "source and target present", setup: func(t *testing.T) { + t.Helper() err := file.Write(filepath.Join(dir, File), nil) if err != nil { t.Fatal(err) diff --git a/connchecker/connchecker.go b/connchecker/connchecker.go index d061f59e..611d9271 100644 --- a/connchecker/connchecker.go +++ b/connchecker/connchecker.go @@ -48,8 +48,7 @@ func New(dnsList, domainList []string, checkInterval time.Duration) (*Checker, e c.CheckInterval = checkInterval } - err := c.initialCheck() - if err != nil { + if err := c.initialCheck(); err != nil { return nil, err } diff --git a/currency/code_test.go b/currency/code_test.go index f0b5081b..779f69d3 100644 --- a/currency/code_test.go +++ b/currency/code_test.go @@ -51,8 +51,7 @@ func TestRoleMarshalJSON(t *testing.T) { t.Error("Role MarshalJSON() error", err) } - expected := `"fiatCurrency"` - if string(d) != expected { + if expected := `"fiatCurrency"`; string(d) != expected { t.Errorf("Role MarshalJSON() error expected %s but received %s", expected, string(d)) @@ -337,18 +336,14 @@ func TestBaseCode(t *testing.T) { } func TestCodeString(t *testing.T) { - expected := "TEST" - cc := NewCode("TEST") - if cc.String() != expected { + if cc, expected := NewCode("TEST"), "TEST"; cc.String() != expected { t.Errorf("Currency Code String() error expected %s but received %s", expected, cc) } } func TestCodeLower(t *testing.T) { - expected := "test" - cc := NewCode("TEST") - if cc.Lower().String() != expected { + if cc, expected := NewCode("TEST"), "test"; cc.Lower().String() != expected { t.Errorf("Currency Code Lower() error expected %s but received %s", expected, cc.Lower()) @@ -356,9 +351,7 @@ func TestCodeLower(t *testing.T) { } func TestCodeUpper(t *testing.T) { - expected := "TEST" - cc := NewCode("test") - if cc.Upper().String() != expected { + if cc, expected := NewCode("test"), "TEST"; cc.Upper().String() != expected { t.Errorf("Currency Code Upper() error expected %s but received %s", expected, cc.Upper()) diff --git a/currency/conversion_test.go b/currency/conversion_test.go index 6cf9a151..189b18f2 100644 --- a/currency/conversion_test.go +++ b/currency/conversion_test.go @@ -33,14 +33,13 @@ func TestNewConversionFromString(t *testing.T) { func TestNewConversionFromStrings(t *testing.T) { from := "AUD" to := "USD" - expected := "AUDUSD" conv, err := NewConversionFromStrings(from, to) if err != nil { t.Error(err) } - if conv.String() != expected { + if expected := "AUDUSD"; conv.String() != expected { t.Errorf("NewConversion() error expected %s but received %s", expected, conv) @@ -50,14 +49,13 @@ func TestNewConversionFromStrings(t *testing.T) { func TestNewConversion(t *testing.T) { from := NewCode("AUD") to := NewCode("USD") - expected := "AUDUSD" conv, err := NewConversion(from, to) if err != nil { t.Error(err) } - if conv.String() != expected { + if expected := "AUDUSD"; conv.String() != expected { t.Errorf("NewConversion() error expected %s but received %s", expected, conv) @@ -79,8 +77,7 @@ func TestConversionIsInvalid(t *testing.T) { } to = AUD - conv, err = NewConversion(from, to) - if err == nil { + if _, err = NewConversion(from, to); err == nil { t.Error("Expected error") } } @@ -100,8 +97,7 @@ func TestConversionIsFiatPair(t *testing.T) { } to = LTC - conv, err = NewConversion(from, to) - if err == nil { + if _, err = NewConversion(from, to); err == nil { t.Error("Expected error") } } @@ -159,17 +155,16 @@ func TestConversionsRatesSystem(t *testing.T) { p := SuperDuperConversionSystem.m[USD.Item][AUD.Item] // inverse * to a rate pi := SuperDuperConversionSystem.m[AUD.Item][USD.Item] - r := *p * 1000 + expectedRate := 1396.9317581 - if r != expectedRate { + if r := *p * 1000; r != expectedRate { t.Errorf("Convert() error expected %.13f but received %.13f", expectedRate, r) } - inverseR := *pi * expectedRate expectedInverseRate := float64(1000) - if inverseR != expectedInverseRate { + if inverseR := *pi * expectedRate; inverseR != expectedInverseRate { t.Errorf("Convert() error expected %.13f but received %.13f", expectedInverseRate, inverseR) diff --git a/currency/currency.go b/currency/currency.go index 3415bf42..24b59786 100644 --- a/currency/currency.go +++ b/currency/currency.go @@ -93,7 +93,7 @@ func CopyPairFormat(p Pair, pairs []Pair, exact bool) Pair { return pairs[x] } } - return Pair{Base: NewCode(""), Quote: NewCode("")} + return Pair{} } // FormatPairs formats a string array to a list of currency pairs with the diff --git a/currency/forexprovider/currencyconverterapi/currencyconverterapi.go b/currency/forexprovider/currencyconverterapi/currencyconverterapi.go index fc9fa5fa..f5340cb2 100644 --- a/currency/forexprovider/currencyconverterapi/currencyconverterapi.go +++ b/currency/forexprovider/currencyconverterapi/currencyconverterapi.go @@ -63,8 +63,7 @@ func (c *CurrencyConverter) GetRates(baseCurrency, symbols string) (map[string]f } currLen := len(completedStrings) - mod := currLen % 2 - if mod == 0 { + if mod := currLen % 2; mod == 0 { processBatch(currLen) return rates, nil } diff --git a/currency/forexprovider/currencyconverterapi/currencyconverterapi_test.go b/currency/forexprovider/currencyconverterapi/currencyconverterapi_test.go index b322ef07..56e42b19 100644 --- a/currency/forexprovider/currencyconverterapi/currencyconverterapi_test.go +++ b/currency/forexprovider/currencyconverterapi/currencyconverterapi_test.go @@ -74,8 +74,7 @@ func TestConvert(t *testing.T) { t.Skip() } - _, err := c.Convert("AUD", "USD") - if err != nil { + if _, err := c.Convert("AUD", "USD"); err != nil { t.Fatal(err) } } @@ -96,8 +95,7 @@ func TestGetCountries(t *testing.T) { t.Skip() } - _, err := c.GetCountries() - if err != nil { + if _, err := c.GetCountries(); err != nil { t.Fatal(err) } } diff --git a/currency/forexprovider/exchangeratesapi.io/exchangeratesapi_test.go b/currency/forexprovider/exchangeratesapi.io/exchangeratesapi_test.go index 93c5d0bb..81cfda2b 100644 --- a/currency/forexprovider/exchangeratesapi.io/exchangeratesapi_test.go +++ b/currency/forexprovider/exchangeratesapi.io/exchangeratesapi_test.go @@ -211,8 +211,7 @@ func TestGetRates(t *testing.T) { t.Skip("API key not set, skipping test") } - _, err := e.GetRates("EUR", "") - if err != nil { + if _, err := e.GetRates("EUR", ""); err != nil { t.Fatalf("failed to GetRates. Err: %s", err) } } diff --git a/currency/forexprovider/fixer.io/fixer_test.go b/currency/forexprovider/fixer.io/fixer_test.go index 45752552..ecfc366a 100644 --- a/currency/forexprovider/fixer.io/fixer_test.go +++ b/currency/forexprovider/fixer.io/fixer_test.go @@ -19,6 +19,7 @@ var f Fixer var isSetup bool func setup(t *testing.T) { + t.Helper() if !isSetup { err := f.Setup(base.Settings{}) if err != nil { diff --git a/currency/forexprovider/openexchangerates/openexchangerates_test.go b/currency/forexprovider/openexchangerates/openexchangerates_test.go index 962de96b..596b2262 100644 --- a/currency/forexprovider/openexchangerates/openexchangerates_test.go +++ b/currency/forexprovider/openexchangerates/openexchangerates_test.go @@ -88,8 +88,7 @@ func TestGetOHLC(t *testing.T) { func TestGetUsageStats(t *testing.T) { t.Parallel() - _, err := o.GetUsageStats(false) - if err == nil { + if _, err := o.GetUsageStats(false); err == nil { t.Error("GetUsageStats() Expected error") } } diff --git a/currency/manager_test.go b/currency/manager_test.go index 3e96a61f..00694d85 100644 --- a/currency/manager_test.go +++ b/currency/manager_test.go @@ -10,6 +10,7 @@ import ( var p PairsManager func initTest(t *testing.T) { + t.Helper() spotAvailable, err := NewPairsFromStrings([]string{"BTC-USD", "LTC-USD"}) if err != nil { t.Fatal(err) diff --git a/currency/pair_test.go b/currency/pair_test.go index c6feb663..09d66c47 100644 --- a/currency/pair_test.go +++ b/currency/pair_test.go @@ -16,13 +16,11 @@ func TestLower(t *testing.T) { if err != nil { t.Fatal(err) } - actual := pair.Lower() expected, err := NewPairFromString(defaultPair) if err != nil { t.Fatal(err) } - - if actual.String() != expected.Lower().String() { + if actual := pair.Lower(); actual.String() != expected.Lower().String() { t.Errorf("Lower(): %s was not equal to expected value: %s", actual, expected.Lower()) @@ -35,12 +33,11 @@ func TestUpper(t *testing.T) { if err != nil { t.Fatal(err) } - actual := pair.Upper() expected, err := NewPairFromString(defaultPair) if err != nil { t.Fatal(err) } - if actual.String() != expected.String() { + if actual := pair.Upper(); actual.String() != expected.String() { t.Errorf("Upper(): %s was not equal to expected value: %s", actual, expected) } @@ -126,9 +123,7 @@ func TestIsFiatPair(t *testing.T) { func TestString(t *testing.T) { t.Parallel() pair := NewPair(BTC, USD) - actual := defaultPair - expected := pair.String() - if actual != expected { + if actual, expected := defaultPair, pair.String(); actual != expected { t.Errorf("String(): %s was not equal to expected value: %s", actual, expected) } @@ -137,9 +132,7 @@ func TestString(t *testing.T) { func TestFirstCurrency(t *testing.T) { t.Parallel() pair := NewPair(BTC, USD) - actual := pair.Base - expected := BTC - if actual != expected { + if actual, expected := pair.Base, BTC; actual != expected { t.Errorf( "GetFirstCurrency(): %s was not equal to expected value: %s", actual, expected, @@ -150,9 +143,7 @@ func TestFirstCurrency(t *testing.T) { func TestSecondCurrency(t *testing.T) { t.Parallel() pair := NewPair(BTC, USD) - actual := pair.Quote - expected := USD - if actual != expected { + if actual, expected := pair.Quote, USD; actual != expected { t.Errorf( "GetSecondCurrency(): %s was not equal to expected value: %s", actual, expected, @@ -163,9 +154,7 @@ func TestSecondCurrency(t *testing.T) { func TestPair(t *testing.T) { t.Parallel() pair := NewPair(BTC, USD) - actual := pair.String() - expected := defaultPair - if actual != expected { + if actual, expected := pair.String(), defaultPair; actual != expected { t.Errorf( "Pair(): %s was not equal to expected value: %s", actual, expected, @@ -282,9 +271,7 @@ func TestEqualIncludeReciprocal(t *testing.T) { func TestSwap(t *testing.T) { t.Parallel() pair := NewPair(BTC, USD) - actual := pair.Swap().String() - expected := "USDBTC" - if actual != expected { + if actual, expected := pair.Swap().String(), "USDBTC"; actual != expected { t.Errorf( "TestSwap: %s was not equal to expected value: %s", actual, expected, @@ -308,9 +295,7 @@ func TestEmpty(t *testing.T) { func TestNewPair(t *testing.T) { t.Parallel() pair := NewPair(BTC, USD) - actual := pair.String() - expected := defaultPair - if actual != expected { + if expected, actual := defaultPair, pair.String(); actual != expected { t.Errorf( "Pair(): %s was not equal to expected value: %s", actual, expected, diff --git a/currency/pairs.go b/currency/pairs.go index 2d5b9a18..e7ab204c 100644 --- a/currency/pairs.go +++ b/currency/pairs.go @@ -185,11 +185,8 @@ func (p Pairs) FindDifferences(pairs Pairs) (newPairs, removedPairs Pairs) { // GetRandomPair returns a random pair from a list of pairs func (p Pairs) GetRandomPair() Pair { - pairsLen := len(p) - - if pairsLen == 0 { - return Pair{Base: NewCode(""), Quote: NewCode("")} + if pairsLen := len(p); pairsLen != 0 { + return p[rand.Intn(pairsLen)] // nolint:gosec // basic number generation required, no need for crypo/rand } - - return p[rand.Intn(pairsLen)] // nolint:gosec // basic number generation required, no need for crypo/rand + return Pair{} } diff --git a/currency/storage.go b/currency/storage.go index 8fef8ff0..6d169d59 100644 --- a/currency/storage.go +++ b/currency/storage.go @@ -82,7 +82,6 @@ func (s *Storage) RunUpdater(overrides BotOverrides, settings *MainConfiguration if err != nil { log.Errorf(log.Global, "Unable to setup CoinMarketCap analysis. Error: %s", err) - c = nil settings.CryptocurrencyProvider.Enabled = false } else { s.currencyAnalysis = c diff --git a/currency/symbol_test.go b/currency/symbol_test.go index 94593ec4..fb6d0e67 100644 --- a/currency/symbol_test.go +++ b/currency/symbol_test.go @@ -3,13 +3,12 @@ package currency import "testing" func TestGetSymbolByCurrencyName(t *testing.T) { - expected := "₩" actual, err := GetSymbolByCurrencyName(KPW) if err != nil { t.Errorf("TestGetSymbolByCurrencyName error: %s", err) } - if actual != expected { + if expected := "₩"; actual != expected { t.Errorf("TestGetSymbolByCurrencyName differing values") } diff --git a/database/README.md b/database/README.md index 533f3690..59ac394d 100644 --- a/database/README.md +++ b/database/README.md @@ -30,17 +30,17 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader [SQLBoiler](https://github.com/thrasher-corp/sqlboiler) ```shell script -go get -u github.com/thrasher-corp/sqlboiler +go install github.com/thrasher-corp/sqlboiler ``` [Postgres Driver](https://github.com/thrasher-corp/sqlboiler/drivers/sqlboiler-psql) ```shell script -go get -u github.com/thrasher-corp/sqlboiler/drivers/sqlboiler-psql +go install github.com/thrasher-corp/sqlboiler/drivers/sqlboiler-psql ``` [SQLite Driver](https://github.com/thrasher-corp/sqlboiler-sqlite3) ```shell script -go get -u github.com/thrasher-corp/sqlboiler-sqlite3 +go install github.com/thrasher-corp/sqlboiler-sqlite3 ``` ##### Configuration diff --git a/database/repository/candle/candle_test.go b/database/repository/candle/candle_test.go index f69fa25f..ee88ff78 100644 --- a/database/repository/candle/candle_test.go +++ b/database/repository/candle/candle_test.go @@ -241,25 +241,19 @@ func TestSeries(t *testing.T) { t.Errorf("unexpected number of results received: %v", len(ret.Candles)) } - ret, err = Series("", "", "", 0, "", start, end) + _, err = Series("", "", "", 0, "", start, end) if !errors.Is(err, errInvalidInput) { t.Fatal(err) } - ret, err = Series(testExchanges[0].Name, + _, err = Series(testExchanges[0].Name, "BTC", "MOON", 864000, "spot", start, end) - if err != nil { - if !errors.Is(err, errInvalidInput) { - if !errors.Is(err, ErrNoCandleDataFound) { - t.Fatal(err) - } - } + if err != nil && !errors.Is(err, errInvalidInput) && !errors.Is(err, ErrNoCandleDataFound) { + t.Fatal(err) } - - err = testhelpers.CloseDatabase(dbConn) - if err != nil { + if err = testhelpers.CloseDatabase(dbConn); err != nil { t.Error(err) } }) diff --git a/database/repository/exchange/exchange_test.go b/database/repository/exchange/exchange_test.go index 1ce6a359..65202ffb 100644 --- a/database/repository/exchange/exchange_test.go +++ b/database/repository/exchange/exchange_test.go @@ -177,8 +177,7 @@ func seed() error { func TestLoadCSV(t *testing.T) { testData := filepath.Join("..", "..", "..", "testdata", "exchangelist.csv") - _, err := LoadCSV(testData) - if err != nil { + if _, err := LoadCSV(testData); err != nil { t.Fatal(err) } } diff --git a/database/repository/trade/trade_test.go b/database/repository/trade/trade_test.go index e280f6a0..b8831351 100644 --- a/database/repository/trade/trade_test.go +++ b/database/repository/trade/trade_test.go @@ -97,6 +97,7 @@ func TestTrades(t *testing.T) { } func tradeSQLTester(t *testing.T) { + t.Helper() var trades, trades2 []Data firstTime := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) for i := 0; i < 20; i++ { diff --git a/database/repository/withdraw/withdraw_test.go b/database/repository/withdraw/withdraw_test.go index 042fe179..f27ddcab 100644 --- a/database/repository/withdraw/withdraw_test.go +++ b/database/repository/withdraw/withdraw_test.go @@ -151,6 +151,7 @@ func seedWithdrawData() { } } func withdrawHelper(t *testing.T) { + t.Helper() seedWithdrawData() _, err := GetEventByUUID(withdraw.DryRunID.String()) diff --git a/dispatch/dispatch.go b/dispatch/dispatch.go index 291116a8..38d7cb32 100644 --- a/dispatch/dispatch.go +++ b/dispatch/dispatch.go @@ -292,7 +292,10 @@ func (d *Dispatcher) subscribe(id uuid.UUID) (chan interface{}, error) { } // Get an unused channel from the channel pool - unusedChan := d.outbound.Get().(chan interface{}) + unusedChan, ok := d.outbound.Get().(chan interface{}) + if !ok { + return nil, errors.New("unable to type assert unusedChan") + } // Lock for writing to the route list d.rMtx.Lock() @@ -356,11 +359,11 @@ func (d *Dispatcher) getNewID() (uuid.UUID, error) { // Check to see if it already exists d.rMtx.RLock() - _, ok := d.routes[newID] - d.rMtx.RUnlock() - if ok { + if _, ok := d.routes[newID]; ok { + d.rMtx.RUnlock() return newID, errors.New("dispatcher collision detected, uuid already exists") } + d.rMtx.RUnlock() // Write the key into system d.rMtx.Lock() diff --git a/docs/ADD_NEW_EXCHANGE.md b/docs/ADD_NEW_EXCHANGE.md index 8d08d643..b476f7a2 100644 --- a/docs/ADD_NEW_EXCHANGE.md +++ b/docs/ADD_NEW_EXCHANGE.md @@ -462,7 +462,7 @@ func (f *FTX) SendAuthHTTPRequest(ctx context.Context, method, path string, data // within time receive windows. NOTE: This is not always necessary and the above // SendHTTPRequest example will suffice. generate := func() (*request.Item, error) { - ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) + ts := strconv.FormatInt(time.Now().UnixMilli(), 10) var body io.Reader var hmac, payload []byte var err error @@ -1028,8 +1028,7 @@ https://docs.ftx.com/#private-channels ```go // WsAuth sends an authentication message to receive auth data func (f *FTX) WsAuth() error { - intNonce := time.Now().UnixNano() / 1000000 - strNonce := strconv.FormatInt(intNonce, 10) + strNonce := strconv.FormatInt(time.Now().UnixMilli(), 10) hmac := crypto.GetHMAC( crypto.HashSHA256, []byte(strNonce+"websocket_login"), diff --git a/engine/apiserver_test.go b/engine/apiserver_test.go index 0ceffbd7..33decd6f 100644 --- a/engine/apiserver_test.go +++ b/engine/apiserver_test.go @@ -234,6 +234,7 @@ func TestGetAllActiveAccounts(t *testing.T) { } func makeHTTPGetRequest(t *testing.T, response interface{}) *http.Response { + t.Helper() w := httptest.NewRecorder() err := writeResponse(w, response) diff --git a/engine/database_connection.go b/engine/database_connection.go index e5e9e42e..e11eaaeb 100644 --- a/engine/database_connection.go +++ b/engine/database_connection.go @@ -51,8 +51,7 @@ func SetupDatabaseConnectionManager(cfg *database.Config) (*DatabaseConnectionMa cfg: *cfg, dbConn: database.DB, } - err := m.dbConn.SetConfig(cfg) - if err != nil { + if err := m.dbConn.SetConfig(cfg); err != nil { return nil, err } @@ -177,8 +176,7 @@ func (m *DatabaseConnectionManager) checkConnection() error { return database.ErrNoDatabaseProvided } - err := m.dbConn.Ping() - if err != nil { + if err := m.dbConn.Ping(); err != nil { m.dbConn.SetConnected(false) return err } diff --git a/engine/datahistory_manager.go b/engine/datahistory_manager.go index 7f81908e..5a831826 100644 --- a/engine/datahistory_manager.go +++ b/engine/datahistory_manager.go @@ -455,7 +455,7 @@ ranges: lookup = append(lookup, *result) job.Results[result.IntervalStartDate] = lookup } - completed := true + completed := true // nolint:ifshort,nolintlint // false positive and triggers only on Windows allResultsSuccessful := true allResultsFailed := true completionCheck: @@ -553,7 +553,7 @@ timesToFetch: job.Results[result.IntervalStartDate] = lookup } - completed := true + completed := true // nolint:ifshort,nolintlint // false positive and triggers only on Windows allResultsSuccessful := true allResultsFailed := true completionCheck: @@ -709,7 +709,7 @@ func (m *DataHistoryManager) processCandleData(job *DataHistoryJob, exch exchang if err != nil { r.Result += "could not get candles: " + err.Error() + ". " r.Status = dataHistoryStatusFailed - return r, nil + return r, nil // nolint:nilerr // error is returned in the job result } job.rangeHolder.SetHasDataFromCandles(candles.Candles) for i := range job.rangeHolder.Ranges[intervalIndex].Intervals { @@ -758,13 +758,13 @@ func (m *DataHistoryManager) processTradeData(job *DataHistoryJob, exch exchange if err != nil { r.Result += "could not get trades: " + err.Error() + ". " r.Status = dataHistoryStatusFailed - return r, nil + return r, nil // nolint:nilerr // error is returned in the job result } candles, err := trade.ConvertTradesToCandles(job.Interval, trades...) if err != nil { r.Result += "could not convert candles to trades: " + err.Error() + ". " r.Status = dataHistoryStatusFailed - return r, nil + return r, nil // nolint:nilerr // error is returned in the job result } job.rangeHolder.SetHasDataFromCandles(candles.Candles) for i := range job.rangeHolder.Ranges[intervalIndex].Intervals { @@ -825,13 +825,13 @@ func (m *DataHistoryManager) convertTradesToCandles(job *DataHistoryJob, startRa if err != nil { r.Result = "could not get trades in range: " + err.Error() r.Status = dataHistoryStatusFailed - return r, nil + return r, nil // nolint:nilerr // error is returned in the job result } candles, err := trade.ConvertTradesToCandles(job.Interval, trades...) if err != nil { r.Result = "could not convert trades in range: " + err.Error() r.Status = dataHistoryStatusFailed - return r, nil + return r, nil // nolint:nilerr // error is returned in the job result } candles.SourceJobID = job.ID err = m.saveCandlesInBatches(job, &candles, r) @@ -864,13 +864,13 @@ func (m *DataHistoryManager) convertCandleData(job *DataHistoryJob, startRange, if err != nil { r.Result = "could not get candles in range: " + err.Error() r.Status = dataHistoryStatusFailed - return r, nil + return r, nil // nolint:nilerr // error is returned in the job result } newCandles, err := kline.ConvertToNewInterval(&candles, job.ConversionInterval) if err != nil { r.Result = "could not convert candles in range: " + err.Error() r.Status = dataHistoryStatusFailed - return r, nil + return r, nil // nolint:nilerr // error is returned in the job result } newCandles.SourceJobID = job.ID err = m.saveCandlesInBatches(job, &candles, r) @@ -912,14 +912,14 @@ func (m *DataHistoryManager) validateCandles(job *DataHistoryJob, exch exchange. if err != nil { r.Result = "could not get API candles: " + err.Error() r.Status = dataHistoryStatusFailed - return r, nil + return r, nil // nolint:nilerr // error is returned in the job result } apiCandles.ValidationJobID = job.ID dbCandles, err := m.candleLoader(job.Exchange, job.Pair, job.Asset, job.Interval, startRange, endRange) if err != nil { r.Result = "could not get database candles: " + err.Error() r.Status = dataHistoryStatusFailed - return r, nil + return r, nil // nolint:nilerr // error is returned in the job result } if len(dbCandles.Candles) == 0 { r.Result = fmt.Sprintf("missing database candles for period %v-%v", startRange, endRange) diff --git a/engine/datahistory_manager_test.go b/engine/datahistory_manager_test.go index 6a884bdc..27ae42b1 100644 --- a/engine/datahistory_manager_test.go +++ b/engine/datahistory_manager_test.go @@ -655,6 +655,7 @@ func TestCompareJobsToData(t *testing.T) { } func TestRunJob(t *testing.T) { + t.Parallel() testCases := []*DataHistoryJob{ { Nickname: "TestRunJobDataHistoryCandleDataType", @@ -923,6 +924,7 @@ func TestConverters(t *testing.T) { // test helper functions func createDHM(t *testing.T) (*DataHistoryManager, *datahistoryjob.DataHistoryJob) { + t.Helper() em := SetupExchangeManager() exch, err := em.NewExchangeByName(testExchange) if !errors.Is(err, nil) { diff --git a/engine/engine.go b/engine/engine.go index 1a9bed6c..ec69c5c6 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -708,8 +708,7 @@ func (bot *Engine) Stop() { // Wait for services to gracefully shutdown bot.ServicesWG.Wait() - err := gctlog.CloseLogger() - if err != nil { + if err := gctlog.CloseLogger(); err != nil { log.Printf("Failed to close logger. Error: %v\n", err) } } diff --git a/engine/helpers_test.go b/engine/helpers_test.go index 36ba23ef..a3e79de7 100644 --- a/engine/helpers_test.go +++ b/engine/helpers_test.go @@ -36,6 +36,7 @@ import ( var testExchange = "Bitstamp" func CreateTestBot(t *testing.T) *Engine { + t.Helper() cFormat := ¤cy.PairFormat{Uppercase: true} cp1 := currency.NewPair(currency.BTC, currency.USD) cp2 := currency.NewPair(currency.BTC, currency.USDT) @@ -86,11 +87,9 @@ func CreateTestBot(t *testing.T) *Engine { }, }, }}} - err := bot.LoadExchange(testExchange, nil) - if err != nil { + if err := bot.LoadExchange(testExchange, nil); err != nil { t.Fatalf("SetupTest: Failed to load exchange: %s", err) } - return bot } @@ -117,6 +116,7 @@ func TestGetRPCEndpoints(t *testing.T) { } func TestSetSubsystem(t *testing.T) { + t.Parallel() testCases := []struct { Subsystem string Engine *Engine diff --git a/engine/order_manager_test.go b/engine/order_manager_test.go index 1096955b..64be84d8 100644 --- a/engine/order_manager_test.go +++ b/engine/order_manager_test.go @@ -182,6 +182,7 @@ func TestOrderManagerStop(t *testing.T) { } func OrdersSetup(t *testing.T) *OrderManager { + t.Helper() var wg sync.WaitGroup em := SetupExchangeManager() exch, err := em.NewExchangeByName(testExchange) @@ -377,8 +378,7 @@ func TestExists(t *testing.T) { Exchange: testExchange, ID: "TestExists", } - err := m.orderStore.add(o) - if err != nil { + if err := m.orderStore.add(o); err != nil { t.Error(err) } b := m.orderStore.exists(o) @@ -551,8 +551,7 @@ func TestCancelAllOrders(t *testing.T) { ID: "TestCancelAllOrders", Status: order.New, } - err := m.orderStore.add(o) - if err != nil { + if err := m.orderStore.add(o); err != nil { t.Error(err) } diff --git a/engine/rpcserver.go b/engine/rpcserver.go index e29fb3c5..0e319f37 100644 --- a/engine/rpcserver.go +++ b/engine/rpcserver.go @@ -109,8 +109,7 @@ func (s *RPCServer) authenticateClient(ctx context.Context) (context.Context, er // StartRPCServer starts a gRPC server with TLS auth func StartRPCServer(engine *Engine) { targetDir := utils.GetTLSDir(engine.Settings.DataDir) - err := checkCerts(targetDir) - if err != nil { + if err := checkCerts(targetDir); err != nil { log.Errorf(log.GRPCSys, "gRPC checkCerts failed. err: %s\n", err) return } @@ -678,7 +677,15 @@ func (s *RPCServer) GetAccountInfoStream(r *gctrpc.GetAccountInfoRequest, stream return errDispatchSystem } - acc := (*data.(*interface{})).(account.Holdings) + d := *data.(*interface{}) + if d == nil { + return errors.New("unable to type assert data") + } + + acc, ok := d.(account.Holdings) + if !ok { + return errors.New("unable to type assert account holdings data") + } var accounts []*gctrpc.Account for x := range acc.Accounts { @@ -1977,7 +1984,16 @@ func (s *RPCServer) GetExchangeOrderbookStream(r *gctrpc.GetExchangeOrderbookStr return errDispatchSystem } - ob := (*data.(*interface{})).(orderbook.Base) + d := *data.(*interface{}) + if d == nil { + return errors.New("unable to type assert data") + } + + ob, ok := d.(orderbook.Base) + if !ok { + return errors.New("unable to type assert orderbook data") + } + bids := make([]*gctrpc.OrderbookItem, len(ob.Bids)) for i := range ob.Bids { bids[i] = &gctrpc.OrderbookItem{ @@ -2050,7 +2066,16 @@ func (s *RPCServer) GetTickerStream(r *gctrpc.GetTickerStreamRequest, stream gct if !ok { return errDispatchSystem } - t := (*data.(*interface{})).(ticker.Price) + + d := *data.(*interface{}) + if d == nil { + return errors.New("unable to type assert data") + } + + t, ok := d.(ticker.Price) + if !ok { + return errors.New("unable to type assert ticker data") + } err := stream.Send(&gctrpc.TickerResponse{ Pair: &gctrpc.CurrencyPair{ @@ -2099,7 +2124,16 @@ func (s *RPCServer) GetExchangeTickerStream(r *gctrpc.GetExchangeTickerStreamReq if !ok { return errDispatchSystem } - t := (*data.(*interface{})).(ticker.Price) + + d := *data.(*interface{}) + if d == nil { + return errors.New("unable to type assert data") + } + + t, ok := d.(ticker.Price) + if !ok { + return errors.New("unable to type assert ticker data") + } err := stream.Send(&gctrpc.TickerResponse{ Pair: &gctrpc.CurrencyPair{ @@ -2355,7 +2389,11 @@ func (s *RPCServer) GCTScriptStatus(_ context.Context, _ *gctrpc.GCTScriptStatus } gctscript.AllVMSync.Range(func(k, v interface{}) bool { - vm := v.(*gctscript.VM) + vm, ok := v.(*gctscript.VM) + if !ok { + log.Errorf(log.GRPCSys, "Unable to type assert gctscript.VM") + return false + } resp.Scripts = append(resp.Scripts, &gctrpc.GCTScript{ UUID: vm.ID.String(), Name: vm.ShortName(), @@ -2376,6 +2414,7 @@ func (s *RPCServer) GCTScriptQuery(_ context.Context, r *gctrpc.GCTScriptQueryRe UUID, err := uuid.FromString(r.Script.UUID) if err != nil { + // nolint:nilerr // error is returned in the GCTScriptQueryResponse return &gctrpc.GCTScriptQueryResponse{Status: MsgStatusError, Data: err.Error()}, nil } @@ -2415,9 +2454,8 @@ func (s *RPCServer) GCTScriptExecute(_ context.Context, r *gctrpc.GCTScriptExecu } script := filepath.Join(r.Script.Path, r.Script.Name) - err := gctVM.Load(script) - if err != nil { - return &gctrpc.GenericResponse{ + if err := gctVM.Load(script); err != nil { + return &gctrpc.GenericResponse{ // nolint:nilerr // error is returned in the generic response Status: MsgStatusError, Data: err.Error(), }, nil @@ -2439,7 +2477,7 @@ func (s *RPCServer) GCTScriptStop(_ context.Context, r *gctrpc.GCTScriptStopRequ UUID, err := uuid.FromString(r.Script.UUID) if err != nil { - return &gctrpc.GenericResponse{Status: MsgStatusError, Data: err.Error()}, nil + return &gctrpc.GenericResponse{Status: MsgStatusError, Data: err.Error()}, nil // nolint:nilerr // error is returned in the generic response } if v, f := gctscript.AllVMSync.Load(UUID); f { @@ -2603,7 +2641,7 @@ func (s *RPCServer) GCTScriptStopAll(context.Context, *gctrpc.GCTScriptStopAllRe err := s.gctScriptManager.ShutdownAll() if err != nil { - return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil + return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil // nolint:nilerr // error is returned in the generic response } return &gctrpc.GenericResponse{ @@ -2621,6 +2659,7 @@ func (s *RPCServer) GCTScriptAutoLoadToggle(_ context.Context, r *gctrpc.GCTScri if r.Status { err := s.gctScriptManager.Autoload(r.Script, true) if err != nil { + // nolint:nilerr // error is returned in the generic response return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil } return &gctrpc.GenericResponse{Status: "success", Data: "script " + r.Script + " removed from autoload list"}, nil @@ -2628,7 +2667,7 @@ func (s *RPCServer) GCTScriptAutoLoadToggle(_ context.Context, r *gctrpc.GCTScri err := s.gctScriptManager.Autoload(r.Script, false) if err != nil { - return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil + return &gctrpc.GenericResponse{Status: "error", Data: err.Error()}, nil // nolint:nilerr // error is returned in the generic response } return &gctrpc.GenericResponse{Status: "success", Data: "script " + r.Script + " added to autoload list"}, nil } @@ -2726,7 +2765,7 @@ func (s *RPCServer) UpdateExchangeSupportedPairs(ctx context.Context, r *gctrpc. return nil, err } - base := exch.GetBase() + base := exch.GetBase() // nolint:ifshort,nolintlint // false positive and triggers only on Windows if base == nil { return nil, errExchangeBaseNotFound } diff --git a/engine/rpcserver_test.go b/engine/rpcserver_test.go index 76afc0a1..6c9f8647 100644 --- a/engine/rpcserver_test.go +++ b/engine/rpcserver_test.go @@ -1557,8 +1557,7 @@ func TestGetActiveDataHistoryJobs(t *testing.T) { Interval: kline.OneMin, } - err := m.UpsertJob(dhj, false) - if !errors.Is(err, nil) { + if err := m.UpsertJob(dhj, false); !errors.Is(err, nil) { t.Fatalf("received %v, expected %v", err, nil) } diff --git a/engine/withdraw_manager.go b/engine/withdraw_manager.go index a03751b1..660648e2 100644 --- a/engine/withdraw_manager.go +++ b/engine/withdraw_manager.go @@ -98,8 +98,7 @@ func (m *WithdrawManager) WithdrawalEventByID(id string) (*withdraw.Response, er if m == nil { return nil, ErrNilSubsystem } - v := withdraw.Cache.Get(id) - if v != nil { + if v := withdraw.Cache.Get(id); v != nil { return v.(*withdraw.Response), nil } diff --git a/exchanges/alert/alert_test.go b/exchanges/alert/alert_test.go index a40787c3..76805d09 100644 --- a/exchanges/alert/alert_test.go +++ b/exchanges/alert/alert_test.go @@ -26,10 +26,10 @@ func TestWait(t *testing.T) { wg.Wait() wg.Add(100) - isLeaky(&wait, nil, t) + isLeaky(t, &wait, nil) wait.Alert() wg.Wait() - isLeaky(&wait, nil, t) + isLeaky(t, &wait, nil) // use kick ch := make(chan struct{}) @@ -46,11 +46,11 @@ func TestWait(t *testing.T) { } wg.Wait() wg.Add(100) - isLeaky(&wait, ch, t) + isLeaky(t, &wait, ch) close(ch) wg.Wait() ch = make(chan struct{}) - isLeaky(&wait, ch, t) + isLeaky(t, &wait, ch) // late receivers wg.Add(100) @@ -70,15 +70,15 @@ func TestWait(t *testing.T) { } wg.Wait() wg.Add(100) - isLeaky(&wait, ch, t) + isLeaky(t, &wait, ch) wait.Alert() wg.Wait() - isLeaky(&wait, ch, t) + isLeaky(t, &wait, ch) } // isLeaky tests to see if the wait functionality is returning an abnormal // channel that is operational when it shouldn't be. -func isLeaky(a *Notice, ch chan struct{}, t *testing.T) { +func isLeaky(t *testing.T, a *Notice, ch chan struct{}) { t.Helper() check := a.Wait(ch) time.Sleep(time.Millisecond * 5) // When we call wait a routine for hold is diff --git a/exchanges/asset/asset_test.go b/exchanges/asset/asset_test.go index 4553b0f1..2bf7cf63 100644 --- a/exchanges/asset/asset_test.go +++ b/exchanges/asset/asset_test.go @@ -62,8 +62,7 @@ func TestIsValid(t *testing.T) { } func TestNew(t *testing.T) { - _, err := New("Spota") - if err == nil { + if _, err := New("Spota"); err == nil { t.Fatal("TestNew returned an unexpected result") } diff --git a/exchanges/binance/binance.go b/exchanges/binance/binance.go index 193a6538..587a9d0f 100644 --- a/exchanges/binance/binance.go +++ b/exchanges/binance/binance.go @@ -374,51 +374,59 @@ func (b *Binance) GetSpotKline(ctx context.Context, arg *KlinesRequestParams) ([ path := candleStick + "?" + params.Encode() - if err := b.SendHTTPRequest(ctx, + err = b.SendHTTPRequest(ctx, exchange.RestSpotSupplementary, path, spotDefaultRate, - &resp); err != nil { - return klineData, err + &resp) + if err != nil { + return nil, err } - - for _, responseData := range resp.([]interface{}) { + responseData, ok := resp.([]interface{}) + if !ok { + return nil, errors.New("unable to type assert responseData") + } + for x := range responseData { + individualData, ok := responseData[x].([]interface{}) + if !ok { + return nil, errors.New("unable to type assert individualData") + } + if len(individualData) != 12 { + return nil, errors.New("unexpected kline data length") + } var candle CandleStick - for i, individualData := range responseData.([]interface{}) { - switch i { - case 0: - tempTime := individualData.(float64) - var err error - candle.OpenTime, err = convert.TimeFromUnixTimestampFloat(tempTime) - if err != nil { - return klineData, err - } - case 1: - candle.Open, _ = strconv.ParseFloat(individualData.(string), 64) - case 2: - candle.High, _ = strconv.ParseFloat(individualData.(string), 64) - case 3: - candle.Low, _ = strconv.ParseFloat(individualData.(string), 64) - case 4: - candle.Close, _ = strconv.ParseFloat(individualData.(string), 64) - case 5: - candle.Volume, _ = strconv.ParseFloat(individualData.(string), 64) - case 6: - tempTime := individualData.(float64) - var err error - candle.CloseTime, err = convert.TimeFromUnixTimestampFloat(tempTime) - if err != nil { - return klineData, err - } - case 7: - candle.QuoteAssetVolume, _ = strconv.ParseFloat(individualData.(string), 64) - case 8: - candle.TradeCount = individualData.(float64) - case 9: - candle.TakerBuyAssetVolume, _ = strconv.ParseFloat(individualData.(string), 64) - case 10: - candle.TakerBuyQuoteAssetVolume, _ = strconv.ParseFloat(individualData.(string), 64) - } + if candle.OpenTime, err = convert.TimeFromUnixTimestampFloat(individualData[0]); err != nil { + return nil, err + } + if candle.Open, err = convert.FloatFromString(individualData[1]); err != nil { + return nil, err + } + if candle.High, err = convert.FloatFromString(individualData[2]); err != nil { + return nil, err + } + if candle.Low, err = convert.FloatFromString(individualData[3]); err != nil { + return nil, err + } + if candle.Close, err = convert.FloatFromString(individualData[4]); err != nil { + return nil, err + } + if candle.Volume, err = convert.FloatFromString(individualData[5]); err != nil { + return nil, err + } + if candle.CloseTime, err = convert.TimeFromUnixTimestampFloat(individualData[6]); err != nil { + return nil, err + } + if candle.QuoteAssetVolume, err = convert.FloatFromString(individualData[7]); err != nil { + return nil, err + } + if candle.TradeCount, ok = individualData[8].(float64); !ok { + return nil, errors.New("unable to type assert trade count") + } + if candle.TakerBuyAssetVolume, err = convert.FloatFromString(individualData[9]); err != nil { + return nil, err + } + if candle.TakerBuyQuoteAssetVolume, err = convert.FloatFromString(individualData[10]); err != nil { + return nil, err } klineData = append(klineData, candle) } @@ -784,7 +792,7 @@ func (b *Binance) SendAuthHTTPRequest(ctx context.Context, ePath exchange.URL, m } if params.Get("recvWindow") == "" { - params.Set("recvWindow", strconv.FormatInt(convert.RecvWindow(defaultRecvWindow), 10)) + params.Set("recvWindow", strconv.FormatInt(defaultRecvWindow.Milliseconds(), 10)) } interim := json.RawMessage{} diff --git a/exchanges/binance/binance_mock_test.go b/exchanges/binance/binance_mock_test.go index c626a72c..cefeb9ae 100644 --- a/exchanges/binance/binance_mock_test.go +++ b/exchanges/binance/binance_mock_test.go @@ -1,4 +1,5 @@ -//+build !mock_test_off +//go:build !mock_test_off +// +build !mock_test_off // This will build if build tag mock_test_off is not parsed and will try to mock // all tests in _test.go diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index c49a52bc..4051dae0 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -1622,6 +1622,7 @@ func TestGetAggregatedTradesBatched(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() if tt.mock != mockTests { t.Skip() } @@ -1677,6 +1678,7 @@ func TestGetAggregatedTradesErrors(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() _, err := b.GetAggregatedTrades(context.Background(), tt.args) if err == nil { t.Errorf("Binance.GetAggregatedTrades() error = %v, wantErr true", err) @@ -2550,8 +2552,8 @@ func TestWsOrderExecutionReport(t *testing.T) { Side: order.Buy, Status: order.New, AssetType: asset.Spot, - Date: time.Unix(0, 1616627567900*int64(time.Millisecond)), - LastUpdated: time.Unix(0, 1616627567900*int64(time.Millisecond)), + Date: time.UnixMilli(1616627567900), + LastUpdated: time.UnixMilli(1616627567900), Pair: currency.NewPair(currency.BTC, currency.USDT), } // empty the channel. otherwise mock_test will fail @@ -2583,8 +2585,7 @@ func TestWsOrderExecutionReport(t *testing.T) { func TestWsOutboundAccountPosition(t *testing.T) { t.Parallel() payload := []byte(`{"stream":"jTfvpakT2yT0hVIo5gYWVihZhdM2PrBgJUZ5PyfZ4EVpCkx4Uoxk5timcrQc","data":{"e":"outboundAccountPosition","E":1616628815745,"u":1616628815745,"B":[{"a":"BTC","f":"0.00225109","l":"0.00123000"},{"a":"BNB","f":"0.00000000","l":"0.00000000"},{"a":"USDT","f":"54.43390661","l":"0.00000000"}]}}`) - err := b.wsHandleData(payload) - if err != nil { + if err := b.wsHandleData(payload); err != nil { t.Fatal(err) } } diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index 894dfd4d..b5f951f1 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -371,7 +371,7 @@ func (b *Binance) FetchTradablePairs(ctx context.Context, a asset.Item) ([]strin case asset.CoinMarginedFutures: cInfo, err := b.FuturesExchangeInfo(ctx) if err != nil { - return pairs, nil + return pairs, err } for z := range cInfo.Symbols { if cInfo.Symbols[z].ContractStatus == "TRADING" { @@ -385,7 +385,7 @@ func (b *Binance) FetchTradablePairs(ctx context.Context, a asset.Item) ([]strin case asset.USDTMarginedFutures: uInfo, err := b.UExchangeInfo(ctx) if err != nil { - return pairs, nil + return pairs, err } for u := range uInfo.Symbols { if uInfo.Symbols[u].Status == "TRADING" { @@ -751,8 +751,7 @@ func (b *Binance) UpdateAccountInfo(ctx context.Context, assetType asset.Item) ( } acc.AssetType = assetType info.Accounts = append(info.Accounts, acc) - err := account.Process(&info) - if err != nil { + if err := account.Process(&info); err != nil { return account.Holdings{}, err } return info, nil diff --git a/exchanges/binance/ratelimit_test.go b/exchanges/binance/ratelimit_test.go index 7e7083a4..2b511966 100644 --- a/exchanges/binance/ratelimit_test.go +++ b/exchanges/binance/ratelimit_test.go @@ -10,6 +10,7 @@ import ( ) func TestRateLimit_Limit(t *testing.T) { + t.Parallel() symbol := "BTC-USDT" testTable := map[string]struct { @@ -56,6 +57,7 @@ func TestRateLimit_Limit(t *testing.T) { } func TestRateLimit_LimitStatic(t *testing.T) { + t.Parallel() testTable := map[string]request.EndpointLimit{ "Default": spotDefaultRate, "Historical Trades": spotHistoricalTradesRate, diff --git a/exchanges/binance/type_convert.go b/exchanges/binance/type_convert.go index 61540fd3..98163866 100644 --- a/exchanges/binance/type_convert.go +++ b/exchanges/binance/type_convert.go @@ -4,8 +4,6 @@ import ( "encoding/json" "strconv" "time" - - "github.com/thrasher-corp/gocryptotrader/common/convert" ) // binanceTime provides an internal conversion helper @@ -16,7 +14,7 @@ func (t *binanceTime) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, ×tamp); err != nil { return err } - *t = binanceTime(time.Unix(0, timestamp*int64(time.Millisecond))) + *t = binanceTime(time.UnixMilli(timestamp)) return nil } @@ -27,7 +25,7 @@ func (t binanceTime) Time() time.Time { // timeString gets the time as Binance timestamp func timeString(t time.Time) string { - return strconv.FormatInt(convert.UnixMillis(t), 10) + return strconv.FormatInt(t.UnixMilli(), 10) } // UnmarshalJSON deserialises the JSON info, including the timestamp @@ -74,11 +72,8 @@ func (a *NewOrderResponse) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &aux); err != nil { return err } - // there can be an empty response, then `a` is set to nil if aux != nil { a.TransactionTime = aux.TransactionTime.Time() - } else { - a = nil } return nil } diff --git a/exchanges/bitfinex/bitfinex.go b/exchanges/bitfinex/bitfinex.go index 6c1e7951..5c178783 100644 --- a/exchanges/bitfinex/bitfinex.go +++ b/exchanges/bitfinex/bitfinex.go @@ -319,16 +319,18 @@ func (b *Bitfinex) GetV2FundingInfo(ctx context.Context, key string) (MarginFund if len(fundingData) < 4 { return response, fmt.Errorf("%v GetV2FundingInfo: invalid length of fundingData", b.Name) } - for x := 0; x < 3; x++ { - _, ok := fundingData[x].(float64) - if !ok { - return response, fmt.Errorf("type conversion failed for x = %d", x) - } + if response.Data.YieldLoan, ok = fundingData[0].(float64); !ok { + return response, errors.New("type conversion failed for YieldLoan") + } + if response.Data.YieldLend, ok = fundingData[1].(float64); !ok { + return response, errors.New("type conversion failed for YieldLend") + } + if response.Data.DurationLoan, ok = fundingData[2].(float64); !ok { + return response, errors.New("type conversion failed for DurationLoan") + } + if response.Data.DurationLend, ok = fundingData[3].(float64); !ok { + return response, errors.New("type conversion failed for DurationLend") } - response.Data.YieldLoan = fundingData[0].(float64) - response.Data.YieldLend = fundingData[1].(float64) - response.Data.DurationLoan = fundingData[2].(float64) - response.Data.DurationLend = fundingData[3].(float64) return response, nil } @@ -637,7 +639,10 @@ func (b *Bitfinex) GetTrades(ctx context.Context, currencyPair string, limit, ti var history []Trade for i := range resp { - amount := resp[i][2].(float64) + amount, ok := resp[i][2].(float64) + if !ok { + return nil, errors.New("unable to type assert amount") + } side := order.Buy.String() if amount < 0 { side = order.Sell.String() @@ -693,10 +698,21 @@ func (b *Bitfinex) GetOrderbook(ctx context.Context, symbol, precision string, l var b Book if len(response[x]) > 3 { // Funding currency - b.Amount = response[x][3].(float64) - b.Rate = response[x][2].(float64) - b.Period = response[x][1].(float64) - b.OrderID = int64(response[x][0].(float64)) + var ok bool + if b.Amount, ok = response[x][3].(float64); !ok { + return Orderbook{}, errors.New("unable to type assert amount") + } + if b.Rate, ok = response[x][2].(float64); !ok { + return Orderbook{}, errors.New("unable to type assert rate") + } + if b.Period, ok = response[x][1].(float64); !ok { + return Orderbook{}, errors.New("unable to type assert period") + } + orderID, ok := response[x][0].(float64) + if !ok { + return Orderbook{}, errors.New("unable to type assert orderID") + } + b.OrderID = int64(orderID) if b.Amount > 0 { o.Asks = append(o.Asks, b) } else { @@ -705,9 +721,18 @@ func (b *Bitfinex) GetOrderbook(ctx context.Context, symbol, precision string, l } } else { // Trading currency - b.Amount = response[x][2].(float64) - b.Price = response[x][1].(float64) - b.OrderID = int64(response[x][0].(float64)) + var ok bool + if b.Amount, ok = response[x][2].(float64); !ok { + return Orderbook{}, errors.New("unable to type assert amount") + } + if b.Price, ok = response[x][1].(float64); !ok { + return Orderbook{}, errors.New("unable to type assert price") + } + orderID, ok := response[x][0].(float64) + if !ok { + return Orderbook{}, errors.New("unable to type assert order ID") + } + b.OrderID = int64(orderID) if b.Amount > 0 { o.Bids = append(o.Bids, b) } else { @@ -721,10 +746,21 @@ func (b *Bitfinex) GetOrderbook(ctx context.Context, symbol, precision string, l var b Book if len(response[x]) > 3 { // Funding currency - b.Amount = response[x][3].(float64) - b.Count = int64(response[x][2].(float64)) - b.Period = response[x][1].(float64) - b.Rate = response[x][0].(float64) + var ok bool + if b.Amount, ok = response[x][3].(float64); !ok { + return Orderbook{}, errors.New("unable to type assert amount") + } + count, ok := response[x][2].(float64) + if !ok { + return Orderbook{}, errors.New("unable to type assert count") + } + b.Count = int64(count) + if b.Period, ok = response[x][1].(float64); !ok { + return Orderbook{}, errors.New("unable to type assert period") + } + if b.Rate, ok = response[x][0].(float64); !ok { + return Orderbook{}, errors.New("unable to type assert rate") + } if b.Amount > 0 { o.Asks = append(o.Asks, b) } else { @@ -733,9 +769,18 @@ func (b *Bitfinex) GetOrderbook(ctx context.Context, symbol, precision string, l } } else { // Trading currency - b.Amount = response[x][2].(float64) - b.Count = int64(response[x][1].(float64)) - b.Price = response[x][0].(float64) + var ok bool + if b.Amount, ok = response[x][2].(float64); !ok { + return Orderbook{}, errors.New("unable to type assert amount") + } + count, ok := response[x][1].(float64) + if !ok { + return Orderbook{}, errors.New("unable to type assert count") + } + b.Count = int64(count) + if b.Price, ok = response[x][0].(float64); !ok { + return Orderbook{}, errors.New("unable to type assert price") + } if b.Amount > 0 { o.Bids = append(o.Bids, b) } else { @@ -940,12 +985,34 @@ func (b *Bitfinex) GetLeaderboard(ctx context.Context, key, timeframe, symbol st var result []LeaderboardEntry for x := range resp { - r := resp[x].([]interface{}) + r, ok := resp[x].([]interface{}) + if !ok { + return nil, errors.New("unable to type assert leaderboard") + } + if len(r) < 10 { + return nil, errors.New("unexpected leaderboard data length") + } + tm, ok := r[0].(float64) + if !ok { + return nil, errors.New("unable to type assert time") + } + username, ok := r[2].(string) + if !ok { + return nil, errors.New("unable to type assert username") + } + ranking, ok := r[3].(float64) + if !ok { + return nil, errors.New("unable to type assert ranking") + } + value, ok := r[6].(float64) + if !ok { + return nil, errors.New("unable to type assert value") + } result = append(result, LeaderboardEntry{ - Timestamp: time.Unix(0, int64(r[0].(float64))*int64(time.Millisecond)), - Username: r[2].(string), - Ranking: int(r[3].(float64)), - Value: r[6].(float64), + Timestamp: time.UnixMilli(int64(tm)), + Username: username, + Ranking: int(ranking), + Value: value, TwitterHandle: parseTwitterHandle(r[9]), }) } diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index 49d6d2ce..a2e74545 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -1067,9 +1067,9 @@ func TestWsAuth(t *testing.T) { } func runAuth(t *testing.T) { + t.Helper() setupWs() - err := b.WsSendAuth() - if err != nil { + if err := b.WsSendAuth(); err != nil { t.Error(err) } timer := time.NewTimer(sharedtestvalues.WebsocketResponseDefaultTimeout) @@ -1118,8 +1118,7 @@ func TestWsCancelOrder(t *testing.T) { if !wsAuthExecuted { runAuth(t) } - err := b.WsCancelOrder(1234) - if err != nil { + if err := b.WsCancelOrder(1234); err != nil { t.Error(err) } } @@ -1150,8 +1149,7 @@ func TestWsCancelAllOrders(t *testing.T) { if !wsAuthExecuted { runAuth(t) } - err := b.WsCancelAllOrders() - if err != nil { + if err := b.WsCancelAllOrders(); err != nil { t.Error(err) } } @@ -1198,8 +1196,7 @@ func TestWsCancelOffer(t *testing.T) { if !wsAuthExecuted { runAuth(t) } - err := b.WsCancelOffer(1234) - if err != nil { + if err := b.WsCancelOffer(1234); err != nil { t.Error(err) } } diff --git a/exchanges/bitfinex/bitfinex_websocket.go b/exchanges/bitfinex/bitfinex_websocket.go index c511e1fd..7e23576c 100644 --- a/exchanges/bitfinex/bitfinex_websocket.go +++ b/exchanges/bitfinex/bitfinex_websocket.go @@ -14,6 +14,7 @@ import ( "github.com/gorilla/websocket" "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" @@ -159,7 +160,10 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { ) } case "auth": - status := d["status"].(string) + status, ok := d["status"].(string) + if !ok { + return errors.New("unable to type assert status") + } if status == "OK" { b.Websocket.DataHandler <- d b.WsAddSubscriptionChannel(0, "account", "N/A") @@ -266,24 +270,27 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { switch id := obSnapBundle[0].(type) { case []interface{}: for i := range obSnapBundle { - data := obSnapBundle[i].([]interface{}) + data, ok := obSnapBundle[i].([]interface{}) + if !ok { + return errors.New("type assertion failed for orderbok item data") + } id, okAssert := data[0].(float64) if !okAssert { - return errors.New("type assertion failed for orderbook item data") + return errors.New("type assertion failed for orderbook id data") } pricePeriod, okAssert := data[1].(float64) if !okAssert { - return errors.New("type assertion failed for orderbook item data") + return errors.New("type assertion failed for orderbook price data") } rateAmount, okAssert := data[2].(float64) if !okAssert { - return errors.New("type assertion failed for orderbook item data") + return errors.New("type assertion failed for orderbook rate data") } if len(data) == 4 { fundingRate = true amount, okFunding := data[3].(float64) if !okFunding { - return errors.New("type assertion failed for orderbook item data") + return errors.New("type assertion failed for orderbook funding data") } newOrderbook = append(newOrderbook, WebsocketBook{ ID: int64(id), @@ -297,26 +304,25 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { Amount: rateAmount}) } } - err := b.WsInsertSnapshot(pair, chanAsset, newOrderbook, fundingRate) - if err != nil { + if err = b.WsInsertSnapshot(pair, chanAsset, newOrderbook, fundingRate); err != nil { return fmt.Errorf("inserting snapshot error: %s", err) } case float64: pricePeriod, okSnap := obSnapBundle[1].(float64) if !okSnap { - return errors.New("type assertion failed for orderbook snapshot data") + return errors.New("type assertion failed for orderbook price snapshot data") } amountRate, okSnap := obSnapBundle[2].(float64) if !okSnap { - return errors.New("type assertion failed for orderbook snapshot data") + return errors.New("type assertion failed for orderbook amount snapshot data") } if len(obSnapBundle) == 4 { fundingRate = true var amount float64 amount, okSnap = obSnapBundle[3].(float64) if !okSnap { - return errors.New("type assertion failed for orderbook snapshot data") + return errors.New("type assertion failed for orderbook amount snapshot data") } newOrderbook = append(newOrderbook, WebsocketBook{ ID: int64(id), @@ -330,8 +336,7 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { Amount: amountRate}) } - err := b.WsUpdateOrderbook(pair, chanAsset, newOrderbook, chanID, int64(sequenceNo), fundingRate) - if err != nil { + if err = b.WsUpdateOrderbook(pair, chanAsset, newOrderbook, chanID, int64(sequenceNo), fundingRate); err != nil { return fmt.Errorf("updating orderbook error: %s", err) } @@ -347,37 +352,73 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { switch candleData := candleBundle[0].(type) { case []interface{}: for i := range candleBundle { - element := candleBundle[i].([]interface{}) - b.Websocket.DataHandler <- stream.KlineData{ - Timestamp: time.Unix(0, int64(element[0].(float64))*int64(time.Millisecond)), - Exchange: b.Name, - AssetType: chanAsset, - Pair: pair, - OpenPrice: element[1].(float64), - ClosePrice: element[2].(float64), - HighPrice: element[3].(float64), - LowPrice: element[4].(float64), - Volume: element[5].(float64), + var element []interface{} + element, ok = candleBundle[i].([]interface{}) + if !ok { + return errors.New("type assertion for element data") } + if len(element) < 6 { + return errors.New("invalid candleBundle length") + } + var klineData stream.KlineData + if klineData.Timestamp, err = convert.TimeFromUnixTimestampFloat(element[0]); err != nil { + return fmt.Errorf("unable to convert timestamp: %w", err) + } + if klineData.OpenPrice, ok = element[1].(float64); !ok { + return errors.New("unable to type assert OpenPrice") + } + if klineData.ClosePrice, ok = element[2].(float64); !ok { + return errors.New("unable to type assert ClosePrice") + } + if klineData.HighPrice, ok = element[3].(float64); !ok { + return errors.New("unable to type assert HighPrice") + } + if klineData.LowPrice, ok = element[4].(float64); !ok { + return errors.New("unable to type assert LowPrice") + } + if klineData.Volume, ok = element[5].(float64); !ok { + return errors.New("unable to type assert volume") + } + klineData.Exchange = b.Name + klineData.AssetType = chanAsset + klineData.Pair = pair + b.Websocket.DataHandler <- klineData } - case float64: - b.Websocket.DataHandler <- stream.KlineData{ - Timestamp: time.Unix(0, int64(candleData)*int64(time.Millisecond)), - Exchange: b.Name, - AssetType: chanAsset, - Pair: pair, - OpenPrice: candleBundle[1].(float64), - ClosePrice: candleBundle[2].(float64), - HighPrice: candleBundle[3].(float64), - LowPrice: candleBundle[4].(float64), - Volume: candleBundle[5].(float64), + if len(candleBundle) < 6 { + return errors.New("invalid candleBundle length") } + var klineData stream.KlineData + if klineData.Timestamp, err = convert.TimeFromUnixTimestampFloat(candleData); err != nil { + return fmt.Errorf("unable to convert timestamp: %w", err) + } + if klineData.OpenPrice, ok = candleBundle[1].(float64); !ok { + return errors.New("unable to type assert OpenPrice") + } + if klineData.ClosePrice, ok = candleBundle[2].(float64); !ok { + return errors.New("unable to type assert ClosePrice") + } + if klineData.HighPrice, ok = candleBundle[3].(float64); !ok { + return errors.New("unable to type assert HighPrice") + } + if klineData.LowPrice, ok = candleBundle[4].(float64); !ok { + return errors.New("unable to type assert LowPrice") + } + if klineData.Volume, ok = candleBundle[5].(float64); !ok { + return errors.New("unable to type assert volume") + } + klineData.Exchange = b.Name + klineData.AssetType = chanAsset + klineData.Pair = pair + b.Websocket.DataHandler <- klineData } } return nil case wsTicker: - tickerData := d[1].([]interface{}) + tickerData, ok := d[1].([]interface{}) + if !ok { + return errors.New("type assertion for tickerData") + } if len(tickerData) == 10 { b.Websocket.DataHandler <- &ticker.Price{ ExchangeName: b.Name, @@ -421,9 +462,15 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { var tradeHolder []WebsocketTrade switch len(d) { case 2: - snapshot := d[1].([]interface{}) + snapshot, ok := d[1].([]interface{}) + if !ok { + return errors.New("unable to type assert snapshot data") + } for i := range snapshot { - elem := snapshot[i].([]interface{}) + elem, ok := snapshot[i].([]interface{}) + if !ok { + return errors.New("unable to type assert snapshot element data") + } if len(elem) == 5 { tradeHolder = append(tradeHolder, WebsocketTrade{ ID: int64(elem[0].(float64)), @@ -446,7 +493,10 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { d[1].(string) != wsTradeExecutionUpdate { return nil } - data := d[2].([]interface{}) + data, ok := d[2].([]interface{}) + if !ok { + return errors.New("data type assertion error") + } if len(data) == 5 { tradeHolder = append(tradeHolder, WebsocketTrade{ ID: int64(data[0].(float64)), @@ -479,7 +529,7 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { trades = append(trades, trade.Data{ TID: strconv.FormatInt(tradeHolder[i].ID, 10), CurrencyPair: pair, - Timestamp: time.Unix(0, tradeHolder[i].Timestamp*int64(time.Millisecond)), + Timestamp: time.UnixMilli(tradeHolder[i].Timestamp), Price: price, Amount: newAmount, Exchange: b.Name, @@ -496,9 +546,15 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { case wsHeartbeat, pong: return nil case wsNotification: - notification := d[2].([]interface{}) + notification, ok := d[2].([]interface{}) + if !ok { + return errors.New("unable to type assert notification data") + } if data, ok := notification[4].([]interface{}); ok { - channelName := notification[1].(string) + channelName, ok := notification[1].(string) + if !ok { + return errors.New("unable to type assert channelName") + } switch { case strings.Contains(channelName, wsFundingOrderNewRequest), strings.Contains(channelName, wsFundingOrderUpdateRequest), @@ -527,18 +583,26 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { respRaw) } } - if notification[5] != nil && - strings.EqualFold(notification[5].(string), wsError) { - return fmt.Errorf("%s - Error %s", - b.Name, - notification[6].(string)) + if notification[5] != nil { + if wsErr, ok := notification[5].(string); ok { + if strings.EqualFold(wsErr, wsError) { + if errMsg, ok := notification[6].(string); ok { + return fmt.Errorf("%s - Error %s", + b.Name, + errMsg) + } + return fmt.Errorf("%s - unhandled error message: %v", b.Name, + notification[6]) + } + } } case wsOrderSnapshot: if snapBundle, ok := d[2].([]interface{}); ok && len(snapBundle) > 0 { if _, ok := snapBundle[0].([]interface{}); ok { for i := range snapBundle { - positionData := snapBundle[i].([]interface{}) - b.wsHandleOrder(positionData) + if positionData, ok := snapBundle[i].([]interface{}); ok { + b.wsHandleOrder(positionData) + } } } } @@ -551,7 +615,10 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { if snapBundle, ok := d[2].([]interface{}); ok && len(snapBundle) > 0 { if _, ok := snapBundle[0].([]interface{}); ok { for i := range snapBundle { - positionData := snapBundle[i].([]interface{}) + positionData, ok := snapBundle[i].([]interface{}) + if !ok { + return errors.New("unable to type assert wsPositionSnapshot positionData") + } position := WebsocketPosition{ Pair: positionData[0].(string), Status: positionData[1].(string), @@ -606,7 +673,10 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { if snapBundle, ok := d[2].([]interface{}); ok && len(snapBundle) > 0 { if _, ok := snapBundle[0].([]interface{}); ok { for i := range snapBundle { - data := snapBundle[i].([]interface{}) + data, ok := snapBundle[i].([]interface{}) + if !ok { + return errors.New("unable to type assert wsFundingOrderSnapshot snapBundle data") + } offer := WsFundingOffer{ ID: int64(data[0].(float64)), Symbol: data[1].(string), @@ -639,7 +709,10 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { if snapBundle, ok := d[2].([]interface{}); ok && len(snapBundle) > 0 { if _, ok := snapBundle[0].([]interface{}); ok { for i := range snapBundle { - data := snapBundle[i].([]interface{}) + data, ok := snapBundle[i].([]interface{}) + if !ok { + return errors.New("unable to type assert wsFundingCreditSnapshot snapBundle data") + } credit := WsCredit{ ID: int64(data[0].(float64)), Symbol: data[1].(string), @@ -695,7 +768,10 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { if snapBundle, ok := d[2].([]interface{}); ok && len(snapBundle) > 0 { if _, ok := snapBundle[0].([]interface{}); ok { for i := range snapBundle { - data := snapBundle[i].([]interface{}) + data, ok := snapBundle[i].([]interface{}) + if !ok { + return errors.New("unable to type assert wsFundingLoanSnapshot snapBundle data") + } credit := WsCredit{ ID: int64(data[0].(float64)), Symbol: data[1].(string), @@ -749,10 +825,13 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { if snapBundle, ok := d[2].([]interface{}); ok && len(snapBundle) > 0 { if _, ok := snapBundle[0].([]interface{}); ok { for i := range snapBundle { - data := snapBundle[i].([]interface{}) + data, ok := snapBundle[i].([]interface{}) + if !ok { + return errors.New("unable to type assert wsWalletSnapshot snapBundle data") + } var balanceAvailable float64 - if _, ok := data[4].(float64); ok { - balanceAvailable = data[4].(float64) + if v, ok := data[4].(float64); ok { + balanceAvailable = v } wallet := WsWallet{ Type: data[0].(string), @@ -769,8 +848,8 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { case wsWalletUpdate: if data, ok := d[2].([]interface{}); ok && len(data) > 0 { var balanceAvailable float64 - if _, ok := data[4].(float64); ok { - balanceAvailable = data[4].(float64) + if v, ok := data[4].(float64); ok { + balanceAvailable = v } b.Websocket.DataHandler <- WsWallet{ Type: data[0].(string), @@ -791,7 +870,10 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { if data, ok := d[2].([]interface{}); ok && len(data) > 0 { if data[0].(string) == "base" { if infoBase, ok := d[2].([]interface{}); ok && len(infoBase) > 0 { - baseData := data[1].([]interface{}) + baseData, ok := data[1].([]interface{}) + if !ok { + return errors.New("unable to type assert wsMarginInfoUpdate baseData") + } b.Websocket.DataHandler <- WsMarginInfoBase{ UserProfitLoss: baseData[0].(float64), UserSwaps: baseData[1].(float64), @@ -804,7 +886,10 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { case wsFundingInfoUpdate: if data, ok := d[2].([]interface{}); ok && len(data) > 0 { if data[0].(string) == "sym" { - symbolData := data[1].([]interface{}) + symbolData, ok := data[1].([]interface{}) + if !ok { + return errors.New("unable to type assert wsFundingInfoUpdate symbolData") + } b.Websocket.DataHandler <- WsFundingInfo{ YieldLoan: symbolData[0].(float64), YieldLend: symbolData[1].(float64), @@ -839,53 +924,86 @@ func (b *Bitfinex) wsHandleData(respRaw []byte) error { func (b *Bitfinex) wsHandleFundingOffer(data []interface{}) { var fo WsFundingOffer + if data[0] != nil { - fo.ID = int64(data[0].(float64)) + if id, ok := data[0].(float64); ok { + fo.ID = int64(id) + } } if data[1] != nil { - fo.Symbol = data[1].(string)[1:] + if sym, ok := data[1].(string); ok { + fo.Symbol = sym[1:] + } } if data[2] != nil { - fo.Created = int64(data[2].(float64)) + if created, ok := data[2].(float64); ok { + fo.Created = int64(created) + } } if data[3] != nil { - fo.Updated = int64(data[0].(float64)) + if updated, ok := data[3].(float64); ok { + fo.Updated = int64(updated) + } } if data[15] != nil { - fo.Period = int64(data[15].(float64)) + if period, ok := data[15].(float64); ok { + fo.Period = int64(period) + } } if data[4] != nil { - fo.Amount = data[4].(float64) + if amount, ok := data[4].(float64); ok { + fo.Amount = amount + } } if data[5] != nil { - fo.OriginalAmount = data[5].(float64) + if origAmount, ok := data[5].(float64); ok { + fo.OriginalAmount = origAmount + } } if data[6] != nil { - fo.Type = data[6].(string) + if fType, ok := data[6].(string); ok { + fo.Type = fType + } } if data[9] != nil { - fo.Flags = data[9].(float64) + if flags, ok := data[9].(float64); ok { + fo.Flags = flags + } } - if data[9] != nil { - fo.Status = data[10].(string) + if data[9] != nil && data[10] != nil { + if status, ok := data[10].(string); ok { + fo.Status = status + } } - if data[9] != nil { - fo.Rate = data[14].(float64) + if data[9] != nil && data[14] != nil { + if rate, ok := data[14].(float64); ok { + fo.Rate = rate + } } if data[16] != nil { - fo.Notify = data[16].(float64) == 1 + if notify, ok := data[16].(float64); ok { + fo.Notify = notify == 1 + } } if data[17] != nil { - fo.Hidden = data[17].(float64) == 1 + if hidden, ok := data[17].(float64); ok { + fo.Hidden = hidden == 1 + } } if data[18] != nil { - fo.Insure = data[18].(float64) == 1 + if insure, ok := data[18].(float64); ok { + fo.Insure = insure == 1 + } } if data[19] != nil { - fo.Renew = data[19].(float64) == 1 + if renew, ok := data[19].(float64); ok { + fo.Renew = renew == 1 + } } if data[20] != nil { - fo.RateReal = data[20].(float64) + if rateReal, ok := data[20].(float64); ok { + fo.RateReal = rateReal + } } b.Websocket.DataHandler <- fo @@ -896,54 +1014,74 @@ func (b *Bitfinex) wsHandleOrder(data []interface{}) { var err error od.Exchange = b.Name if data[0] != nil { - od.ID = strconv.FormatFloat(data[0].(float64), 'f', -1, 64) + if id, ok := data[0].(float64); ok { + od.ID = strconv.FormatFloat(id, 'f', -1, 64) + } } if data[16] != nil { - od.Price = data[16].(float64) + if price, ok := data[16].(float64); ok { + od.Price = price + } } if data[7] != nil { - od.Amount = data[7].(float64) + if amount, ok := data[7].(float64); ok { + od.Amount = amount + } } if data[6] != nil { - od.RemainingAmount = data[6].(float64) + if remainingAmount, ok := data[6].(float64); ok { + od.RemainingAmount = remainingAmount + } } if data[7] != nil && data[6] != nil { - od.ExecutedAmount = data[7].(float64) - data[6].(float64) + if executedAmount, ok := data[7].(float64); ok { + od.ExecutedAmount = executedAmount - od.RemainingAmount + } } if data[4] != nil { - od.Date = time.Unix(int64(data[4].(float64))*1000, 0) + if date, ok := data[4].(float64); ok { + od.Date = time.Unix(int64(date)*1000, 0) + } } if data[5] != nil { - od.LastUpdated = time.Unix(int64(data[5].(float64))*1000, 0) + if lastUpdated, ok := data[5].(float64); ok { + od.LastUpdated = time.Unix(int64(lastUpdated)*1000, 0) + } } if data[2] != nil { - od.Pair, od.AssetType, err = b.GetRequestFormattedPairAndAssetType(data[3].(string)[1:]) - if err != nil { - b.Websocket.DataHandler <- err - return + if p, ok := data[3].(string); ok { + od.Pair, od.AssetType, err = b.GetRequestFormattedPairAndAssetType(p[1:]) + if err != nil { + b.Websocket.DataHandler <- err + return + } } } if data[8] != nil { - oType, err := order.StringToOrderType(data[8].(string)) - if err != nil { - b.Websocket.DataHandler <- order.ClassificationError{ - Exchange: b.Name, - OrderID: od.ID, - Err: err, + if ordType, ok := data[8].(string); ok { + oType, err := order.StringToOrderType(ordType) + if err != nil { + b.Websocket.DataHandler <- order.ClassificationError{ + Exchange: b.Name, + OrderID: od.ID, + Err: err, + } } + od.Type = oType } - od.Type = oType } if data[13] != nil { - oStatus, err := order.StringToOrderStatus(data[13].(string)) - if err != nil { - b.Websocket.DataHandler <- order.ClassificationError{ - Exchange: b.Name, - OrderID: od.ID, - Err: err, + if ordStatus, ok := data[13].(string); ok { + oStatus, err := order.StringToOrderStatus(ordStatus) + if err != nil { + b.Websocket.DataHandler <- order.ClassificationError{ + Exchange: b.Name, + OrderID: od.ID, + Err: err, + } } + od.Status = oStatus } - od.Status = oStatus } b.Websocket.DataHandler <- &od } @@ -1242,19 +1380,45 @@ func (b *Bitfinex) WsNewOrder(data *WsNewOrderRequest) (string, error) { if err != nil { return "", err } - responseDataDetail := respData[2].([]interface{}) - responseOrderDetail := responseDataDetail[4].([]interface{}) - var orderID string - if responseOrderDetail[0] != nil && responseOrderDetail[0].(float64) > 0 { - orderID = strconv.FormatFloat(responseOrderDetail[0].(float64), 'f', -1, 64) - } - errCode := responseDataDetail[6].(string) - errorMessage := responseDataDetail[7].(string) + if len(respData) < 3 { + return "", errors.New("unexpected respData length") + } + responseDataDetail, ok := respData[2].([]interface{}) + if !ok { + return "", errors.New("unable to type assert respData") + } + + if len(responseDataDetail) < 4 { + return "", errors.New("invalid responseDataDetail length") + } + + responseOrderDetail, ok := responseDataDetail[4].([]interface{}) + if !ok { + return "", errors.New("unable to type assert responseOrderDetail") + } + var orderID string + if responseOrderDetail[0] != nil { + if ordID, ordOK := responseOrderDetail[0].(float64); ordOK && ordID > 0 { + orderID = strconv.FormatFloat(ordID, 'f', -1, 64) + } + } + var errorMessage, errCode string + if len(responseDataDetail) > 6 { + errCode, ok = responseDataDetail[6].(string) + if !ok { + return "", errors.New("unable to type assert errCode") + } + } + if len(responseDataDetail) > 7 { + errorMessage, ok = responseDataDetail[7].(string) + if !ok { + return "", errors.New("unable to type assert errorMessage") + } + } if strings.EqualFold(errCode, wsError) { return orderID, errors.New(b.Name + " - " + errCode + ": " + errorMessage) } - return orderID, nil } @@ -1274,13 +1438,29 @@ func (b *Bitfinex) WsModifyOrder(data *WsUpdateOrderRequest) error { if err != nil { return err } - responseOrderData := responseData[2].([]interface{}) - errCode := responseOrderData[6].(string) - errorMessage := responseOrderData[7].(string) + if len(responseData) < 3 { + return errors.New("unexpected responseData length") + } + responseOrderData, ok := responseData[2].([]interface{}) + if !ok { + return errors.New("unable to type assert responseOrderData") + } + var errorMessage, errCode string + if len(responseOrderData) > 6 { + errCode, ok = responseOrderData[6].(string) + if !ok { + return errors.New("unable to type assert errCode") + } + } + if len(responseOrderData) > 7 { + errorMessage, ok = responseOrderData[7].(string) + if !ok { + return errors.New("unable to type assert errorMessage") + } + } if strings.EqualFold(errCode, wsError) { return errors.New(b.Name + " - " + errCode + ": " + errorMessage) } - return nil } @@ -1311,13 +1491,29 @@ func (b *Bitfinex) WsCancelOrder(orderID int64) error { if err != nil { return err } - responseOrderData := responseData[2].([]interface{}) - errCode := responseOrderData[6].(string) - errorMessage := responseOrderData[7].(string) + if len(responseData) < 3 { + return errors.New("unexpected responseData length") + } + responseOrderData, ok := responseData[2].([]interface{}) + if !ok { + return errors.New("unable to type assert responseOrderData") + } + var errorMessage, errCode string + if len(responseOrderData) > 6 { + errCode, ok = responseOrderData[6].(string) + if !ok { + return errors.New("unable to type assert errCode") + } + } + if len(responseOrderData) > 7 { + errorMessage, ok = responseOrderData[7].(string) + if !ok { + return errors.New("unable to type assert errorMessage") + } + } if strings.EqualFold(errCode, wsError) { return errors.New(b.Name + " - " + errCode + ": " + errorMessage) } - return nil } @@ -1352,11 +1548,25 @@ func (b *Bitfinex) WsCancelOffer(orderID int64) error { if err != nil { return err } - responseOrderData := responseData[2].([]interface{}) - errCode := responseOrderData[6].(string) - var errorMessage string - if responseOrderData[7] != nil { - errorMessage = responseOrderData[7].(string) + if len(responseData) < 3 { + return errors.New("unexpected responseData length") + } + responseOrderData, ok := responseData[2].([]interface{}) + if !ok { + return errors.New("unable to type assert responseOrderData") + } + var errorMessage, errCode string + if len(responseOrderData) > 6 { + errCode, ok = responseOrderData[6].(string) + if !ok { + return errors.New("unable to type assert errCode") + } + } + if len(responseOrderData) > 7 { + errorMessage, ok = responseOrderData[7].(string) + if !ok { + return errors.New("unable to type assert errorMessage") + } } if strings.EqualFold(errCode, wsError) { return errors.New(b.Name + " - " + errCode + ": " + errorMessage) diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index aa804176..ce780024 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -365,8 +365,7 @@ func (b *Bitfinex) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (b *Bitfinex) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := b.UpdateTickers(ctx, a) - if err != nil { + if err := b.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(b.Name, p, a) @@ -567,7 +566,7 @@ allTrades: return nil, err } for i := range tradeData { - tradeTS := time.Unix(0, tradeData[i].Timestamp*int64(time.Millisecond)) + tradeTS := time.UnixMilli(tradeData[i].Timestamp) if tradeTS.Before(timestampStart) && !timestampStart.IsZero() { break allTrades } @@ -579,7 +578,7 @@ allTrades: AssetType: assetType, Price: tradeData[i].Price, Amount: tradeData[i].Amount, - Timestamp: time.Unix(0, tradeData[i].Timestamp*int64(time.Millisecond)), + Timestamp: time.UnixMilli(tradeData[i].Timestamp), }) if i == len(tradeData)-1 { if ts.Equal(tradeTS) { diff --git a/exchanges/bithumb/bithumb.go b/exchanges/bithumb/bithumb.go index 7111035b..66486fba 100644 --- a/exchanges/bithumb/bithumb.go +++ b/exchanges/bithumb/bithumb.go @@ -236,7 +236,11 @@ func (b *Bithumb) GetAccountBalance(ctx context.Context, c string) (FullBalance, return fullBalance, err } } else { - val = datum.(float64) + var ok bool + val, ok = datum.(float64) + if !ok { + return fullBalance, errors.New("unable to type assert datum") + } } switch splitTag[0] { @@ -529,7 +533,7 @@ func (b *Bithumb) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange. var intermediary json.RawMessage err = b.SendPayload(ctx, request.Auth, func() (*request.Item, error) { // This is time window sensitive - tnMS := time.Now().UnixNano() / int64(time.Millisecond) + tnMS := time.Now().UnixMilli() n := strconv.FormatInt(tnMS, 10) params.Set("endpoint", path) diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 1fa66f66..f5575b59 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -291,8 +291,7 @@ func (b *Bithumb) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (b *Bithumb) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := b.UpdateTickers(ctx, a) - if err != nil { + if err := b.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(b.Name, p, a) @@ -818,60 +817,33 @@ func (b *Bithumb) GetHistoricCandles(ctx context.Context, pair currency.Pair, a } for x := range candle.Data { + if len(candle.Data[x]) < 6 { + return kline.Item{}, errors.New("invalid candle length") + } var tempCandle kline.Candle - - tempTime := candle.Data[x][0].(float64) - timestamp := time.Unix(0, int64(tempTime)*int64(time.Millisecond)) - if timestamp.Before(start) { + if tempCandle.Time, err = convert.TimeFromUnixTimestampFloat(candle.Data[x][0]); err != nil { + return kline.Item{}, fmt.Errorf("unable to convert timestamp: %w", err) + } + if tempCandle.Time.Before(start) { continue } - if timestamp.After(end) { + if tempCandle.Time.After(end) { break } - tempCandle.Time = timestamp - - open, ok := candle.Data[x][1].(string) - if !ok { - return kline.Item{}, errors.New("open conversion failed") + if tempCandle.Open, err = convert.FloatFromString(candle.Data[x][1]); err != nil { + return kline.Item{}, fmt.Errorf("kline open conversion failed: %w", err) } - tempCandle.Open, err = strconv.ParseFloat(open, 64) - if err != nil { - return kline.Item{}, err + if tempCandle.High, err = convert.FloatFromString(candle.Data[x][2]); err != nil { + return kline.Item{}, fmt.Errorf("kline high conversion failed: %w", err) } - high, ok := candle.Data[x][2].(string) - if !ok { - return kline.Item{}, errors.New("high conversion failed") + if tempCandle.Low, err = convert.FloatFromString(candle.Data[x][3]); err != nil { + return kline.Item{}, fmt.Errorf("kline low conversion failed: %w", err) } - tempCandle.High, err = strconv.ParseFloat(high, 64) - if err != nil { - return kline.Item{}, err + if tempCandle.Close, err = convert.FloatFromString(candle.Data[x][4]); err != nil { + return kline.Item{}, fmt.Errorf("kline close conversion failed: %w", err) } - - low, ok := candle.Data[x][3].(string) - if !ok { - return kline.Item{}, errors.New("low conversion failed") - } - tempCandle.Low, err = strconv.ParseFloat(low, 64) - if err != nil { - return kline.Item{}, err - } - - closeTemp, ok := candle.Data[x][4].(string) - if !ok { - return kline.Item{}, errors.New("close conversion failed") - } - tempCandle.Close, err = strconv.ParseFloat(closeTemp, 64) - if err != nil { - return kline.Item{}, err - } - - vol, ok := candle.Data[x][5].(string) - if !ok { - return kline.Item{}, errors.New("vol conversion failed") - } - tempCandle.Volume, err = strconv.ParseFloat(vol, 64) - if err != nil { - return kline.Item{}, err + if tempCandle.Volume, err = convert.FloatFromString(candle.Data[x][5]); err != nil { + return kline.Item{}, fmt.Errorf("kline volume conversion failed: %w", err) } ret.Candles = append(ret.Candles, tempCandle) } diff --git a/exchanges/bithumb/bithumb_ws_orderbook.go b/exchanges/bithumb/bithumb_ws_orderbook.go index b00cec7c..4486b767 100644 --- a/exchanges/bithumb/bithumb_ws_orderbook.go +++ b/exchanges/bithumb/bithumb_ws_orderbook.go @@ -442,7 +442,7 @@ func (b *Bithumb) SeedLocalCacheWithBook(p currency.Pair, o *Orderbook) error { newOrderBook.Pair = p newOrderBook.Asset = asset.Spot newOrderBook.Exchange = b.Name - newOrderBook.LastUpdated = time.Unix(0, o.Data.Timestamp*int64(time.Millisecond)) + newOrderBook.LastUpdated = time.UnixMilli(o.Data.Timestamp) newOrderBook.VerifyOrderbook = b.CanVerifyOrderbook return b.Websocket.Orderbook.LoadSnapshot(&newOrderBook) } diff --git a/exchanges/bithumb/convert_test.go b/exchanges/bithumb/convert_test.go index d5afa0fb..74326af3 100644 --- a/exchanges/bithumb/convert_test.go +++ b/exchanges/bithumb/convert_test.go @@ -20,7 +20,7 @@ func TestBithumbTime(t *testing.T) { tt := newTime.Time() if tt.UTC().String() != "2021-08-12 03:39:50 +0000 UTC" { - t.Fatalf("expected: %s but receieved: %s", + t.Fatalf("expected: %s but received: %s", "2021-08-12 03:39:50 +0000 UTC", tt.UTC().String()) } diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index 6980d92c..c7b67565 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -320,8 +320,7 @@ func (b *Bitmex) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (b *Bitmex) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := b.UpdateTickers(ctx, a) - if err != nil { + if err := b.UpdateTickers(ctx, a); err != nil { return nil, err } diff --git a/exchanges/bittrex/bittrex.go b/exchanges/bittrex/bittrex.go index 17847111..111ac464 100644 --- a/exchanges/bittrex/bittrex.go +++ b/exchanges/bittrex/bittrex.go @@ -370,7 +370,7 @@ func (b *Bittrex) SendAuthHTTPRequest(ctx context.Context, ep exchange.URL, meth } newRequest := func() (*request.Item, error) { - ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) + ts := strconv.FormatInt(time.Now().UnixMilli(), 10) path := common.EncodeURLValues(action, params) var body io.Reader diff --git a/exchanges/bittrex/bittrex_test.go b/exchanges/bittrex/bittrex_test.go index 22595efa..49f84b4a 100644 --- a/exchanges/bittrex/bittrex_test.go +++ b/exchanges/bittrex/bittrex_test.go @@ -568,6 +568,7 @@ func TestModifyOrder(t *testing.T) { } func WithdrawCryptocurrencyFunds(t *testing.T) { + t.Helper() withdrawCryptoRequest := withdraw.Request{ Amount: -1, Currency: currency.BTC, diff --git a/exchanges/bittrex/bittrex_websocket.go b/exchanges/bittrex/bittrex_websocket.go index 479fbac6..907a1af3 100644 --- a/exchanges/bittrex/bittrex_websocket.go +++ b/exchanges/bittrex/bittrex_websocket.go @@ -156,7 +156,7 @@ func (b *Bittrex) WsAuth() error { if err != nil { return err } - timestamp := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) + timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10) hmac, err := crypto.GetHMAC( crypto.HashSHA512, []byte(timestamp+randomContent.String()), diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index 44d04d24..3791546f 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -950,8 +950,8 @@ func (b *Bittrex) GetHistoricCandles(ctx context.Context, pair currency.Pair, a year, month, day := start.Date() curYear, curMonth, curDay := time.Now().Date() - getHistoric := false - getRecent := false + getHistoric := false // nolint:ifshort,nolintlint // false positive and triggers only on Windows + getRecent := false // nolint:ifshort,nolintlint // false positive and triggers only on Windows switch interval { case kline.OneMin, kline.FiveMin: diff --git a/exchanges/btcmarkets/btcmarkets.go b/exchanges/btcmarkets/btcmarkets.go index e9802fc8..12de4bf9 100644 --- a/exchanges/btcmarkets/btcmarkets.go +++ b/exchanges/btcmarkets/btcmarkets.go @@ -704,7 +704,7 @@ func (b *BTCMarkets) SendAuthenticatedRequest(ctx context.Context, method, path newRequest := func() (*request.Item, error) { now := time.Now() - strTime := strconv.FormatInt(now.UTC().UnixNano()/1000000, 10) + strTime := strconv.FormatInt(now.UTC().UnixMilli(), 10) var body io.Reader var payload, hmac []byte diff --git a/exchanges/btcmarkets/btcmarkets_websocket.go b/exchanges/btcmarkets/btcmarkets_websocket.go index 14ace369..a7d88a14 100644 --- a/exchanges/btcmarkets/btcmarkets_websocket.go +++ b/exchanges/btcmarkets/btcmarkets_websocket.go @@ -343,7 +343,7 @@ func (b *BTCMarkets) Subscribe(channelsToSubscribe []stream.ChannelSubscription) if !common.StringDataCompare(payload.Channels, authChannels[i]) { continue } - signTime := strconv.FormatInt(time.Now().UTC().UnixNano()/1000000, 10) + signTime := strconv.FormatInt(time.Now().UTC().UnixMilli(), 10) strToSign := "/users/self/subscribe" + "\n" + signTime tempSign, err := crypto.GetHMAC(crypto.HashSHA512, []byte(strToSign), diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 68c9f029..5579a83a 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -332,8 +332,7 @@ func (b *BTCMarkets) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (b *BTCMarkets) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := b.UpdateTickers(ctx, a) - if err != nil { + if err := b.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(b.Name, p, a) diff --git a/exchanges/btse/btse.go b/exchanges/btse/btse.go index a543bc66..c333f964 100644 --- a/exchanges/btse/btse.go +++ b/exchanges/btse/btse.go @@ -477,7 +477,7 @@ func (b *BTSE) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange.URL var hmac []byte var body io.Reader - nonce := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10) + nonce := strconv.FormatInt(time.Now().UnixMilli(), 10) headers := map[string]string{ "btse-api": b.API.Credentials.Key, "btse-nonce": nonce, diff --git a/exchanges/btse/btse_test.go b/exchanges/btse/btse_test.go index 89b07f90..81743b95 100644 --- a/exchanges/btse/btse_test.go +++ b/exchanges/btse/btse_test.go @@ -543,12 +543,11 @@ func TestGetFee(t *testing.T) { } func TestParseOrderTime(t *testing.T) { - expected := int64(1534792846) actual, err := parseOrderTime("2018-08-20 19:20:46") if err != nil { t.Fatal(err) } - if expected != actual.Unix() { + if expected := int64(1534792846); expected != actual.Unix() { t.Errorf("TestParseOrderTime expected: %d, got %d", expected, actual.Unix()) } } @@ -920,8 +919,7 @@ func TestWsLogin(t *testing.T) { func TestWsSubscription(t *testing.T) { t.Parallel() data := []byte(`{"event":"subscribe","channel":["orderBookL2Api:SFI-ETH_0","tradeHistory:SFI-ETH"]}`) - err := b.wsHandleData(data) - if err != nil { + if err := b.wsHandleData(data); err != nil { t.Error(err) } } diff --git a/exchanges/btse/btse_websocket.go b/exchanges/btse/btse_websocket.go index 7ce64984..daf062c0 100644 --- a/exchanges/btse/btse_websocket.go +++ b/exchanges/btse/btse_websocket.go @@ -57,7 +57,7 @@ func (b *BTSE) WsConnect() error { // WsAuthenticate Send an authentication message to receive auth data func (b *BTSE) WsAuthenticate() error { - nonce := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10) + nonce := strconv.FormatInt(time.Now().UnixMilli(), 10) path := "/spotWS" + nonce hmac, err := crypto.GetHMAC(crypto.HashSHA512_384, @@ -216,7 +216,7 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { Side: oSide, Status: oStatus, AssetType: a, - Date: time.Unix(0, notification.Data[i].Timestamp*int64(time.Millisecond)), + Date: time.UnixMilli(notification.Data[i].Timestamp), Pair: p, } } @@ -250,7 +250,7 @@ func (b *BTSE) wsHandleData(respRaw []byte) error { return err } trades = append(trades, trade.Data{ - Timestamp: time.Unix(0, tradeHistory.Data[x].TransactionTime*int64(time.Millisecond)), + Timestamp: time.UnixMilli(tradeHistory.Data[x].TransactionTime), CurrencyPair: p, AssetType: a, Exchange: b.Name, diff --git a/exchanges/btse/btse_wrapper.go b/exchanges/btse/btse_wrapper.go index 36907ad2..7fadb951 100644 --- a/exchanges/btse/btse_wrapper.go +++ b/exchanges/btse/btse_wrapper.go @@ -313,8 +313,7 @@ func (b *BTSE) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (b *BTSE) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := b.UpdateTickers(ctx, a) - if err != nil { + if err := b.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(b.Name, p, a) @@ -589,7 +588,7 @@ func (b *BTSE) CancelAllOrders(ctx context.Context, orderCancellation *order.Can allOrders, err := b.CancelExistingOrder(ctx, "", fPair.String(), "") if err != nil { - return resp, nil + return resp, err } resp.Status = make(map[string]string) diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index 05bf9e8d..3afc7606 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -432,7 +432,10 @@ func (c *CoinbasePro) UpdateOrderbook(ctx context.Context, p currency.Pair, asse return book, err } - obNew := orderbookNew.(OrderbookL1L2) + obNew, ok := orderbookNew.(OrderbookL1L2) + if !ok { + return book, errors.New("unable to type assert orderbook data") + } for x := range obNew.Bids { book.Bids = append(book.Bids, orderbook.Item{ Amount: obNew.Bids[x].Amount, diff --git a/exchanges/coinbene/coinbene_websocket.go b/exchanges/coinbene/coinbene_websocket.go index 82392741..ec491511 100644 --- a/exchanges/coinbene/coinbene_websocket.go +++ b/exchanges/coinbene/coinbene_websocket.go @@ -443,8 +443,7 @@ func (c *Coinbene) getCurrencyFromWsTopic(assetType asset.Item, channelTopic str // Subscribe sends a websocket message to receive data from the channel func (c *Coinbene) Subscribe(channelsToSubscribe []stream.ChannelSubscription) error { - maxSubsPerHour := 240 - if len(channelsToSubscribe) > maxSubsPerHour { + if maxSubsPerHour := 240; len(channelsToSubscribe) > maxSubsPerHour { return fmt.Errorf("channel subscriptions length %d exceeds coinbene's limit of %d, try reducing enabled pairs", len(channelsToSubscribe), maxSubsPerHour) diff --git a/exchanges/coinbene/coinbene_wrapper.go b/exchanges/coinbene/coinbene_wrapper.go index de5935c0..a869f6a7 100644 --- a/exchanges/coinbene/coinbene_wrapper.go +++ b/exchanges/coinbene/coinbene_wrapper.go @@ -382,8 +382,7 @@ func (c *Coinbene) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (c *Coinbene) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := c.UpdateTickers(ctx, a) - if err != nil { + if err := c.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(c.Name, p, a) @@ -893,11 +892,17 @@ func (c *Coinbene) GetHistoricCandles(ctx context.Context, pair currency.Pair, a } for x := range candles.Data { + if len(candles.Data[x]) < 6 { + return kline.Item{}, errors.New("unexpected candle data length") + } var tempCandle kline.Candle - tempTime := candles.Data[x][0].(string) + tempTime, ok := candles.Data[x][0].(string) + if !ok { + return kline.Item{}, errors.New("timestamp conversion failed") + } timestamp, err := time.Parse(time.RFC3339, tempTime) if err != nil { - continue + return kline.Item{}, err } tempCandle.Time = timestamp open, ok := candles.Data[x][1].(string) diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index f347684b..4670e6cd 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -438,8 +438,7 @@ func (i *instrumentMap) Seed(curr string, id int64) { } // check to see if the instrument already exists - _, ok := i.Instruments[curr] - if ok { + if _, ok := i.Instruments[curr]; ok { return } diff --git a/exchanges/coinut/coinut_test.go b/exchanges/coinut/coinut_test.go index 64b77bd8..6e86f3e2 100644 --- a/exchanges/coinut/coinut_test.go +++ b/exchanges/coinut/coinut_test.go @@ -59,6 +59,7 @@ func TestMain(m *testing.M) { } func setupWSTestAuth(t *testing.T) { + t.Helper() if wsSetupRan { return } @@ -430,8 +431,7 @@ func TestGetDepositAddress(t *testing.T) { // TestWsAuthGetAccountBalance dials websocket, retrieves account balance func TestWsAuthGetAccountBalance(t *testing.T) { setupWSTestAuth(t) - _, err := c.wsGetAccountBalance() - if err != nil { + if _, err := c.wsGetAccountBalance(); err != nil { t.Error(err) } } @@ -449,8 +449,7 @@ func TestWsAuthSubmitOrder(t *testing.T) { Price: 1, Side: order.Buy, } - _, err := c.wsSubmitOrder(&ord) - if err != nil { + if _, err := c.wsSubmitOrder(&ord); err != nil { t.Error(err) } } diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index 4f65da57..1d2a3ed7 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -630,18 +630,31 @@ func (c *COINUT) SubmitOrder(ctx context.Context, o *order.Submit) (order.Submit if err != nil { return submitOrderResponse, err } - responseMap := APIResponse.(map[string]interface{}) - switch responseMap["reply"].(string) { + responseMap, ok := APIResponse.(map[string]interface{}) + if !ok { + return submitOrderResponse, errors.New("unable to type assert responseMap") + } + orderType, ok := responseMap["reply"].(string) + if !ok { + return submitOrderResponse, errors.New("unable to type assert orderType") + } + switch orderType { case "order_rejected": return submitOrderResponse, fmt.Errorf("clientOrderID: %v was rejected: %v", o.ClientID, responseMap["reasons"]) case "order_filled": - orderID := responseMap["order_id"].(float64) + orderID, ok := responseMap["order_id"].(float64) + if !ok { + return submitOrderResponse, errors.New("unable to type assert orderID") + } submitOrderResponse.OrderID = strconv.FormatFloat(orderID, 'f', -1, 64) submitOrderResponse.IsOrderPlaced = true submitOrderResponse.FullyMatched = true return submitOrderResponse, nil case "order_accepted": - orderID := responseMap["order_id"].(float64) + orderID, ok := responseMap["order_id"].(float64) + if !ok { + return submitOrderResponse, errors.New("unable to type assert orderID") + } submitOrderResponse.OrderID = strconv.FormatFloat(orderID, 'f', -1, 64) submitOrderResponse.IsOrderPlaced = true return submitOrderResponse, nil diff --git a/exchanges/exchange.go b/exchanges/exchange.go index bdd11293..e105915f 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -409,7 +409,7 @@ func (b *Base) GetPairFormat(assetType asset.Item, requestFormat bool) (currency func (b *Base) GetEnabledPairs(a asset.Item) (currency.Pairs, error) { err := b.CurrencyPairs.IsAssetEnabled(a) if err != nil { - return nil, nil + return nil, nil // nolint:nilerr // non-fatal error } format, err := b.GetPairFormat(a, false) if err != nil { @@ -1156,7 +1156,7 @@ func (b *Base) FormatExchangeKlineInterval(in kline.Interval) string { // ValidateKline confirms that the requested pair, asset & interval are supported and/or enabled by the requested exchange func (b *Base) ValidateKline(pair currency.Pair, a asset.Item, interval kline.Interval) error { var errorList []string - var err kline.ErrorKline + var err kline.Error if b.CurrencyPairs.IsAssetEnabled(a) != nil { err.Asset = a errorList = append(errorList, "asset not enabled") @@ -1242,7 +1242,7 @@ func (e *Endpoints) SetRunning(key, val string) error { key, val, e.Exchange) - return nil + return nil // nolint:nilerr // non-fatal error as we won't update the running URL } e.defaults[key] = val return nil diff --git a/exchanges/exchange_test.go b/exchanges/exchange_test.go index bc56aa63..a35cf132 100644 --- a/exchanges/exchange_test.go +++ b/exchanges/exchange_test.go @@ -752,8 +752,7 @@ func TestGetName(t *testing.T) { Name: "TESTNAME", } - name := b.GetName() - if name != "TESTNAME" { + if name := b.GetName(); name != "TESTNAME" { t.Error("Exchange GetName() returned incorrect name") } } @@ -1167,8 +1166,7 @@ func TestFormatExchangeCurrencies(t *testing.T) { if err != nil { t.Errorf("Exchange TestFormatExchangeCurrencies error %s", err) } - expected := "btc~usd^ltc~btc" - if actual != expected { + if expected := "btc~usd^ltc~btc"; actual != expected { t.Errorf("Exchange TestFormatExchangeCurrencies %s != %s", actual, expected) } @@ -1801,8 +1799,7 @@ func TestGetBase(t *testing.T) { func TestGetAssetType(t *testing.T) { var b Base p := currency.NewPair(currency.BTC, currency.USD) - _, err := b.GetPairAssetType(p) - if err == nil { + if _, err := b.GetPairAssetType(p); err == nil { t.Fatal("error cannot be nil") } b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore) @@ -2410,6 +2407,7 @@ func TestAssetWebsocketFunctionality(t *testing.T) { } func TestGetGetURLTypeFromString(t *testing.T) { + t.Parallel() testCases := []struct { Endpoint string Expected URL diff --git a/exchanges/exmo/exmo.go b/exchanges/exmo/exmo.go index 6467485a..31723300 100644 --- a/exchanges/exmo/exmo.go +++ b/exchanges/exmo/exmo.go @@ -213,7 +213,11 @@ func (e *EXMO) GetCryptoDepositAddress(ctx context.Context) (map[string]string, case map[string]interface{}: mapString := make(map[string]string) for key, value := range r { - mapString[key] = value.(string) + v, ok := value.(string) + if !ok { + return nil, errors.New("unable to type assert value data") + } + mapString[key] = v } return mapString, nil diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index bf8eb2ab..beb76265 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -219,8 +219,7 @@ func (e *EXMO) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (e *EXMO) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := e.UpdateTickers(ctx, a) - if err != nil { + if err := e.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(e.Name, p, a) diff --git a/exchanges/ftx/ftx.go b/exchanges/ftx/ftx.go index adaf1c47..c070cfbe 100644 --- a/exchanges/ftx/ftx.go +++ b/exchanges/ftx/ftx.go @@ -1151,7 +1151,7 @@ func (f *FTX) SendAuthHTTPRequest(ctx context.Context, ep exchange.URL, method, } newRequest := func() (*request.Item, error) { - ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) + ts := strconv.FormatInt(time.Now().UnixMilli(), 10) var body io.Reader var hmac, payload []byte diff --git a/exchanges/ftx/ftx_test.go b/exchanges/ftx/ftx_test.go index e483abe2..97ea0c47 100644 --- a/exchanges/ftx/ftx_test.go +++ b/exchanges/ftx/ftx_test.go @@ -1364,7 +1364,7 @@ func TestCompatibleOrderVars(t *testing.T) { t.Errorf("received %v expected %v", orderVars.Status, order.Filled) } - orderVars, err = f.compatibleOrderVars(context.Background(), + _, err = f.compatibleOrderVars(context.Background(), "buy", "closed", "limit", @@ -1375,7 +1375,7 @@ func TestCompatibleOrderVars(t *testing.T) { t.Errorf("received %v expected %v", err, errInvalidOrderAmounts) } - orderVars, err = f.compatibleOrderVars(context.Background(), + _, err = f.compatibleOrderVars(context.Background(), "buy", "fake", "limit", diff --git a/exchanges/ftx/ftx_websocket.go b/exchanges/ftx/ftx_websocket.go index 601ce1a0..ad0dfa02 100644 --- a/exchanges/ftx/ftx_websocket.go +++ b/exchanges/ftx/ftx_websocket.go @@ -77,7 +77,7 @@ func (f *FTX) WsConnect() error { // WsAuth sends an authentication message to receive auth data func (f *FTX) WsAuth() error { - intNonce := time.Now().UnixNano() / 1000000 + intNonce := time.Now().UnixMilli() strNonce := strconv.FormatInt(intNonce, 10) hmac, err := crypto.GetHMAC( crypto.HashSHA256, diff --git a/exchanges/ftx/ftx_websocket_test.go b/exchanges/ftx/ftx_websocket_test.go index 2e66f970..4986335d 100644 --- a/exchanges/ftx/ftx_websocket_test.go +++ b/exchanges/ftx/ftx_websocket_test.go @@ -251,8 +251,7 @@ func TestParsingOrders(t *testing.T) { }, "type": "update" }`) - err := f.wsHandleData(data) - if err != nil { + if err := f.wsHandleData(data); err != nil { t.Error(err) } } @@ -274,8 +273,7 @@ func TestParsingWSTradesData(t *testing.T) { } ] }`) - err := f.wsHandleData(data) - if err != nil { + if err := f.wsHandleData(data); err != nil { t.Error(err) } } @@ -295,8 +293,7 @@ func TestParsingWSTickerData(t *testing.T) { "time": 1589505004.4237103 } }`) - err := f.wsHandleData(data) - if err != nil { + if err := f.wsHandleData(data); err != nil { t.Error(err) } } @@ -323,8 +320,7 @@ func TestParsingWSOrdersData(t *testing.T) { }, "type": "update" }`) - err := f.wsHandleData(data) - if err != nil { + if err := f.wsHandleData(data); err != nil { t.Error(err) } } @@ -360,8 +356,7 @@ func TestParsingMarketsData(t *testing.T) { "group": "quarterly"}}}, "action": "partial" }`) - err := f.wsHandleData(data) - if err != nil { + if err := f.wsHandleData(data); err != nil { t.Error(err) } } diff --git a/exchanges/gateio/gateio.go b/exchanges/gateio/gateio.go index 8fb353ec..a6e559e1 100644 --- a/exchanges/gateio/gateio.go +++ b/exchanges/gateio/gateio.go @@ -52,9 +52,6 @@ func (g *Gateio) GetSymbols(ctx context.Context) ([]string, error) { var result []string urlPath := fmt.Sprintf("/%s/%s", gateioAPIVersion, gateioSymbol) err := g.SendHTTPRequest(ctx, exchange.RestSpotSupplementary, urlPath, &result) - if err != nil { - return nil, nil - } return result, err } @@ -76,9 +73,15 @@ func (g *Gateio) GetMarketInfo(ctx context.Context) (MarketInfoResponse, error) result.Result = res.Result for _, v := range res.Pairs { - item := v.(map[string]interface{}) + item, ok := v.(map[string]interface{}) + if !ok { + return result, errors.New("unable to type assert item") + } for itemk, itemv := range item { - pairv := itemv.(map[string]interface{}) + pairv, ok := itemv.(map[string]interface{}) + if !ok { + return result, errors.New("unable to type assert pairv") + } result.Pairs = append(result.Pairs, MarketInfoPairsResponse{ Symbol: itemk, DecimalPlaces: pairv["decimal_places"].(float64), @@ -203,28 +206,27 @@ func (g *Gateio) GetSpotKline(ctx context.Context, arg KlinesRequestParams) (kli arg.GroupSec, arg.HourSize) - var rawKlines map[string]interface{} - err := g.SendHTTPRequest(ctx, exchange.RestSpotSupplementary, urlPath, &rawKlines) - if err != nil { + resp := struct { + Data [][]string `json:"data"` + Result string `json:"result"` + }{} + + if err := g.SendHTTPRequest(ctx, exchange.RestSpotSupplementary, urlPath, &resp); err != nil { return kline.Item{}, err } + if resp.Result != "true" || len(resp.Data) == 0 { + return kline.Item{}, errors.New("rawKlines unexpected data returned") + } result := kline.Item{ Exchange: g.Name, } - if rawKlines == nil || rawKlines["data"] == nil { - return kline.Item{}, errors.New("rawKlines is nil") - } - - rawKlineDatasString, _ := json.Marshal(rawKlines["data"].([]interface{})) - var rawKlineDatas [][]interface{} - if err := json.Unmarshal(rawKlineDatasString, &rawKlineDatas); err != nil { - return kline.Item{}, fmt.Errorf("rawKlines unmarshal failed. Err: %s", err) - } - - for _, k := range rawKlineDatas { - otString, err := strconv.ParseFloat(k[0].(string), 64) + for x := range resp.Data { + if len(resp.Data[x]) < 6 { + return kline.Item{}, fmt.Errorf("unexpected kline data length") + } + otString, err := strconv.ParseFloat(resp.Data[x][0], 64) if err != nil { return kline.Item{}, err } @@ -232,23 +234,23 @@ func (g *Gateio) GetSpotKline(ctx context.Context, arg KlinesRequestParams) (kli if err != nil { return kline.Item{}, fmt.Errorf("cannot parse Kline.OpenTime. Err: %s", err) } - _vol, err := convert.FloatFromString(k[1]) + _vol, err := convert.FloatFromString(resp.Data[x][1]) if err != nil { return kline.Item{}, fmt.Errorf("cannot parse Kline.Volume. Err: %s", err) } - _close, err := convert.FloatFromString(k[2]) + _close, err := convert.FloatFromString(resp.Data[x][2]) if err != nil { return kline.Item{}, fmt.Errorf("cannot parse Kline.Close. Err: %s", err) } - _high, err := convert.FloatFromString(k[3]) + _high, err := convert.FloatFromString(resp.Data[x][3]) if err != nil { return kline.Item{}, fmt.Errorf("cannot parse Kline.High. Err: %s", err) } - _low, err := convert.FloatFromString(k[4]) + _low, err := convert.FloatFromString(resp.Data[x][4]) if err != nil { return kline.Item{}, fmt.Errorf("cannot parse Kline.Low. Err: %s", err) } - _open, err := convert.FloatFromString(k[5]) + _open, err := convert.FloatFromString(resp.Data[x][5]) if err != nil { return kline.Item{}, fmt.Errorf("cannot parse Kline.Open. Err: %s", err) } diff --git a/exchanges/gateio/gateio_test.go b/exchanges/gateio/gateio_test.go index 4a1ef237..27717dfe 100644 --- a/exchanges/gateio/gateio_test.go +++ b/exchanges/gateio/gateio_test.go @@ -544,14 +544,14 @@ func TestWsGetOrderInfo(t *testing.T) { } func setupWSTestAuth(t *testing.T) { + t.Helper() if wsSetupRan { return } if !g.Websocket.IsEnabled() && !g.API.AuthenticatedWebsocketSupport || !areTestAPIKeysSet() { t.Skip(stream.WebsocketNotEnabled) } - err := g.Websocket.Connect() - if err != nil { + if err := g.Websocket.Connect(); err != nil { t.Fatal(err) } wsSetupRan = true diff --git a/exchanges/gateio/gateio_websocket.go b/exchanges/gateio/gateio_websocket.go index b96a2f61..c33544bb 100644 --- a/exchanges/gateio/gateio_websocket.go +++ b/exchanges/gateio/gateio_websocket.go @@ -225,11 +225,22 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { if err != nil { return err } - invalidJSON := orderUpdate.Params[1].(map[string]interface{}) + if len(orderUpdate.Params) < 2 { + return errors.New("unexpected orderUpdate.Params data length") + } + invalidJSON, ok := orderUpdate.Params[1].(map[string]interface{}) + if !ok { + return errors.New("unable to type assert invalidJSON") + } oStatus := order.UnknownStatus oType := order.UnknownType oSide := order.UnknownSide - switch orderUpdate.Params[0].(float64) { + + orderStatus, ok := orderUpdate.Params[0].(float64) + if !ok { + return errors.New("unable to type assert orderStatus") + } + switch orderStatus { case 1: oStatus = order.New case 2: @@ -237,42 +248,57 @@ func (g *Gateio) wsHandleData(respRaw []byte) error { case 3: oStatus = order.Filled } - switch invalidJSON["orderType"].(float64) { + + orderType, ok := invalidJSON["orderType"].(float64) + if !ok { + return errors.New("unable to type assert orderType") + } + switch orderType { case 1: oType = order.Limit case 2: oType = order.Market } - switch invalidJSON["type"].(float64) { + + orderSide, ok := invalidJSON["type"].(float64) + if !ok { + return errors.New("unable to type assert orderSide") + } + switch orderSide { case 1: oSide = order.Sell case 2: oSide = order.Buy } + var price, amount, filledTotal, left, fee float64 - price, err = strconv.ParseFloat(invalidJSON["price"].(string), 64) + price, err = convert.FloatFromString(invalidJSON["price"]) if err != nil { return err } - amount, err = strconv.ParseFloat(invalidJSON["amount"].(string), 64) + amount, err = convert.FloatFromString(invalidJSON["amount"]) if err != nil { return err } - filledTotal, err = strconv.ParseFloat(invalidJSON["filledTotal"].(string), 64) + filledTotal, err = convert.FloatFromString(invalidJSON["filledTotal"]) if err != nil { return err } - left, err = strconv.ParseFloat(invalidJSON["left"].(string), 64) + left, err = convert.FloatFromString(invalidJSON["left"]) if err != nil { return err } - fee, err = strconv.ParseFloat(invalidJSON["dealFee"].(string), 64) + fee, err = convert.FloatFromString(invalidJSON["dealFee"]) if err != nil { return err } var p currency.Pair - p, err = currency.NewPairFromString(invalidJSON["market"].(string)) + pairStr, ok := invalidJSON["market"].(string) + if !ok { + return errors.New("unable to type assert market") + } + p, err = currency.NewPairFromString(pairStr) if err != nil { return err } diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index 3f1a9b1e..b27e144e 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -270,8 +270,7 @@ func (g *Gateio) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (g *Gateio) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := g.UpdateTickers(ctx, a) - if err != nil { + if err := g.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(g.Name, p, a) @@ -411,8 +410,7 @@ func (g *Gateio) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a } info.Exchange = g.Name - err := account.Process(&info) - if err != nil { + if err := account.Process(&info); err != nil { return account.Holdings{}, err } diff --git a/exchanges/gemini/gemini_mock_test.go b/exchanges/gemini/gemini_mock_test.go index 9bfa292a..b2ee314d 100644 --- a/exchanges/gemini/gemini_mock_test.go +++ b/exchanges/gemini/gemini_mock_test.go @@ -1,4 +1,5 @@ -//+build !mock_test_off +//go:build !mock_test_off +// +build !mock_test_off // This will build if build tag mock_test_off is not parsed and will try to mock // all tests in _test.go diff --git a/exchanges/gemini/gemini_websocket.go b/exchanges/gemini/gemini_websocket.go index 45f84ee9..b647b5f0 100644 --- a/exchanges/gemini/gemini_websocket.go +++ b/exchanges/gemini/gemini_websocket.go @@ -324,7 +324,7 @@ func (g *Gemini) wsHandleData(respRaw []byte) error { Side: oSide, Status: oStatus, AssetType: asset.Spot, - Date: time.Unix(0, result[i].Timestampms*int64(time.Millisecond)), + Date: time.UnixMilli(result[i].Timestampms), Pair: pair, } } diff --git a/exchanges/hitbtc/hitbtc_test.go b/exchanges/hitbtc/hitbtc_test.go index 7ba75bcf..6dec89ad 100644 --- a/exchanges/hitbtc/hitbtc_test.go +++ b/exchanges/hitbtc/hitbtc_test.go @@ -455,6 +455,7 @@ func TestGetDepositAddress(t *testing.T) { } } func setupWsAuth(t *testing.T) { + t.Helper() if wsSetupRan { return } @@ -524,8 +525,7 @@ func TestWsReplaceOrder(t *testing.T) { // TestWsGetActiveOrders dials websocket, sends get active orders request. func TestWsGetActiveOrders(t *testing.T) { setupWsAuth(t) - _, err := h.wsGetActiveOrders() - if err != nil { + if _, err := h.wsGetActiveOrders(); err != nil { t.Fatal(err) } } @@ -533,8 +533,7 @@ func TestWsGetActiveOrders(t *testing.T) { // TestWsGetTradingBalance dials websocket, sends get trading balance request. func TestWsGetTradingBalance(t *testing.T) { setupWsAuth(t) - _, err := h.wsGetTradingBalance() - if err != nil { + if _, err := h.wsGetTradingBalance(); err != nil { t.Fatal(err) } } diff --git a/exchanges/hitbtc/hitbtc_websocket.go b/exchanges/hitbtc/hitbtc_websocket.go index ee27c1fe..a5e54490 100644 --- a/exchanges/hitbtc/hitbtc_websocket.go +++ b/exchanges/hitbtc/hitbtc_websocket.go @@ -113,7 +113,10 @@ func (h *HitBTC) wsGetTableName(respRaw []byte) (string, error) { return "", nil } - data := resultType[0].(map[string]interface{}) + data, ok := resultType[0].(map[string]interface{}) + if !ok { + return "", errors.New("unable to type assert data") + } if _, ok := data["clientOrderId"]; ok { return "order", nil } else if _, ok := data["available"]; ok { diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index b7974a74..0f928afb 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -358,8 +358,7 @@ func (h *HitBTC) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (h *HitBTC) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := h.UpdateTickers(ctx, a) - if err != nil { + if err := h.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(h.Name, p, a) @@ -498,8 +497,8 @@ allTrades: p.String(), "", "", - ts.UnixNano()/int64(time.Millisecond), - timestampEnd.UnixNano()/int64(time.Millisecond), + ts.UnixMilli(), + timestampEnd.UnixMilli(), int64(limit), 0) if err != nil { diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index 37a208f7..f52a3004 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -57,6 +57,7 @@ func TestMain(m *testing.M) { } func setupWsTests(t *testing.T) { + t.Helper() if wsSetupRan { return } @@ -2196,8 +2197,7 @@ func TestQueryWithdrawQuota(t *testing.T) { // TestWsGetAccountsList connects to WS, logs in, gets account list func TestWsGetAccountsList(t *testing.T) { setupWsTests(t) - _, err := h.wsGetAccountsList() - if err != nil { + if _, err := h.wsGetAccountsList(); err != nil { t.Fatal(err) } } diff --git a/exchanges/huobi/huobi_websocket.go b/exchanges/huobi/huobi_websocket.go index 5e4c7836..e6d67677 100644 --- a/exchanges/huobi/huobi_websocket.go +++ b/exchanges/huobi/huobi_websocket.go @@ -351,7 +351,7 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { return err } h.Websocket.DataHandler <- stream.KlineData{ - Timestamp: time.Unix(0, kline.Timestamp*int64(time.Millisecond)), + Timestamp: time.UnixMilli(kline.Timestamp), Exchange: h.Name, AssetType: a, Pair: p, @@ -388,12 +388,11 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { Exchange: h.Name, AssetType: a, CurrencyPair: p, - Timestamp: time.Unix(0, - t.Tick.Data[i].Timestamp*int64(time.Millisecond)), - Amount: t.Tick.Data[i].Amount, - Price: t.Tick.Data[i].Price, - Side: side, - TID: strconv.FormatFloat(t.Tick.Data[i].TradeID, 'f', -1, 64), + Timestamp: time.UnixMilli(t.Tick.Data[i].Timestamp), + Amount: t.Tick.Data[i].Amount, + Price: t.Tick.Data[i].Price, + Side: side, + TID: strconv.FormatFloat(t.Tick.Data[i].TradeID, 'f', -1, 64), }) } return trade.AddTradesToBuffer(h.Name, trades...) @@ -427,7 +426,7 @@ func (h *HUOBI) wsHandleData(respRaw []byte) error { QuoteVolume: wsTicker.Tick.Volume, High: wsTicker.Tick.High, Low: wsTicker.Tick.Low, - LastUpdated: time.Unix(0, wsTicker.Timestamp*int64(time.Millisecond)), + LastUpdated: time.UnixMilli(wsTicker.Timestamp), AssetType: a, Pair: p, } diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index d193adab..2547edad 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -793,8 +793,7 @@ func (h *HUOBI) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (ac } acc.AssetType = assetType info.Accounts = append(info.Accounts, acc) - err := account.Process(&info) - if err != nil { + if err := account.Process(&info); err != nil { return info, err } return info, nil @@ -844,7 +843,7 @@ func (h *HUOBI) GetRecentTrades(ctx context.Context, p currency.Pair, assetType Side: side, Price: tradeData[i].Trades[j].Price, Amount: tradeData[i].Trades[j].Amount, - Timestamp: time.Unix(0, tradeData[i].Timestamp*int64(time.Millisecond)), + Timestamp: time.UnixMilli(tradeData[i].Timestamp), }) } } @@ -1183,7 +1182,7 @@ func (h *HUOBI) GetOrderInfo(ctx context.Context, orderID string, pair currency. Pair: p, Type: orderType, Side: orderSide, - Date: time.Unix(0, respData.CreatedAt*int64(time.Millisecond)), + Date: time.UnixMilli(respData.CreatedAt), Status: orderStatus, Price: respData.Price, Amount: respData.Amount, @@ -1349,7 +1348,7 @@ func (h *HUOBI) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest Pair: req.Pairs[i], Type: orderType, Side: orderSide, - Date: time.Unix(0, resp.Data[j].CreatedAt*int64(time.Millisecond)), + Date: time.UnixMilli(resp.Data[j].CreatedAt), Status: orderStatus, Price: resp.Data[j].Price, Amount: resp.Data[j].OrderAmount, @@ -1377,7 +1376,7 @@ func (h *HUOBI) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest Pair: req.Pairs[i], Exchange: h.Name, ExecutedAmount: resp[x].FilledAmount, - Date: time.Unix(0, resp[x].CreatedAt*int64(time.Millisecond)), + Date: time.UnixMilli(resp[x].CreatedAt), Status: order.Status(resp[x].State), AccountID: strconv.FormatInt(resp[x].AccountID, 10), Fee: resp[x].FilledFees, @@ -1389,7 +1388,7 @@ func (h *HUOBI) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest } case asset.CoinMarginedFutures: for x := range req.Pairs { - var currentPage int64 = 0 + var currentPage int64 for done := false; !done; { openOrders, err := h.GetSwapOpenOrders(ctx, req.Pairs[x], currentPage, 50) @@ -1429,7 +1428,7 @@ func (h *HUOBI) GetActiveOrders(ctx context.Context, req *order.GetOrdersRequest } case asset.Futures: for x := range req.Pairs { - var currentPage int64 = 0 + var currentPage int64 for done := false; !done; { openOrders, err := h.FGetOpenOrders(ctx, req.Pairs[x].Base, currentPage, 50) @@ -1508,7 +1507,7 @@ func (h *HUOBI) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest Pair: req.Pairs[i], Exchange: h.Name, ExecutedAmount: resp[x].FilledAmount, - Date: time.Unix(0, resp[x].CreatedAt*int64(time.Millisecond)), + Date: time.UnixMilli(resp[x].CreatedAt), Status: order.Status(resp[x].State), AccountID: strconv.FormatInt(resp[x].AccountID, 10), Fee: resp[x].FilledFees, @@ -1519,7 +1518,7 @@ func (h *HUOBI) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest } case asset.CoinMarginedFutures: for x := range req.Pairs { - var currentPage int64 = 0 + var currentPage int64 for done := false; !done; { orderHistory, err := h.GetSwapOrderHistory(ctx, req.Pairs[x], @@ -1570,7 +1569,7 @@ func (h *HUOBI) GetOrderHistory(ctx context.Context, req *order.GetOrdersRequest } case asset.Futures: for x := range req.Pairs { - var currentPage int64 = 0 + var currentPage int64 for done := false; !done; { openOrders, err := h.FGetOrderHistory(ctx, req.Pairs[x], diff --git a/exchanges/itbit/itbit.go b/exchanges/itbit/itbit.go index ea769c7e..688a23dd 100644 --- a/exchanges/itbit/itbit.go +++ b/exchanges/itbit/itbit.go @@ -324,7 +324,7 @@ func (i *ItBit) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange.UR var intermediary json.RawMessage err = i.SendPayload(ctx, request.Unset, func() (*request.Item, error) { n := i.Requester.GetNonce(true).String() - timestamp := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) + timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10) var message []byte message, err = json.Marshal([]string{method, urlPath, string(PayloadJSON), n, timestamp}) diff --git a/exchanges/kline/kline.go b/exchanges/kline/kline.go index 8ff0de35..df3e9724 100644 --- a/exchanges/kline/kline.go +++ b/exchanges/kline/kline.go @@ -19,8 +19,7 @@ func CreateKline(trades []order.TradeHistory, interval Interval, p currency.Pair return Item{}, fmt.Errorf("invalid time interval: [%s]", interval) } - err := validateData(trades) - if err != nil { + if err := validateData(trades); err != nil { return Item{}, err } diff --git a/exchanges/kline/kline_test.go b/exchanges/kline/kline_test.go index f0964f95..ff03b328 100644 --- a/exchanges/kline/kline_test.go +++ b/exchanges/kline/kline_test.go @@ -92,7 +92,7 @@ func TestValidateData(t *testing.T) { func TestCreateKline(t *testing.T) { t.Parallel() - c, err := CreateKline(nil, + _, err := CreateKline(nil, OneMin, currency.NewPair(currency.BTC, currency.USD), asset.Spot, @@ -113,7 +113,7 @@ func TestCreateKline(t *testing.T) { }) } - c, err = CreateKline(trades, + _, err = CreateKline(trades, 0, currency.NewPair(currency.BTC, currency.USD), asset.Spot, @@ -122,7 +122,7 @@ func TestCreateKline(t *testing.T) { t.Fatal("error cannot be nil") } - c, err = CreateKline(trades, + c, err := CreateKline(trades, OneMin, currency.NewPair(currency.BTC, currency.USD), asset.Spot, @@ -251,6 +251,8 @@ func TestDurationToWord(t *testing.T) { for x := range testCases { test := testCases[x] t.Run(test.name, func(t *testing.T) { + t.Parallel() + t.Helper() v := durationToWord(test.interval) if !strings.EqualFold(v, test.name) { t.Fatalf("%v: received %v expected %v", test.name, v, test.name) @@ -261,7 +263,7 @@ func TestDurationToWord(t *testing.T) { func TestKlineErrors(t *testing.T) { t.Parallel() - v := ErrorKline{ + v := Error{ Interval: OneYear, Pair: currency.NewPair(currency.BTC, currency.AUD), Err: errors.New("hello world"), @@ -398,6 +400,7 @@ func TestTotalCandlesPerInterval(t *testing.T) { for x := range testCases { test := testCases[x] t.Run(test.name, func(t *testing.T) { + t.Parallel() v := TotalCandlesPerInterval(start, end, test.interval) if v != test.expected { t.Fatalf("%v: received %v expected %v", test.name, v, test.expected) @@ -506,6 +509,7 @@ func TestItem_SortCandlesByTimestamp(t *testing.T) { } func setupTest(t *testing.T) { + t.Helper() if verbose { testhelpers.EnableVerboseTestOutput() } diff --git a/exchanges/kline/kline_types.go b/exchanges/kline/kline_types.go index 16fc949d..ab6066b2 100644 --- a/exchanges/kline/kline_types.go +++ b/exchanges/kline/kline_types.go @@ -128,8 +128,8 @@ type ExchangeCapabilitiesEnabled struct { // Interval type for kline Interval usage type Interval time.Duration -// ErrorKline struct to hold kline interval errors -type ErrorKline struct { +// Error struct to hold kline interval errors +type Error struct { Asset asset.Item Pair currency.Pair Interval Interval @@ -137,13 +137,13 @@ type ErrorKline struct { } // Error returns short interval unsupported message -func (k *ErrorKline) Error() string { - return k.Err.Error() +func (e *Error) Error() string { + return e.Err.Error() } // Unwrap returns interval unsupported message -func (k *ErrorKline) Unwrap() error { - return k.Err +func (e *Error) Unwrap() error { + return e.Err } // IntervalRangeHolder holds the entire range of intervals diff --git a/exchanges/kraken/kraken.go b/exchanges/kraken/kraken.go index ad228224..ed927dad 100644 --- a/exchanges/kraken/kraken.go +++ b/exchanges/kraken/kraken.go @@ -10,8 +10,10 @@ import ( "strconv" "strings" "sync" + "time" "github.com/thrasher-corp/gocryptotrader/common" + "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/common/crypto" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" @@ -214,46 +216,59 @@ func (k *Kraken) GetOHLC(ctx context.Context, symbol currency.Pair, interval str Data map[string]interface{} `json:"result"` } - var OHLC []OpenHighLowClose var result Response path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenOHLC, values.Encode()) err = k.SendHTTPRequest(ctx, exchange.RestSpot, path, &result) if err != nil { - return OHLC, err + return nil, err } if len(result.Error) != 0 { - return OHLC, fmt.Errorf("getOHLC error: %s", result.Error) + return nil, fmt.Errorf("getOHLC error: %s", result.Error) } - _, ok := result.Data[translatedAsset].([]interface{}) + ohlcData, ok := result.Data[translatedAsset].([]interface{}) if !ok { return nil, errors.New("invalid data returned") } - for _, y := range result.Data[translatedAsset].([]interface{}) { - o := OpenHighLowClose{} - for i, x := range y.([]interface{}) { - switch i { - case 0: - o.Time = x.(float64) - case 1: - o.Open, _ = strconv.ParseFloat(x.(string), 64) - case 2: - o.High, _ = strconv.ParseFloat(x.(string), 64) - case 3: - o.Low, _ = strconv.ParseFloat(x.(string), 64) - case 4: - o.Close, _ = strconv.ParseFloat(x.(string), 64) - case 5: - o.VolumeWeightedAveragePrice, _ = strconv.ParseFloat(x.(string), 64) - case 6: - o.Volume, _ = strconv.ParseFloat(x.(string), 64) - case 7: - o.Count = x.(float64) - } + var OHLC []OpenHighLowClose + for x := range ohlcData { + subData, ok := ohlcData[x].([]interface{}) + if !ok { + return nil, errors.New("unable to type assert subData") + } + + if len(subData) < 8 { + return nil, errors.New("unexpected data length returned") + } + + var o OpenHighLowClose + if o.Time, ok = subData[0].(float64); !ok { + return nil, errors.New("unable to type assert time") + } + if o.Open, err = convert.FloatFromString(subData[1]); err != nil { + return nil, err + } + if o.High, err = convert.FloatFromString(subData[2]); err != nil { + return nil, err + } + if o.Low, err = convert.FloatFromString(subData[3]); err != nil { + return nil, err + } + if o.Close, err = convert.FloatFromString(subData[4]); err != nil { + return nil, err + } + if o.VolumeWeightedAveragePrice, err = convert.FloatFromString(subData[5]); err != nil { + return nil, err + } + if o.Volume, err = convert.FloatFromString(subData[6]); err != nil { + return nil, err + } + if o.Count, ok = subData[7].(float64); !ok { + return nil, errors.New("unable to type assert count") } OHLC = append(OHLC, o) } @@ -280,24 +295,40 @@ func (k *Kraken) GetDepth(ctx context.Context, symbol currency.Pair) (Orderbook, return orderBook, fmt.Errorf("%s GetDepth result is nil", k.Name) } - data := result.(map[string]interface{}) - if data["result"] == nil { + data, ok := result.(map[string]interface{}) + if !ok { + return orderBook, errors.New("unable to type assert data") + } + orderbookData, ok := data["result"].(map[string]interface{}) + if !ok { return orderBook, fmt.Errorf("%s GetDepth data[result] is nil", k.Name) } - orderbookData := data["result"].(map[string]interface{}) - var bidsData []interface{} var asksData []interface{} for _, y := range orderbookData { - yData := y.(map[string]interface{}) - bidsData = yData["bids"].([]interface{}) - asksData = yData["asks"].([]interface{}) + yData, ok := y.(map[string]interface{}) + if !ok { + return orderBook, errors.New("unable to type assert yData") + } + if bidsData, ok = yData["bids"].([]interface{}); !ok { + return orderBook, errors.New("unable to type assert bidsData") + } + if asksData, ok = yData["asks"].([]interface{}); !ok { + return orderBook, errors.New("unable to type assert asksData") + } } processOrderbook := func(data []interface{}) ([]OrderbookBase, error) { var result []OrderbookBase for x := range data { - entry := data[x].([]interface{}) + entry, ok := data[x].([]interface{}) + if !ok { + return nil, errors.New("unable to type assert entry") + } + + if len(entry) < 2 { + return nil, errors.New("unexpected entry length") + } price, priceErr := strconv.ParseFloat(entry[0].(string), 64) if priceErr != nil { @@ -439,30 +470,48 @@ func (k *Kraken) GetSpread(ctx context.Context, symbol currency.Pair) ([]Spread, } values.Set("pair", symbolValue) - var peanutButter []Spread - var response interface{} - + resp := struct { + SpreadData map[string]interface{} `json:"result"` + }{} path := fmt.Sprintf("/%s/public/%s?%s", krakenAPIVersion, krakenSpread, values.Encode()) - - err = k.SendHTTPRequest(ctx, exchange.RestSpot, path, &response) + err = k.SendHTTPRequest(ctx, exchange.RestSpot, path, &resp) if err != nil { - return peanutButter, err + return nil, err } - data := response.(map[string]interface{}) - result := data["result"].(map[string]interface{}) + data, ok := resp.SpreadData[symbolValue] + if !ok { + return nil, fmt.Errorf("unable to find %s in spread data", symbolValue) + } - for _, x := range result[symbolValue].([]interface{}) { - s := Spread{} - for i, y := range x.([]interface{}) { - switch i { - case 0: - s.Time = y.(float64) - case 1: - s.Bid, _ = strconv.ParseFloat(y.(string), 64) - case 2: - s.Ask, _ = strconv.ParseFloat(y.(string), 64) - } + spreadData, ok := data.([]interface{}) + if !ok { + return nil, errors.New("unable to type assert spreadData") + } + + var peanutButter []Spread + for x := range spreadData { + subData, ok := spreadData[x].([]interface{}) + if !ok { + return nil, errors.New("unable to type assert subData") + } + + if len(subData) < 3 { + return nil, errors.New("unexpected data length") + } + + var s Spread + timeData, ok := subData[0].(float64) + if !ok { + return nil, errors.New("unable to type assert timeData") + } + s.Time = time.Unix(int64(timeData), 0) + + if s.Bid, err = convert.FloatFromString(subData[1]); err != nil { + return nil, err + } + if s.Ask, err = convert.FloatFromString(subData[2]); err != nil { + return nil, err } peanutButter = append(peanutButter, s) } @@ -1203,8 +1252,7 @@ func (a *assetTranslatorStore) Seed(orig, alt string) { a.Assets = make(map[string]string) } - _, ok := a.Assets[orig] - if ok { + if _, ok := a.Assets[orig]; ok { a.l.Unlock() return } diff --git a/exchanges/kraken/kraken_futures.go b/exchanges/kraken/kraken_futures.go index b862a60d..c341726b 100644 --- a/exchanges/kraken/kraken_futures.go +++ b/exchanges/kraken/kraken_futures.go @@ -275,7 +275,7 @@ func (k *Kraken) SendFuturesAuthRequest(ctx context.Context, method, path string interim := json.RawMessage{} newRequest := func() (*request.Item, error) { - nonce := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) + nonce := strconv.FormatInt(time.Now().UnixMilli(), 10) reqData := "" if len(data) > 0 { temp, err := json.Marshal(data) diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index 7c8b561c..37f7a985 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -1128,6 +1128,7 @@ func TestWithdrawCancel(t *testing.T) { // ---------------------------- Websocket tests ----------------------------------------- func setupWsTests(t *testing.T) { + t.Helper() if wsSetupRan { return } @@ -1205,16 +1206,14 @@ func TestWsAddOrder(t *testing.T) { func TestWsCancelOrder(t *testing.T) { setupWsTests(t) - err := k.wsCancelOrders([]string{"1337"}) - if err != nil { + if err := k.wsCancelOrders([]string{"1337"}); err != nil { t.Error(err) } } func TestWsCancelAllOrders(t *testing.T) { setupWsTests(t) - _, err := k.wsCancelAllOrders() - if err != nil { + if _, err := k.wsCancelAllOrders(); err != nil { t.Error(err) } } @@ -1994,6 +1993,7 @@ func Test_FormatExchangeKlineInterval(t *testing.T) { test := testCases[x] t.Run(test.name, func(t *testing.T) { + t.Parallel() ret := k.FormatExchangeKlineInterval(test.interval) if ret != test.output { diff --git a/exchanges/kraken/kraken_types.go b/exchanges/kraken/kraken_types.go index 175a46eb..65f4ba03 100644 --- a/exchanges/kraken/kraken_types.go +++ b/exchanges/kraken/kraken_types.go @@ -188,7 +188,7 @@ type Orderbook struct { // Spread holds the spread between trades type Spread struct { - Time float64 + Time time.Time Bid float64 Ask float64 } diff --git a/exchanges/kraken/kraken_websocket.go b/exchanges/kraken/kraken_websocket.go index 47e7e9fe..e9865f3a 100644 --- a/exchanges/kraken/kraken_websocket.go +++ b/exchanges/kraken/kraken_websocket.go @@ -717,16 +717,38 @@ func (k *Kraken) wsProcessTickers(channelData *WebsocketChannelData, data map[st // wsProcessSpread converts spread/orderbook data and sends it to the datahandler func (k *Kraken) wsProcessSpread(channelData *WebsocketChannelData, data []interface{}) { - bestBid := data[0].(string) - bestAsk := data[1].(string) + if len(data) < 5 { + k.Websocket.DataHandler <- fmt.Errorf("%s unexpected wsProcessSpread data length", k.Name) + return + } + bestBid, ok := data[0].(string) + if !ok { + k.Websocket.DataHandler <- fmt.Errorf("%s wsProcessSpread: unable to type assert bestBid", k.Name) + return + } + bestAsk, ok := data[1].(string) + if !ok { + k.Websocket.DataHandler <- fmt.Errorf("%s wsProcessSpread: unable to type assert bestAsk", k.Name) + return + } timeData, err := strconv.ParseFloat(data[2].(string), 64) if err != nil { - k.Websocket.DataHandler <- err + k.Websocket.DataHandler <- fmt.Errorf("%s wsProcessSpread: unable to parse timeData. Error: %s", + k.Name, + err) + return + } + bidVolume, ok := data[3].(string) + if !ok { + k.Websocket.DataHandler <- fmt.Errorf("%s wsProcessSpread: unable to type assert bidVolume", k.Name) + return + } + askVolume, ok := data[4].(string) + if !ok { + k.Websocket.DataHandler <- fmt.Errorf("%s wsProcessSpread: unable to type assert askVolume", k.Name) return } - bidVolume := data[3].(string) - askVolume := data[4].(string) if k.Verbose { log.Debugf(log.ExchangeSys, "%v Spread data for '%v' received. Best bid: '%v' Best ask: '%v' Time: '%v', Bid volume '%v', Ask volume '%v'", @@ -839,7 +861,13 @@ func (k *Kraken) wsProcessOrderBookPartial(channelData *WebsocketChannelData, as // to respect both within a reasonable degree var highestLastUpdate time.Time for i := range askData { - asks := askData[i].([]interface{}) + asks, ok := askData[i].([]interface{}) + if !ok { + return errors.New("unable to type assert asks") + } + if len(asks) < 3 { + return errors.New("unexpected asks length") + } price, err := strconv.ParseFloat(asks[0].(string), 64) if err != nil { return err @@ -863,7 +891,13 @@ func (k *Kraken) wsProcessOrderBookPartial(channelData *WebsocketChannelData, as } for i := range bidData { - bids := bidData[i].([]interface{}) + bids, ok := bidData[i].([]interface{}) + if !ok { + return errors.New("unable to type assert bids") + } + if len(bids) < 3 { + return errors.New("unexpected bids length") + } price, err := strconv.ParseFloat(bids[0].(string), 64) if err != nil { return err @@ -906,7 +940,10 @@ func (k *Kraken) wsProcessOrderBookUpdate(channelData *WebsocketChannelData, ask var highestLastUpdate time.Time // Ask data is not always sent for i := range askData { - asks := askData[i].([]interface{}) + asks, ok := askData[i].([]interface{}) + if !ok { + return errors.New("asks type assertion failure") + } priceStr, ok := asks[0].(string) if !ok { @@ -966,7 +1003,10 @@ func (k *Kraken) wsProcessOrderBookUpdate(channelData *WebsocketChannelData, ask // Bid data is not always sent for i := range bidData { - bids := bidData[i].([]interface{}) + bids, ok := bidData[i].([]interface{}) + if !ok { + return errors.New("unable to type assert bids") + } priceStr, ok := bids[0].(string) if !ok { diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 65166c15..3549a620 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -487,8 +487,7 @@ func (k *Kraken) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (k *Kraken) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := k.UpdateTickers(ctx, a) - if err != nil { + if err := k.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(k.Name, p, a) @@ -615,8 +614,7 @@ func (k *Kraken) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (a }) } } - err := account.Process(&info) - if err != nil { + if err := account.Process(&info); err != nil { return account.Holdings{}, err } return info, nil @@ -698,8 +696,7 @@ func (k *Kraken) GetHistoricTrades(_ context.Context, _ currency.Pair, _ asset.I // SubmitOrder submits a new order func (k *Kraken) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { var submitOrderResponse order.SubmitResponse - err := s.Validate() - if err != nil { + if err := s.Validate(); err != nil { return submitOrderResponse, err } switch s.AssetType { @@ -707,7 +704,7 @@ func (k *Kraken) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit if k.Websocket.CanUseAuthenticatedWebsocketForWrapper() { var resp string s.Pair.Delimiter = "/" // required pair format: ISO 4217-A3 - resp, err = k.wsAddOrder(&WsAddOrderRequest{ + resp, err := k.wsAddOrder(&WsAddOrderRequest{ OrderType: s.Type.Lower(), OrderSide: s.Side.Lower(), Pair: s.Pair.String(), @@ -721,7 +718,7 @@ func (k *Kraken) SubmitOrder(ctx context.Context, s *order.Submit) (order.Submit submitOrderResponse.IsOrderPlaced = true } else { var response AddOrderResponse - response, err = k.AddOrder(ctx, + response, err := k.AddOrder(ctx, s.Pair, s.Side.String(), s.Type.String(), diff --git a/exchanges/lbank/lbank.go b/exchanges/lbank/lbank.go index 11471407..586ff637 100644 --- a/exchanges/lbank/lbank.go +++ b/exchanges/lbank/lbank.go @@ -15,6 +15,7 @@ import ( "net/url" "strconv" "strings" + "time" gctcrypto "github.com/thrasher-corp/gocryptotrader/common/crypto" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" @@ -114,70 +115,32 @@ func (l *Lbank) GetTrades(ctx context.Context, symbol string, limit, time int64) } // GetKlines returns kline data -func (l *Lbank) GetKlines(ctx context.Context, symbol, size, klineType, time string) ([]KlineResponse, error) { - var klineTemp interface{} - var k []KlineResponse +func (l *Lbank) GetKlines(ctx context.Context, symbol, size, klineType, tm string) ([]KlineResponse, error) { + var klineTemp [][]float64 params := url.Values{} params.Set("symbol", symbol) params.Set("size", size) params.Set("type", klineType) - params.Set("time", time) + params.Set("time", tm) path := fmt.Sprintf("/v%s/%s?%s", lbankAPIVersion, lbankKlines, params.Encode()) err := l.SendHTTPRequest(ctx, exchange.RestSpot, path, &klineTemp) if err != nil { - return k, err + return nil, err } - resp, ok := klineTemp.([]interface{}) - if !ok { - return nil, errors.New("response received is invalid") - } - - for i := range resp { - resp2, ok := resp[i].([]interface{}) - if !ok { - return nil, errors.New("response received is invalid") + var k []KlineResponse + for x := range klineTemp { + if len(klineTemp[x]) < 6 { + return nil, errors.New("unexpected kline data length") } - var tempResp KlineResponse - for x := range resp2 { - switch x { - case 0: - tempResp.TimeStamp = int64(resp2[x].(float64)) - case 1: - if val, ok := resp2[x].(int64); ok { - tempResp.OpenPrice = float64(val) - } else { - tempResp.OpenPrice = resp2[x].(float64) - } - case 2: - if val, ok := resp2[x].(int64); ok { - tempResp.HigestPrice = float64(val) - } else { - tempResp.HigestPrice = resp2[x].(float64) - } - case 3: - if val, ok := resp2[x].(int64); ok { - tempResp.LowestPrice = float64(val) - } else { - tempResp.LowestPrice = resp2[x].(float64) - } - - case 4: - if val, ok := resp2[x].(int64); ok { - tempResp.ClosePrice = float64(val) - } else { - tempResp.ClosePrice = resp2[x].(float64) - } - - case 5: - if val, ok := resp2[x].(int64); ok { - tempResp.TradingVolume = float64(val) - } else { - tempResp.TradingVolume = resp2[x].(float64) - } - } - } - k = append(k, tempResp) + k = append(k, KlineResponse{ + TimeStamp: time.Unix(int64(klineTemp[x][0]), 0).UTC(), + OpenPrice: klineTemp[x][1], + HigestPrice: klineTemp[x][2], + LowestPrice: klineTemp[x][3], + ClosePrice: klineTemp[x][4], + TradingVolume: klineTemp[x][5], + }) } return k, nil } diff --git a/exchanges/lbank/lbank_types.go b/exchanges/lbank/lbank_types.go index 4f678987..897752c6 100644 --- a/exchanges/lbank/lbank_types.go +++ b/exchanges/lbank/lbank_types.go @@ -2,6 +2,7 @@ package lbank import ( "encoding/json" + "time" "github.com/thrasher-corp/gocryptotrader/currency" ) @@ -44,12 +45,12 @@ type TradeResponse struct { // KlineResponse stores kline info for given currency exchange type KlineResponse struct { - TimeStamp int64 `json:"timestamp"` - OpenPrice float64 `json:"openprice"` - HigestPrice float64 `json:"highestprice"` - LowestPrice float64 `json:"lowestprice"` - ClosePrice float64 `json:"closeprice"` - TradingVolume float64 `json:"tradingvolume"` + TimeStamp time.Time `json:"timestamp"` + OpenPrice float64 `json:"openprice"` + HigestPrice float64 `json:"highestprice"` + LowestPrice float64 `json:"lowestprice"` + ClosePrice float64 `json:"closeprice"` + TradingVolume float64 `json:"tradingvolume"` } // InfoResponse stores info diff --git a/exchanges/lbank/lbank_wrapper.go b/exchanges/lbank/lbank_wrapper.go index 32d3dcde..da792166 100644 --- a/exchanges/lbank/lbank_wrapper.go +++ b/exchanges/lbank/lbank_wrapper.go @@ -224,8 +224,7 @@ func (l *Lbank) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (l *Lbank) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := l.UpdateTickers(ctx, a) - if err != nil { + if err := l.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(l.Name, p, a) @@ -387,12 +386,12 @@ allTrades: tradeData, err = l.GetTrades(ctx, p.String(), int64(limit), - ts.UnixNano()/int64(time.Millisecond)) + ts.UnixMilli()) if err != nil { return nil, err } for i := range tradeData { - tradeTime := time.Unix(0, tradeData[i].DateMS*int64(time.Millisecond)) + tradeTime := time.UnixMilli(tradeData[i].DateMS) if tradeTime.Before(timestampStart) || tradeTime.After(timestampEnd) { break allTrades } @@ -499,7 +498,7 @@ func (l *Lbank) CancelAllOrders(ctx context.Context, o *order.Cancel) (order.Can var resp order.CancelAllResponse orderIDs, err := l.getAllOpenOrderID(ctx) if err != nil { - return resp, nil + return resp, err } for key := range orderIDs { @@ -935,7 +934,7 @@ func (l *Lbank) GetHistoricCandles(ctx context.Context, pair currency.Pair, a as for x := range data { ret.Candles = append(ret.Candles, kline.Candle{ - Time: time.Unix(data[x].TimeStamp, 0), + Time: data[x].TimeStamp, Open: data[x].OpenPrice, High: data[x].HigestPrice, Low: data[x].LowestPrice, @@ -981,11 +980,11 @@ func (l *Lbank) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pa return kline.Item{}, err } for i := range data { - if data[i].TimeStamp < dates.Ranges[x].Start.Ticks || data[i].TimeStamp > dates.Ranges[x].End.Ticks { + if data[i].TimeStamp.Unix() < dates.Ranges[x].Start.Ticks || data[i].TimeStamp.Unix() > dates.Ranges[x].End.Ticks { continue } ret.Candles = append(ret.Candles, kline.Candle{ - Time: time.Unix(data[i].TimeStamp, 0).UTC(), + Time: data[i].TimeStamp, Open: data[i].OpenPrice, High: data[i].HigestPrice, Low: data[i].LowestPrice, diff --git a/exchanges/localbitcoins/localbitcoins_mock_test.go b/exchanges/localbitcoins/localbitcoins_mock_test.go index 6d1c243c..e9799289 100644 --- a/exchanges/localbitcoins/localbitcoins_mock_test.go +++ b/exchanges/localbitcoins/localbitcoins_mock_test.go @@ -1,4 +1,5 @@ -//+build !mock_test_off +//go:build !mock_test_off +// +build !mock_test_off // This will build if build tag mock_test_off is not parsed and will try to mock // all tests in _test.go diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 154b2ca4..1f63ec38 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -199,8 +199,7 @@ func (l *LocalBitcoins) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (l *LocalBitcoins) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := l.UpdateTickers(ctx, a) - if err != nil { + if err := l.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(l.Name, p, a) diff --git a/exchanges/mock/recording.go b/exchanges/mock/recording.go index 3bb21087..149ebff8 100644 --- a/exchanges/mock/recording.go +++ b/exchanges/mock/recording.go @@ -349,7 +349,10 @@ func CheckJSON(data interface{}, excluded *Exclusion) (interface{}, error) { context[key] = 0.0 // Zero val float } case Slice: - slice := val.([]interface{}) + slice, ok := val.([]interface{}) + if !ok { + return nil, errors.New("unable to type assert slice") + } if len(slice) < 1 { // Empty slice found context[key] = slice diff --git a/exchanges/nonce/nonce_test.go b/exchanges/nonce/nonce_test.go index 8ee3e1fa..93a37e7e 100644 --- a/exchanges/nonce/nonce_test.go +++ b/exchanges/nonce/nonce_test.go @@ -8,9 +8,7 @@ import ( func TestGet(t *testing.T) { var nonce Nonce nonce.Set(112321313) - expected := Value(112321313) - result := nonce.Get() - if expected != result { + if expected, result := Value(112321313), nonce.Get(); expected != result { t.Errorf("Expected %d got %d", expected, result) } } @@ -18,9 +16,7 @@ func TestGet(t *testing.T) { func TestGetInc(t *testing.T) { var nonce Nonce nonce.Set(1) - expected := Value(2) - result := nonce.GetInc() - if expected != result { + if expected, result := Value(2), nonce.GetInc(); expected != result { t.Errorf("Expected %d got %d", expected, result) } } @@ -28,9 +24,7 @@ func TestGetInc(t *testing.T) { func TestSet(t *testing.T) { var nonce Nonce nonce.Set(1) - expected := Value(1) - result := nonce.Get() - if expected != result { + if result, expected := nonce.Get(), Value(1); expected != result { t.Errorf("Expected %d got %d", expected, result) } } @@ -62,9 +56,7 @@ func TestNonceConcurrency(t *testing.T) { wg.Wait() - result := nonce.Get() - expected := Value(12312 + 1000) - if expected != result { + if expected, result := Value(12312+1000), nonce.Get(); expected != result { t.Errorf("Expected %d got %d", expected, result) } } diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go index 4db5a447..2183d542 100644 --- a/exchanges/okcoin/okcoin_wrapper.go +++ b/exchanges/okcoin/okcoin_wrapper.go @@ -316,8 +316,7 @@ func (o *OKCoin) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (o *OKCoin) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := o.UpdateTickers(ctx, a) - if err != nil { + if err := o.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(o.Name, p, a) diff --git a/exchanges/okex/okex_test.go b/exchanges/okex/okex_test.go index 46520ba2..0f1c9e25 100644 --- a/exchanges/okex/okex_test.go +++ b/exchanges/okex/okex_test.go @@ -187,6 +187,7 @@ func TestGetPerpSwapMarkets(t *testing.T) { } func testStandardErrorHandling(t *testing.T, err error) { + t.Helper() if !areTestAPIKeysSet() && err == nil { t.Errorf("Expecting an error when no keys are set") } diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index 00ab4549..e52c822b 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -540,8 +540,7 @@ func (o *OKEX) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (o *OKEX) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := o.UpdateTickers(ctx, a) - if err != nil { + if err := o.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(o.Name, p, a) diff --git a/exchanges/okgroup/okgroup.go b/exchanges/okgroup/okgroup.go index 28e0f0f1..69a38a13 100644 --- a/exchanges/okgroup/okgroup.go +++ b/exchanges/okgroup/okgroup.go @@ -546,11 +546,11 @@ func FormatParameters(request interface{}) (parameters string) { func (o *OKGroup) GetErrorCode(code interface{}) error { var assertedCode string - switch reflect.TypeOf(code).String() { - case "float64": - assertedCode = strconv.FormatFloat(code.(float64), 'f', -1, 64) - case "string": - assertedCode = code.(string) + switch d := code.(type) { + case float64: + assertedCode = strconv.FormatFloat(d, 'f', -1, 64) + case string: + assertedCode = d default: return errors.New("unusual type returned") } diff --git a/exchanges/okgroup/okgroup_wrapper.go b/exchanges/okgroup/okgroup_wrapper.go index f8617efe..74f3264d 100644 --- a/exchanges/okgroup/okgroup_wrapper.go +++ b/exchanges/okgroup/okgroup_wrapper.go @@ -276,8 +276,7 @@ func (o *OKGroup) GetFundingHistory(ctx context.Context) (resp []exchange.FundHi // SubmitOrder submits a new order func (o *OKGroup) SubmitOrder(ctx context.Context, s *order.Submit) (order.SubmitResponse, error) { - err := s.Validate() - if err != nil { + if err := s.Validate(); err != nil { return order.SubmitResponse{}, err } @@ -625,37 +624,34 @@ func (o *OKGroup) GetHistoricCandles(ctx context.Context, pair currency.Pair, a } for x := range candles { - t := candles[x].([]interface{}) - tempCandle := kline.Candle{} + t, ok := candles[x].([]interface{}) + if !ok { + return kline.Item{}, errors.New("unable to type asset candle data") + } + if len(t) < 6 { + return kline.Item{}, errors.New("incorrect candles data length") + } v, ok := t[0].(string) if !ok { - return kline.Item{}, errors.New("unexpected value received") + return kline.Item{}, errors.New("unable to type asset time data") } - tempCandle.Time, err = time.Parse(time.RFC3339, v) - if err != nil { + var tempCandle kline.Candle + if tempCandle.Time, err = time.Parse(time.RFC3339, v); err != nil { return kline.Item{}, err } - tempCandle.Open, err = convert.FloatFromString(t[1]) - if err != nil { + if tempCandle.Open, err = convert.FloatFromString(t[1]); err != nil { return kline.Item{}, err } - tempCandle.High, err = convert.FloatFromString(t[2]) - if err != nil { + if tempCandle.High, err = convert.FloatFromString(t[2]); err != nil { return kline.Item{}, err } - - tempCandle.Low, err = convert.FloatFromString(t[3]) - if err != nil { + if tempCandle.Low, err = convert.FloatFromString(t[3]); err != nil { return kline.Item{}, err } - - tempCandle.Close, err = convert.FloatFromString(t[4]) - if err != nil { + if tempCandle.Close, err = convert.FloatFromString(t[4]); err != nil { return kline.Item{}, err } - - tempCandle.Volume, err = convert.FloatFromString(t[5]) - if err != nil { + if tempCandle.Volume, err = convert.FloatFromString(t[5]); err != nil { return kline.Item{}, err } ret.Candles = append(ret.Candles, tempCandle) @@ -703,37 +699,37 @@ func (o *OKGroup) GetHistoricCandlesExtended(ctx context.Context, pair currency. } for i := range candles { - t := candles[i].([]interface{}) - tempCandle := kline.Candle{} + t, ok := candles[i].([]interface{}) + if !ok { + return kline.Item{}, errors.New("unable to type assert candles data") + } + if len(t) < 6 { + return kline.Item{}, errors.New("candle data length invalid") + } v, ok := t[0].(string) if !ok { - return kline.Item{}, errors.New("unexpected value received") + return kline.Item{}, errors.New("unable to type assert time value") } - tempCandle.Time, err = time.Parse(time.RFC3339, v) - if err != nil { + var tempCandle kline.Candle + if tempCandle.Time, err = time.Parse(time.RFC3339, v); err != nil { return kline.Item{}, err } - tempCandle.Open, err = convert.FloatFromString(t[1]) - if err != nil { + if tempCandle.Open, err = convert.FloatFromString(t[1]); err != nil { return kline.Item{}, err } - tempCandle.High, err = convert.FloatFromString(t[2]) - if err != nil { + if tempCandle.High, err = convert.FloatFromString(t[2]); err != nil { return kline.Item{}, err } - tempCandle.Low, err = convert.FloatFromString(t[3]) - if err != nil { + if tempCandle.Low, err = convert.FloatFromString(t[3]); err != nil { return kline.Item{}, err } - tempCandle.Close, err = convert.FloatFromString(t[4]) - if err != nil { + if tempCandle.Close, err = convert.FloatFromString(t[4]); err != nil { return kline.Item{}, err } - tempCandle.Volume, err = convert.FloatFromString(t[5]) - if err != nil { + if tempCandle.Volume, err = convert.FloatFromString(t[5]); err != nil { return kline.Item{}, err } ret.Candles = append(ret.Candles, tempCandle) diff --git a/exchanges/orderbook/linked_list_test.go b/exchanges/orderbook/linked_list_test.go index 588548f9..a632cd61 100644 --- a/exchanges/orderbook/linked_list_test.go +++ b/exchanges/orderbook/linked_list_test.go @@ -40,7 +40,7 @@ func (ll *linkedList) display() { func TestLoad(t *testing.T) { list := asks{} - Check(list, 0, 0, 0, t) + Check(t, list, 0, 0, 0) stack := newStack() list.load(Items{ @@ -56,7 +56,7 @@ func TestLoad(t *testing.T) { t.Fatalf("incorrect stack count expected: %v received: %v", 0, stack.getCount()) } - Check(list, 6, 36, 6, t) + Check(t, list, 6, 36, 6) list.load(Items{ {Price: 1, Amount: 1}, @@ -68,7 +68,7 @@ func TestLoad(t *testing.T) { t.Fatalf("incorrect stack count expected: %v received: %v", 3, stack.getCount()) } - Check(list, 3, 9, 3, t) + Check(t, list, 3, 9, 3) list.load(Items{ {Price: 1, Amount: 1}, @@ -81,7 +81,7 @@ func TestLoad(t *testing.T) { t.Fatalf("incorrect stack count expected: %v received: %v", 2, stack.getCount()) } - Check(list, 4, 16, 4, t) + Check(t, list, 4, 16, 4) // purge entire list list.load(nil, stack) @@ -90,7 +90,7 @@ func TestLoad(t *testing.T) { t.Fatalf("incorrect stack count expected: %v received: %v", 6, stack.getCount()) } - Check(list, 0, 0, 0, t) + Check(t, list, 0, 0, 0) } // 22222386 57.3 ns/op 0 B/op 0 allocs/op (old) @@ -121,7 +121,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 1, Amount: 2}, }, stack, 0, getNow()) - Check(a, 7, 37, 6, t) + Check(t, a, 7, 37, 6) if stack.getCount() != 0 { t.Fatalf("incorrect stack count expected: %v received: %v", 0, stack.getCount()) @@ -132,7 +132,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 0.5, Amount: 2}, }, stack, 0, getNow()) - Check(a, 9, 38, 7, t) + Check(t, a, 9, 38, 7) if stack.getCount() != 0 { t.Fatalf("incorrect stack count expected: %v received: %v", 0, stack.getCount()) @@ -143,7 +143,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 12, Amount: 2}, }, stack, 0, getNow()) - Check(a, 11, 62, 8, t) + Check(t, a, 11, 62, 8) if stack.getCount() != 0 { t.Fatalf("incorrect stack count expected: %v received: %v", 0, stack.getCount()) @@ -156,7 +156,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 13, Amount: 2}, }, stack, 10, getNow()) - Check(a, 15, 106, 10, t) + Check(t, a, 15, 106, 10) if stack.getCount() != 1 { t.Fatalf("incorrect stack count expected: %v received: %v", 1, stack.getCount()) @@ -167,7 +167,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 12, Amount: 0}, }, stack, 0, getNow()) - Check(a, 13, 82, 9, t) + Check(t, a, 13, 82, 9) if stack.getCount() != 2 { t.Fatalf("incorrect stack count expected: %v received: %v", 2, stack.getCount()) @@ -178,7 +178,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 7, Amount: 0}, }, stack, 0, getNow()) - Check(a, 12, 75, 8, t) + Check(t, a, 12, 75, 8) if stack.getCount() != 3 { t.Fatalf("incorrect stack count expected: %v received: %v", 3, stack.getCount()) @@ -189,7 +189,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 0.5, Amount: 0}, }, stack, 0, getNow()) - Check(a, 10, 74, 7, t) + Check(t, a, 10, 74, 7) if stack.getCount() != 4 { t.Fatalf("incorrect stack count expected: %v received: %v", 4, stack.getCount()) @@ -208,7 +208,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 11, Amount: 1}, }, stack, 0, getNow()) - Check(a, 6, 36, 6, t) + Check(t, a, 6, 36, 6) if stack.getCount() != 5 { t.Fatalf("incorrect stack count expected: %v received: %v", 4, stack.getCount()) @@ -230,7 +230,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 11, Amount: 2}, }, stack, 0, getNow()) - Check(b, 7, 47, 6, t) + Check(t, b, 7, 47, 6) if stack.getCount() != 0 { t.Fatalf("incorrect stack count expected: %v received: %v", 0, stack.getCount()) @@ -241,7 +241,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 12, Amount: 2}, }, stack, 0, getNow()) - Check(b, 9, 71, 7, t) + Check(t, b, 9, 71, 7) if stack.getCount() != 0 { t.Fatalf("incorrect stack count expected: %v received: %v", 0, stack.getCount()) @@ -252,7 +252,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 0.5, Amount: 2}, }, stack, 0, getNow()) - Check(b, 11, 72, 8, t) + Check(t, b, 11, 72, 8) if stack.getCount() != 0 { t.Fatalf("incorrect stack count expected: %v received: %v", 0, stack.getCount()) @@ -265,7 +265,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 13, Amount: 2}, }, stack, 10, getNow()) - Check(b, 15, 141, 10, t) + Check(t, b, 15, 141, 10) if stack.getCount() != 1 { t.Fatalf("incorrect stack count expected: %v received: %v", 0, stack.getCount()) @@ -276,7 +276,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 1, Amount: 0}, }, stack, 0, getNow()) - Check(b, 14, 140, 9, t) + Check(t, b, 14, 140, 9) if stack.getCount() != 2 { t.Fatalf("incorrect stack count expected: %v received: %v", 2, stack.getCount()) @@ -287,7 +287,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 10.5, Amount: 0}, }, stack, 0, getNow()) - Check(b, 12, 119, 8, t) + Check(t, b, 12, 119, 8) if stack.getCount() != 3 { t.Fatalf("incorrect stack count expected: %v received: %v", 3, stack.getCount()) @@ -298,7 +298,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 13, Amount: 0}, }, stack, 0, getNow()) - Check(b, 10, 93, 7, t) + Check(t, b, 10, 93, 7) if stack.getCount() != 4 { t.Fatalf("incorrect stack count expected: %v received: %v", 4, stack.getCount()) @@ -317,7 +317,7 @@ func TestUpdateInsertByPrice(t *testing.T) { {Price: 11, Amount: 1}, }, stack, 0, getNow()) - Check(b, 6, 36, 6, t) + Check(t, b, 6, 36, 6) if stack.getCount() != 5 { t.Fatalf("incorrect stack count expected: %v received: %v", 4, stack.getCount()) @@ -338,15 +338,15 @@ func TestCleanup(t *testing.T) { a.load(asksSnapshot, stack) a.cleanup(6, stack) - Check(a, 6, 36, 6, t) + Check(t, a, 6, 36, 6) a.cleanup(5, stack) - Check(a, 5, 25, 5, t) + Check(t, a, 5, 25, 5) a.cleanup(1, stack) - Check(a, 1, 1, 1, t) + Check(t, a, 1, 1, 1) a.cleanup(10, stack) - Check(a, 1, 1, 1, t) + Check(t, a, 1, 1, 1) a.cleanup(0, stack) // will purge, underlying checks are done elseware to prevent this - Check(a, 0, 0, 0, t) + Check(t, a, 0, 0, 0) } // 46154023 24.0 ns/op 0 B/op 0 allocs/op (old) @@ -421,7 +421,7 @@ func TestUpdateByID(t *testing.T) { t.Fatal(err) } - Check(a, 6, 36, 6, t) + Check(t, a, 6, 36, 6) err = a.updateByID(Items{ {Price: 11, Amount: 1, ID: 1337}, @@ -496,7 +496,7 @@ func TestDeleteByID(t *testing.T) { t.Fatal(err) } - Check(a, 5, 35, 5, t) + Check(t, a, 5, 35, 5) // Delete at tail err = a.deleteByID(Items{ @@ -506,7 +506,7 @@ func TestDeleteByID(t *testing.T) { t.Fatal(err) } - Check(a, 4, 24, 4, t) + Check(t, a, 4, 24, 4) // Delete in middle err = a.deleteByID(Items{ @@ -516,7 +516,7 @@ func TestDeleteByID(t *testing.T) { t.Fatal(err) } - Check(a, 3, 19, 3, t) + Check(t, a, 3, 19, 3) // Intentional error err = a.deleteByID(Items{ @@ -556,7 +556,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 7, 37, 6, t) + Check(t, a, 7, 37, 6) // Reset a.load(asksSnapshot, s) @@ -574,7 +574,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 12, 72, 6, t) + Check(t, a, 12, 72, 6) // Update all instances with matching ID in backwards err = a.updateInsertByID(Items{ @@ -589,7 +589,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 12, 72, 6, t) + Check(t, a, 12, 72, 6) // Update all instances with matching ID all over the ship err = a.updateInsertByID(Items{ @@ -604,7 +604,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 12, 72, 6, t) + Check(t, a, 12, 72, 6) // Update all instances move one before ID in middle err = a.updateInsertByID(Items{ @@ -619,7 +619,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 12, 66, 6, t) + Check(t, a, 12, 66, 6) // Update all instances move one before ID at head err = a.updateInsertByID(Items{ @@ -634,7 +634,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 12, 63, 6, t) + Check(t, a, 12, 63, 6) // Reset a.load(asksSnapshot, s) @@ -652,7 +652,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 12, 78, 6, t) + Check(t, a, 12, 78, 6) // Reset a.load(asksSnapshot, s) @@ -670,7 +670,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 12, 86, 6, t) + Check(t, a, 12, 86, 6) // Update all instances then pop new instance err = a.updateInsertByID(Items{ @@ -686,7 +686,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 14, 106, 7, t) + Check(t, a, 14, 106, 7) // Reset a.load(asksSnapshot, s) @@ -705,7 +705,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 14, 87, 7, t) + Check(t, a, 14, 87, 7) // bookmark head and move to mid err = a.updateInsertByID(Items{ @@ -715,7 +715,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 14, 101, 7, t) + Check(t, a, 14, 101, 7) // bookmark head and move to tail err = a.updateInsertByID(Items{ @@ -725,7 +725,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 14, 124, 7, t) + Check(t, a, 14, 124, 7) // move tail location to head err = a.updateInsertByID(Items{ @@ -735,7 +735,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 14, 104, 7, t) + Check(t, a, 14, 104, 7) // move tail location to mid err = a.updateInsertByID(Items{ @@ -745,7 +745,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 14, 96, 7, t) + Check(t, a, 14, 96, 7) // insert at tail dont match err = a.updateInsertByID(Items{ @@ -755,7 +755,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 16, 156, 8, t) + Check(t, a, 16, 156, 8) // insert between last and 2nd last err = a.updateInsertByID(Items{ @@ -765,7 +765,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 18, 180, 9, t) + Check(t, a, 18, 180, 9) // readjust at end err = a.updateInsertByID(Items{ @@ -775,7 +775,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 19, 207, 9, t) + Check(t, a, 19, 207, 9) // readjust further and decrease price past tail err = a.updateInsertByID(Items{ @@ -785,7 +785,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 19, 213, 9, t) + Check(t, a, 19, 213, 9) // purge a.load(nil, s) @@ -804,7 +804,7 @@ func TestUpdateInsertByIDAsk(t *testing.T) { t.Fatal(err) } - Check(a, 14, 87, 7, t) + Check(t, a, 14, 87, 7) } func TestUpdateInsertByIDBids(t *testing.T) { @@ -828,7 +828,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 7, 37, 6, t) + Check(t, b, 7, 37, 6) // Reset b.load(bidsSnapshot, s) @@ -846,7 +846,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 12, 72, 6, t) + Check(t, b, 12, 72, 6) // Update all instances with matching ID in backwards err = b.updateInsertByID(Items{ @@ -861,7 +861,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 12, 72, 6, t) + Check(t, b, 12, 72, 6) // Update all instances with matching ID all over the ship err = b.updateInsertByID(Items{ @@ -876,7 +876,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 12, 72, 6, t) + Check(t, b, 12, 72, 6) // Update all instances move one before ID in middle err = b.updateInsertByID(Items{ @@ -891,7 +891,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 12, 66, 6, t) + Check(t, b, 12, 66, 6) // Update all instances move one before ID at head err = b.updateInsertByID(Items{ @@ -906,7 +906,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 12, 63, 6, t) + Check(t, b, 12, 63, 6) // Reset b.load(bidsSnapshot, s) @@ -924,7 +924,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 12, 78, 6, t) + Check(t, b, 12, 78, 6) // Reset b.load(bidsSnapshot, s) @@ -942,7 +942,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 12, 86, 6, t) + Check(t, b, 12, 86, 6) // Update all instances then pop new instance err = b.updateInsertByID(Items{ @@ -958,7 +958,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 14, 106, 7, t) + Check(t, b, 14, 106, 7) // Reset b.load(bidsSnapshot, s) @@ -977,7 +977,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 14, 87, 7, t) + Check(t, b, 14, 87, 7) // bookmark head and move to mid err = b.updateInsertByID(Items{ @@ -987,7 +987,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 14, 82, 7, t) + Check(t, b, 14, 82, 7) // bookmark head and move to tail err = b.updateInsertByID(Items{ @@ -997,7 +997,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 14, 60.5, 7, t) + Check(t, b, 14, 60.5, 7) // move tail location to head err = b.updateInsertByID(Items{ @@ -1007,7 +1007,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 14, 80, 7, t) + Check(t, b, 14, 80, 7) // move tail location to mid err = b.updateInsertByID(Items{ @@ -1017,7 +1017,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 14, 94, 7, t) + Check(t, b, 14, 94, 7) // insert at head dont match err = b.updateInsertByID(Items{ @@ -1027,7 +1027,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 16, 154, 8, t) + Check(t, b, 16, 154, 8) // insert between last and 2nd last err = b.updateInsertByID(Items{ @@ -1036,7 +1036,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { if err != nil { t.Fatal(err) } - Check(b, 18, 157, 9, t) + Check(t, b, 18, 157, 9) // readjust at end err = b.updateInsertByID(Items{ @@ -1045,7 +1045,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { if err != nil { t.Fatal(err) } - Check(b, 19, 158, 9, t) + Check(t, b, 19, 158, 9) // readjust further and decrease price past tail err = b.updateInsertByID(Items{ @@ -1054,7 +1054,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { if err != nil { t.Fatal(err) } - Check(b, 19, 157.7, 9, t) + Check(t, b, 19, 157.7, 9) // purge b.load(nil, s) @@ -1073,7 +1073,7 @@ func TestUpdateInsertByIDBids(t *testing.T) { t.Fatal(err) } - Check(b, 14, 87, 7, t) + Check(t, b, 14, 87, 7) } func TestInsertUpdatesBid(t *testing.T) { @@ -1101,7 +1101,7 @@ func TestInsertUpdatesBid(t *testing.T) { t.Fatalf("expected error %s but received %v", errCollisionDetected, err) } - Check(b, 6, 36, 6, t) + Check(t, b, 6, 36, 6) // Insert at head err = b.insertUpdates(Items{ @@ -1111,7 +1111,7 @@ func TestInsertUpdatesBid(t *testing.T) { t.Fatal(err) } - Check(b, 7, 48, 7, t) + Check(t, b, 7, 48, 7) // Insert at tail err = b.insertUpdates(Items{ @@ -1121,7 +1121,7 @@ func TestInsertUpdatesBid(t *testing.T) { t.Fatal(err) } - Check(b, 8, 48.5, 8, t) + Check(t, b, 8, 48.5, 8) // Insert at mid err = b.insertUpdates(Items{ @@ -1131,7 +1131,7 @@ func TestInsertUpdatesBid(t *testing.T) { t.Fatal(err) } - Check(b, 9, 54, 9, t) + Check(t, b, 9, 54, 9) // purge b.load(nil, s) @@ -1144,7 +1144,7 @@ func TestInsertUpdatesBid(t *testing.T) { t.Fatal(err) } - Check(b, 1, 5.5, 1, t) + Check(t, b, 1, 5.5, 1) } func TestInsertUpdatesAsk(t *testing.T) { @@ -1172,7 +1172,7 @@ func TestInsertUpdatesAsk(t *testing.T) { t.Fatalf("expected error %s but received %v", errCollisionDetected, err) } - Check(a, 6, 36, 6, t) + Check(t, a, 6, 36, 6) // Insert at tail err = a.insertUpdates(Items{ @@ -1182,7 +1182,7 @@ func TestInsertUpdatesAsk(t *testing.T) { t.Fatal(err) } - Check(a, 7, 48, 7, t) + Check(t, a, 7, 48, 7) // Insert at head err = a.insertUpdates(Items{ @@ -1192,7 +1192,7 @@ func TestInsertUpdatesAsk(t *testing.T) { t.Fatal(err) } - Check(a, 8, 48.5, 8, t) + Check(t, a, 8, 48.5, 8) // Insert at mid err = a.insertUpdates(Items{ @@ -1202,7 +1202,7 @@ func TestInsertUpdatesAsk(t *testing.T) { t.Fatal(err) } - Check(a, 9, 54, 9, t) + Check(t, a, 9, 54, 9) // purge a.load(nil, s) @@ -1215,11 +1215,11 @@ func TestInsertUpdatesAsk(t *testing.T) { t.Fatal(err) } - Check(a, 1, 5.5, 1, t) + Check(t, a, 1, 5.5, 1) } // check checks depth values after an update has taken place -func Check(depth interface{}, liquidity, value float64, nodeCount int, t *testing.T) { +func Check(t *testing.T, depth interface{}, liquidity, value float64, nodeCount int) { t.Helper() b, isBid := depth.(bids) a, isAsk := depth.(asks) @@ -1434,7 +1434,7 @@ func TestShiftBookmark(t *testing.T) { t.Fatal("nilBookmark not reassigned") } - head := bookmarkedNode + head := bookmarkedNode // nolint // ifshort false positive bookmarkedNode.Prev = nil bookmarkedNode.Next = originalBookmarkNext tip.Next = nil diff --git a/exchanges/orderbook/node.go b/exchanges/orderbook/node.go index 005ecce8..f52aec67 100644 --- a/exchanges/orderbook/node.go +++ b/exchanges/orderbook/node.go @@ -54,7 +54,6 @@ func getNow() now { func (s *stack) Push(n *Node, tn now) { if !atomic.CompareAndSwapUint32(&s.sema, neutral, active) { // Stack is in use, for now we can dereference pointer - n = nil return } // Adds a time when its placed back on to stack. diff --git a/exchanges/orderbook/orderbook.go b/exchanges/orderbook/orderbook.go index 0d3dcc4a..cc68e2d7 100644 --- a/exchanges/orderbook/orderbook.go +++ b/exchanges/orderbook/orderbook.go @@ -321,8 +321,7 @@ func (b *Base) Process() error { b.LastUpdated = time.Now() } - err := b.Verify() - if err != nil { + if err := b.Verify(); err != nil { return err } return service.Update(b) diff --git a/exchanges/orderbook/orderbook_test.go b/exchanges/orderbook/orderbook_test.go index 162596e4..2387f659 100644 --- a/exchanges/orderbook/orderbook_test.go +++ b/exchanges/orderbook/orderbook_test.go @@ -605,9 +605,7 @@ func TestReverse(t *testing.T) { var b Base b.VerifyOrderbook = true - length := 1000 - b.Bids = deploySliceOrdered() - if len(b.Bids) != length { + if b.Bids = deploySliceOrdered(); len(b.Bids) != 1000 { t.Fatal("incorrect length") } @@ -637,9 +635,8 @@ func TestReverse(t *testing.T) { // 705985 1856 ns/op 0 B/op 0 allocs/op func BenchmarkReverse(b *testing.B) { - length := 1000 s := deploySliceOrdered() - if len(s) != length { + if len(s) != 1000 { b.Fatal("incorrect length") } diff --git a/exchanges/poloniex/poloniex.go b/exchanges/poloniex/poloniex.go index 0b74fa5f..b5636cf2 100644 --- a/exchanges/poloniex/poloniex.go +++ b/exchanges/poloniex/poloniex.go @@ -246,7 +246,10 @@ func (p *Poloniex) GetBalances(ctx context.Context) (Balance, error) { return Balance{}, err } - data := result.(map[string]interface{}) + data, ok := result.(map[string]interface{}) + if !ok { + return Balance{}, errors.New("unable to type assert balance result") + } balance := Balance{} balance.Currency = make(map[string]float64) @@ -287,7 +290,10 @@ func (p *Poloniex) GetDepositAddresses(ctx context.Context) (DepositAddresses, e } for x, y := range data { - addresses.Addresses[x] = y.(string) + addresses.Addresses[x], ok = y.(string) + if !ok { + return addresses, errors.New("unable to type assert address to string") + } } return addresses, nil diff --git a/exchanges/poloniex/poloniex_mock_test.go b/exchanges/poloniex/poloniex_mock_test.go index e2355e18..c0507731 100644 --- a/exchanges/poloniex/poloniex_mock_test.go +++ b/exchanges/poloniex/poloniex_mock_test.go @@ -1,4 +1,5 @@ -//+build !mock_test_off +//go:build !mock_test_off +// +build !mock_test_off // This will build if build tag mock_test_off is not parsed and will try to mock // all tests in _test.go diff --git a/exchanges/poloniex/poloniex_test.go b/exchanges/poloniex/poloniex_test.go index db4a4e58..fcda0527 100644 --- a/exchanges/poloniex/poloniex_test.go +++ b/exchanges/poloniex/poloniex_test.go @@ -270,6 +270,7 @@ func TestGetOrderStatus(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() if tt.mock != mockTests { t.Skip() } @@ -325,6 +326,7 @@ func TestGetOrderTrades(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() if tt.mock != mockTests { t.Skip() } diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index 4209664f..87c9c7a7 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -312,8 +312,7 @@ func (p *Poloniex) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (p *Poloniex) UpdateTicker(ctx context.Context, currencyPair currency.Pair, a asset.Item) (*ticker.Price, error) { - err := p.UpdateTickers(ctx, a) - if err != nil { + if err := p.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(p.Name, currencyPair, a) diff --git a/exchanges/request/backoff_test.go b/exchanges/request/backoff_test.go index 25dc2cea..06e24b86 100644 --- a/exchanges/request/backoff_test.go +++ b/exchanges/request/backoff_test.go @@ -8,6 +8,7 @@ import ( ) func TestLinearBackoff(t *testing.T) { + t.Parallel() type args struct { Backoff request.Backoff } diff --git a/exchanges/request/request.go b/exchanges/request/request.go index 6e84875f..a7b88959 100644 --- a/exchanges/request/request.go +++ b/exchanges/request/request.go @@ -272,7 +272,7 @@ func (r *Requester) GetNonce(isNano bool) nonce.Value { func (r *Requester) GetNonceMilli() nonce.Value { r.timedLock.LockForDuration() if r.Nonce.Get() == 0 { - r.Nonce.Set(time.Now().UnixNano() / int64(time.Millisecond)) + r.Nonce.Set(time.Now().UnixMilli()) return r.Nonce.Get() } return r.Nonce.GetInc() diff --git a/exchanges/request/request_test.go b/exchanges/request/request_test.go index e7b396c5..e4d0dbd7 100644 --- a/exchanges/request/request_test.go +++ b/exchanges/request/request_test.go @@ -484,18 +484,14 @@ func TestGetNonce(t *testing.T) { new(http.Client), WithLimiter(&globalshell)) - n1 := r.GetNonce(false) - n2 := r.GetNonce(false) - if n1 == n2 { + if n1, n2 := r.GetNonce(false), r.GetNonce(false); n1 == n2 { t.Fatal(unexpected) } r2 := New("test", new(http.Client), WithLimiter(&globalshell)) - n3 := r2.GetNonce(true) - n4 := r2.GetNonce(true) - if n3 == n4 { + if n1, n2 := r2.GetNonce(true), r2.GetNonce(true); n1 == n2 { t.Fatal(unexpected) } } @@ -505,9 +501,7 @@ func TestGetNonceMillis(t *testing.T) { r := New("test", new(http.Client), WithLimiter(&globalshell)) - m1 := r.GetNonceMilli() - m2 := r.GetNonceMilli() - if m1 == m2 { + if m1, m2 := r.GetNonceMilli(), r.GetNonceMilli(); m1 == m2 { log.Fatal(unexpected) } } @@ -562,7 +556,7 @@ func TestBasicLimiter(t *testing.T) { defer cancel() err = r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil }) if !errors.Is(err, context.DeadlineExceeded) { - t.Fatalf("receieved: %v but expected: %v", err, context.DeadlineExceeded) + t.Fatalf("received: %v but expected: %v", err, context.DeadlineExceeded) } } diff --git a/exchanges/request/retry_test.go b/exchanges/request/retry_test.go index e0857c1f..6e3f4a6d 100644 --- a/exchanges/request/retry_test.go +++ b/exchanges/request/retry_test.go @@ -11,6 +11,7 @@ import ( ) func TestDefaultRetryPolicy(t *testing.T) { + t.Parallel() type args struct { Error error Response *http.Response @@ -70,6 +71,7 @@ func TestDefaultRetryPolicy(t *testing.T) { } func TestRetryAfter(t *testing.T) { + t.Parallel() now := time.Date(2020, time.April, 20, 13, 31, 13, 0, time.UTC) type args struct { diff --git a/exchanges/stream/stream_match.go b/exchanges/stream/stream_match.go index ecf30583..f5cb57ae 100644 --- a/exchanges/stream/stream_match.go +++ b/exchanges/stream/stream_match.go @@ -48,8 +48,7 @@ func (m *Match) IncomingWithData(signature interface{}, data []byte) bool { func (m *Match) set(signature interface{}) (matcher, error) { var ch chan []byte m.Lock() - _, ok := m.m[signature] - if ok { + if _, ok := m.m[signature]; ok { m.Unlock() return matcher{}, errors.New("signature collision") } diff --git a/exchanges/stream/stream_match_test.go b/exchanges/stream/stream_match_test.go index 0a72390e..5ab57f7c 100644 --- a/exchanges/stream/stream_match_test.go +++ b/exchanges/stream/stream_match_test.go @@ -42,9 +42,8 @@ func TestMatch(t *testing.T) { fmt.Println("should not have been able to match") } - data := <-m.C - if data != nil { - t.Fatal("wow") + if data := <-m.C; data != nil { + t.Fatal("data chan should be nil") } m.Cleanup() diff --git a/exchanges/stream/websocket.go b/exchanges/stream/websocket.go index ad9d8144..fbacbadf 100644 --- a/exchanges/stream/websocket.go +++ b/exchanges/stream/websocket.go @@ -475,8 +475,7 @@ func (w *Websocket) FlushChannels() error { return nil } - err := w.Shutdown() - if err != nil { + if err := w.Shutdown(); err != nil { return err } return w.Connect() @@ -867,8 +866,7 @@ func (w *Websocket) SubscribeToChannels(channels []ChannelSubscription) error { } } } - err := w.Subscriber(channels) - if err != nil { + if err := w.Subscriber(channels); err != nil { return fmt.Errorf("%v %w: %v", w.exchangeName, ErrSubscriptionFailure, err) } return nil diff --git a/exchanges/stream/websocket_test.go b/exchanges/stream/websocket_test.go index 7d5aaac1..1292afd0 100644 --- a/exchanges/stream/websocket_test.go +++ b/exchanges/stream/websocket_test.go @@ -167,8 +167,7 @@ func TestSetup(t *testing.T) { func TestTrafficMonitorTimeout(t *testing.T) { t.Parallel() ws := *New() - err := ws.Setup(defaultSetup) - if err != nil { + if err := ws.Setup(defaultSetup); err != nil { t.Fatal(err) } ws.trafficTimeout = time.Second * 2 @@ -650,6 +649,7 @@ func TestDial(t *testing.T) { for i := range testCases { testData := &testCases[i] t.Run(testData.WC.ExchangeName, func(t *testing.T) { + t.Parallel() if testData.WC.ProxyURL != "" && !useProxyTests { t.Skip("Proxy testing not enabled, skipping") } @@ -697,6 +697,7 @@ func TestSendMessage(t *testing.T) { for i := range testCases { testData := &testCases[i] t.Run(testData.WC.ExchangeName, func(t *testing.T) { + t.Parallel() if testData.WC.ProxyURL != "" && !useProxyTests { t.Skip("Proxy testing not enabled, skipping") } @@ -737,7 +738,7 @@ func TestSendMessageWithResponse(t *testing.T) { t.Fatal(err) } - go readMessages(wc, t) + go readMessages(t, wc) request := testRequest{ Event: "subscribe", @@ -755,7 +756,8 @@ func TestSendMessageWithResponse(t *testing.T) { } // readMessages helper func -func readMessages(wc *WebsocketConnection, t *testing.T) { +func readMessages(t *testing.T, wc *WebsocketConnection) { + t.Helper() timer := time.NewTimer(20 * time.Second) for { select { diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index 349db47e..a0ee8e61 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -222,8 +222,7 @@ func (y *Yobit) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (y *Yobit) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := y.UpdateTickers(ctx, a) - if err != nil { + if err := y.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(y.Name, p, a) diff --git a/exchanges/zb/zb.go b/exchanges/zb/zb.go index cba1d2be..9c0096cc 100644 --- a/exchanges/zb/zb.go +++ b/exchanges/zb/zb.go @@ -227,39 +227,39 @@ func (z *ZB) GetSpotKline(ctx context.Context, arg KlinesRequestParams) (KLineRe urlPath := fmt.Sprintf("/%s/%s/%s?%s", zbData, zbAPIVersion, zbKline, vals.Encode()) var res KLineResponse - var rawKlines map[string]interface{} - err := z.SendHTTPRequest(ctx, exchange.RestSpot, urlPath, &rawKlines, klineFunc) + resp := struct { + Data [][]float64 `json:"data"` + MoneyType string `json:"moneyType"` + Symbol string `json:"symbol"` + }{} + err := z.SendHTTPRequest(ctx, exchange.RestSpot, urlPath, &resp, klineFunc) if err != nil { return res, err } - if rawKlines == nil || rawKlines["symbol"] == nil { - return res, errors.New("zb GetSpotKline rawKlines is nil") + if resp.Data == nil || resp.Symbol == "" || resp.MoneyType == "" { + return res, errors.New("GetSpotKline received empty data") } + res.MoneyType = resp.MoneyType + res.Symbol = resp.Symbol - res.Symbol = rawKlines["symbol"].(string) - res.MoneyType = rawKlines["moneyType"].(string) + for x := range resp.Data { + if len(resp.Data[x]) < 6 { + return res, errors.New("unexpected kline data length") + } - rawKlineDatasString, _ := json.Marshal(rawKlines["data"].([]interface{})) - var rawKlineDatas [][]interface{} - if err := json.Unmarshal(rawKlineDatasString, &rawKlineDatas); err != nil { - return res, errors.New("zb rawKlines unmarshal failed") - } - for _, k := range rawKlineDatas { - ot, err := convert.TimeFromUnixTimestampFloat(k[0]) + ot, err := convert.TimeFromUnixTimestampFloat(resp.Data[x][0]) if err != nil { - return res, errors.New("zb cannot parse Kline.OpenTime") + return res, err } res.Data = append(res.Data, &KLineResponseData{ - ID: k[0].(float64), KlineTime: ot, - Open: k[1].(float64), - High: k[2].(float64), - Low: k[3].(float64), - Close: k[4].(float64), - Volume: k[5].(float64), + Open: resp.Data[x][1], + High: resp.Data[x][2], + Low: resp.Data[x][3], + Close: resp.Data[x][4], + Volume: resp.Data[x][5], }) } - return res, nil } @@ -325,7 +325,7 @@ func (z *ZB) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange.URL, var intermediary json.RawMessage newRequest := func() (*request.Item, error) { now := time.Now() - params.Set("reqTime", fmt.Sprintf("%d", convert.UnixMillis(now))) + params.Set("reqTime", strconv.FormatInt(now.UnixMilli(), 10)) params.Set("sign", fmt.Sprintf("%x", hmac)) urlPath := fmt.Sprintf("%s/%s?%s", diff --git a/exchanges/zb/zb_mock_test.go b/exchanges/zb/zb_mock_test.go index 0e0fb40e..dea033b9 100644 --- a/exchanges/zb/zb_mock_test.go +++ b/exchanges/zb/zb_mock_test.go @@ -1,4 +1,5 @@ -//+build !mock_test_off +//go:build !mock_test_off +// +build !mock_test_off // This will build if build tag mock_test_off is not parsed and will try to mock // all tests in _test.go diff --git a/exchanges/zb/zb_test.go b/exchanges/zb/zb_test.go index 5ee511b2..af6d2b77 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -12,7 +12,6 @@ import ( "github.com/gorilla/websocket" "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/core" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" @@ -35,6 +34,7 @@ var z ZB var wsSetupRan bool func setupWsAuth(t *testing.T) { + t.Helper() if wsSetupRan { return } @@ -500,8 +500,7 @@ func TestWsTransferFunds(t *testing.T) { // TestGetSubUserList ws test func TestGetSubUserList(t *testing.T) { setupWsAuth(t) - _, err := z.wsGetSubUserList() - if err != nil { + if _, err := z.wsGetSubUserList(); err != nil { t.Fatal(err) } } @@ -833,7 +832,7 @@ func TestGetSpotKline(t *testing.T) { } if mockTests { startTime := time.Date(2020, 9, 1, 0, 0, 0, 0, time.UTC) - arg.Since = convert.UnixMillis(startTime) + arg.Since = startTime.UnixMilli() arg.Type = "1day" } diff --git a/exchanges/zb/zb_types.go b/exchanges/zb/zb_types.go index f8bf85cc..e489136a 100644 --- a/exchanges/zb/zb_types.go +++ b/exchanges/zb/zb_types.go @@ -120,7 +120,6 @@ type KlinesRequestParams struct { // KLineResponseData Kline Data type KLineResponseData struct { - ID float64 `json:"id"` // K线ID KlineTime time.Time `json:"klineTime"` Open float64 `json:"open"` // 开盘价 Close float64 `json:"close"` // 收盘价, 当K线为最晚的一根时, 时最新成交价 diff --git a/exchanges/zb/zb_websocket.go b/exchanges/zb/zb_websocket.go index 661b3c3d..df7e454b 100644 --- a/exchanges/zb/zb_websocket.go +++ b/exchanges/zb/zb_websocket.go @@ -109,7 +109,7 @@ func (z *ZB) wsHandleData(respRaw []byte) error { Last: wsTicker.Data.Last, Bid: wsTicker.Data.Buy, Ask: wsTicker.Data.Sell, - LastUpdated: time.Unix(0, wsTicker.Date*int64(time.Millisecond)), + LastUpdated: time.UnixMilli(wsTicker.Date), AssetType: asset.Spot, Pair: p, } diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index 16d7d909..0ed9ca3b 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -11,7 +11,6 @@ import ( "time" "github.com/thrasher-corp/gocryptotrader/common" - "github.com/thrasher-corp/gocryptotrader/common/convert" "github.com/thrasher-corp/gocryptotrader/config" "github.com/thrasher-corp/gocryptotrader/currency" exchange "github.com/thrasher-corp/gocryptotrader/exchanges" @@ -283,8 +282,7 @@ func (z *ZB) UpdateTickers(ctx context.Context, a asset.Item) error { // UpdateTicker updates and returns the ticker for a currency pair func (z *ZB) UpdateTicker(ctx context.Context, p currency.Pair, a asset.Item) (*ticker.Price, error) { - err := z.UpdateTickers(ctx, a) - if err != nil { + if err := z.UpdateTickers(ctx, a); err != nil { return nil, err } return ticker.GetTicker(z.Name, p, a) @@ -389,8 +387,7 @@ func (z *ZB) UpdateAccountInfo(ctx context.Context, assetType asset.Item) (accou Currencies: balances, }) - err := account.Process(&info) - if err != nil { + if err := account.Process(&info); err != nil { return account.Holdings{}, err } @@ -864,7 +861,7 @@ func (z *ZB) GetHistoricCandles(ctx context.Context, p currency.Pair, a asset.It klineParams := KlinesRequestParams{ Type: z.FormatExchangeKlineInterval(interval), Symbol: p.String(), - Since: convert.UnixMillis(start), + Since: start.UnixMilli(), Size: int64(z.Features.Enabled.Kline.ResultLimit), } var candles KLineResponse @@ -909,7 +906,7 @@ allKlines: klineParams := KlinesRequestParams{ Type: z.FormatExchangeKlineInterval(interval), Symbol: p.String(), - Since: convert.UnixMillis(startTime), + Since: startTime.UnixMilli(), Size: int64(z.Features.Enabled.Kline.ResultLimit), } diff --git a/gctrpc/README.md b/gctrpc/README.md index d881b39e..7102130b 100644 --- a/gctrpc/README.md +++ b/gctrpc/README.md @@ -33,10 +33,10 @@ manager or by downloading one of the releases from the official repository: [protoc releases](https://github.com/protocolbuffers/protobuf/releases) -Then use `go get` to download the following packages: +Then use `go install` to download the following packages: ```bash -go get \ +go install \ github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ google.golang.org/protobuf/cmd/protoc-gen-go \ diff --git a/gctscript/modules/ta/indicators/atr.go b/gctscript/modules/ta/indicators/atr.go index e1809553..2b191d31 100644 --- a/gctscript/modules/ta/indicators/atr.go +++ b/gctscript/modules/ta/indicators/atr.go @@ -48,7 +48,13 @@ func atr(args ...objects.Object) (objects.Object, error) { ohlcvData := make([][]float64, 6) var allErrors []string for x := range ohlcvInputData { - t := ohlcvInputData[x].([]interface{}) + t, ok := ohlcvInputData[x].([]interface{}) + if !ok { + return nil, errors.New("ohlcvInputData type assert failed") + } + if len(t) < 6 { + return nil, errors.New("ohlcvInputData invalid data length") + } value, err := toFloat64(t[2]) if err != nil { allErrors = append(allErrors, err.Error()) diff --git a/gctscript/modules/ta/indicators/bbands.go b/gctscript/modules/ta/indicators/bbands.go index 3c9d16d8..523cc7c8 100644 --- a/gctscript/modules/ta/indicators/bbands.go +++ b/gctscript/modules/ta/indicators/bbands.go @@ -61,7 +61,14 @@ func bbands(args ...objects.Object) (objects.Object, error) { ohlcvData := make([][]float64, 6) var allErrors []string for x := range ohlcvInputData { - t := ohlcvInputData[x].([]interface{}) + var t []interface{} + t, ok = ohlcvInputData[x].([]interface{}) + if !ok { + return nil, errors.New("ohlcvInputData type assert failed") + } + if len(t) < 6 { + return nil, errors.New("ohlcvInputData invalid data length") + } value, err := toFloat64(t[2]) if err != nil { allErrors = append(allErrors, err.Error()) diff --git a/gctscript/modules/ta/indicators/correlation.go b/gctscript/modules/ta/indicators/correlation.go index 77db500e..e637070e 100644 --- a/gctscript/modules/ta/indicators/correlation.go +++ b/gctscript/modules/ta/indicators/correlation.go @@ -50,7 +50,13 @@ func correlationCoefficient(args ...objects.Object) (objects.Object, error) { var ohlcvClose []float64 for x := range ohlcvInputData { - t := ohlcvInputData[x].([]interface{}) + t, ok := ohlcvInputData[x].([]interface{}) + if !ok { + return nil, errors.New("ohlcvInputData type assert failed") + } + if len(t) < 5 { + return nil, errors.New("ohlcvInputData invalid data length") + } value, err := toFloat64(t[4]) if err != nil { allErrors = append(allErrors, err.Error()) diff --git a/gctscript/modules/ta/indicators/ema.go b/gctscript/modules/ta/indicators/ema.go index 72d571d6..9c1bb252 100644 --- a/gctscript/modules/ta/indicators/ema.go +++ b/gctscript/modules/ta/indicators/ema.go @@ -49,7 +49,13 @@ func ema(args ...objects.Object) (objects.Object, error) { var ohlcvClose []float64 var allErrors []string for x := range ohlcvInputData { - t := ohlcvInputData[x].([]interface{}) + t, ok := ohlcvInputData[x].([]interface{}) + if !ok { + return nil, errors.New("ohlcvInputData type assert failed") + } + if len(t) < 5 { + return nil, errors.New("ohlcvInputData invalid data length") + } value, err := toFloat64(t[4]) if err != nil { diff --git a/gctscript/modules/ta/indicators/macd.go b/gctscript/modules/ta/indicators/macd.go index 24957f01..9e9dc067 100644 --- a/gctscript/modules/ta/indicators/macd.go +++ b/gctscript/modules/ta/indicators/macd.go @@ -50,7 +50,13 @@ func macd(args ...objects.Object) (objects.Object, error) { var ohlcvClose []float64 var allErrors []string for x := range ohlcvInputData { - t := ohlcvInputData[x].([]interface{}) + t, ok := ohlcvInputData[x].([]interface{}) + if !ok { + return nil, errors.New("ohlcvInputData type assert failed") + } + if len(t) < 5 { + return nil, errors.New("ohlcvInputData invalid data length") + } value, err := toFloat64(t[4]) if err != nil { allErrors = append(allErrors, err.Error()) diff --git a/gctscript/modules/ta/indicators/mfi.go b/gctscript/modules/ta/indicators/mfi.go index 9f04313a..955499be 100644 --- a/gctscript/modules/ta/indicators/mfi.go +++ b/gctscript/modules/ta/indicators/mfi.go @@ -49,7 +49,13 @@ func mfi(args ...objects.Object) (objects.Object, error) { var allErrors []string for x := range ohlcvInputData { - t := ohlcvInputData[x].([]interface{}) + t, ok := ohlcvInputData[x].([]interface{}) + if !ok { + return nil, errors.New("ohlcvInputData type assert failed") + } + if len(t) < 6 { + return nil, errors.New("ohlcvInputData invalid data length") + } value, err := toFloat64(t[2]) if err != nil { allErrors = append(allErrors, err.Error()) diff --git a/gctscript/modules/ta/indicators/obv.go b/gctscript/modules/ta/indicators/obv.go index 6644e6c7..68643a7e 100644 --- a/gctscript/modules/ta/indicators/obv.go +++ b/gctscript/modules/ta/indicators/obv.go @@ -48,7 +48,13 @@ func obv(args ...objects.Object) (objects.Object, error) { ohlcvData := make([][]float64, 6) var allErrors []string for x := range ohlcvInputData { - t := ohlcvInputData[x].([]interface{}) + t, ok := ohlcvInputData[x].([]interface{}) + if !ok { + return nil, errors.New("ohlcvInputData type assert failed") + } + if len(t) < 6 { + return nil, errors.New("ohlcvInputData invalid data length") + } value, err := toFloat64(t[2]) if err != nil { allErrors = append(allErrors, err.Error()) diff --git a/gctscript/modules/ta/indicators/rsi.go b/gctscript/modules/ta/indicators/rsi.go index 06a46108..7588bda3 100644 --- a/gctscript/modules/ta/indicators/rsi.go +++ b/gctscript/modules/ta/indicators/rsi.go @@ -49,7 +49,13 @@ func rsi(args ...objects.Object) (objects.Object, error) { var ohlcvClose []float64 var allErrors []string for x := range ohlcvInputData { - t := ohlcvInputData[x].([]interface{}) + t, ok := ohlcvInputData[x].([]interface{}) + if !ok { + return nil, errors.New("ohlcvInputData type assert failed") + } + if len(t) < 5 { + return nil, errors.New("ohlcvInputData invalid data length") + } value, err := toFloat64(t[4]) if err != nil { diff --git a/gctscript/modules/ta/indicators/sma.go b/gctscript/modules/ta/indicators/sma.go index e204ea60..3e1d674b 100644 --- a/gctscript/modules/ta/indicators/sma.go +++ b/gctscript/modules/ta/indicators/sma.go @@ -49,7 +49,13 @@ func sma(args ...objects.Object) (objects.Object, error) { var ohlcvClose []float64 var allErrors []string for x := range ohlcvInputData { - t := ohlcvInputData[x].([]interface{}) + t, ok := ohlcvInputData[x].([]interface{}) + if !ok { + return nil, errors.New("unable to type assert ohlcvInputData") + } + if len(t) < 5 { + return nil, errors.New("ohlcvInputData invalid data length") + } value, err := toFloat64(t[4]) if err != nil { allErrors = append(allErrors, err.Error()) diff --git a/gctscript/vm/manager.go b/gctscript/vm/manager.go index ea6a3742..bfbc644e 100644 --- a/gctscript/vm/manager.go +++ b/gctscript/vm/manager.go @@ -73,8 +73,7 @@ func (g *GctScriptManager) Stop() error { atomic.CompareAndSwapInt32(&g.started, 1, 0) }() - err := g.ShutdownAll() - if err != nil { + if err := g.ShutdownAll(); err != nil { return err } close(g.shutdown) diff --git a/gctscript/vm/manager_test.go b/gctscript/vm/manager_test.go index ba6313c5..4f8907f0 100644 --- a/gctscript/vm/manager_test.go +++ b/gctscript/vm/manager_test.go @@ -37,6 +37,7 @@ func TestNewManager(t *testing.T) { for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() got, err := NewManager(tt.args.config) if (err != nil) != tt.wantErr { t.Errorf("NewManager() error = %v, wantErr %v", err, tt.wantErr) diff --git a/gctscript/vm/vm_test.go b/gctscript/vm/vm_test.go index c8e1a851..8e355d90 100644 --- a/gctscript/vm/vm_test.go +++ b/gctscript/vm/vm_test.go @@ -279,7 +279,7 @@ func TestVMWithRunnerOnce(t *testing.T) { config: configHelper(true, true, maxTestVirtualMachines), started: 1, } - vmCount := VMSCount.Len() + vmCount := VMSCount.Len() // nolint:ifshort,nolintlint // false positive and triggers only on Windows VM := manager.New() if VM == nil { t.Fatal("Failed to allocate new VM exiting") @@ -533,8 +533,7 @@ func TestVMLimit(t *testing.T) { config: configHelper(true, false, 0), started: 1, } - testVM := manager.New() - if testVM != nil { + if testVM := manager.New(); testVM != nil { t.Fatal("expected nil but received pointer to VM") } } diff --git a/gctscript/wrappers/gct/exchange/exchange_test.go b/gctscript/wrappers/gct/exchange/exchange_test.go index aa6990f7..876603ff 100644 --- a/gctscript/wrappers/gct/exchange/exchange_test.go +++ b/gctscript/wrappers/gct/exchange/exchange_test.go @@ -47,8 +47,7 @@ var ( func TestMain(m *testing.M) { var t int - err := setupEngine() - if err != nil { + if err := setupEngine(); err != nil { fmt.Printf("Failed to configure exchange test cannot continue: %v", err) os.Exit(1) } @@ -59,10 +58,8 @@ func TestMain(m *testing.M) { func TestExchange_Exchanges(t *testing.T) { t.Parallel() - x := exchangeTest.Exchanges(false) - y := len(x) - if y != 1 { - t.Fatalf("expected 1 received %v", y) + if x := exchangeTest.Exchanges(false); len(x) != 1 { + t.Fatalf("expected 1 received %v", x) } } diff --git a/log/logger.go b/log/logger.go index 93f7fa32..72e46338 100644 --- a/log/logger.go +++ b/log/logger.go @@ -19,8 +19,7 @@ func NewSubLogger(name string) (*SubLogger, error) { return nil, errEmptyLoggerName } name = strings.ToUpper(name) - _, ok := subLoggers[name] - if ok { + if _, ok := subLoggers[name]; ok { return nil, errSubLoggerAlreadyregistered } return registerNewSubLogger(name), nil @@ -43,7 +42,10 @@ func (l *Logger) newLogEvent(data, header, slName string, w io.Writer) error { return errors.New("io.Writer not set") } - e := eventPool.Get().(*Event) + e, ok := eventPool.Get().(*Event) + if !ok { + return errors.New("unable to type asset event") + } e.output = w e.data = append(e.data, []byte(header)...) if l.ShowLogSystemName { diff --git a/log/logger_setup.go b/log/logger_setup.go index 2a53574d..05e8e5b9 100644 --- a/log/logger_setup.go +++ b/log/logger_setup.go @@ -12,7 +12,7 @@ import ( func getWriters(s *SubLoggerConfig) io.Writer { mw := MultiWriter() - m := mw.(*multiWriter) + m := mw.(*multiWriter) // nolint // type assert not required outputWriters := strings.Split(s.Output, "|") for x := range outputWriters { diff --git a/log/logger_test.go b/log/logger_test.go index c4c8c1fc..e9043d63 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -68,27 +68,26 @@ func BenchmarkInfo(b *testing.B) { } func SetupTestDisabled(t *testing.T) { + t.Helper() SetupDisabled() } func TestAddWriter(t *testing.T) { mw := MultiWriter() - m := mw.(*multiWriter) + m := mw.(*multiWriter) // nolint // type assert not required m.Add(ioutil.Discard) m.Add(os.Stdin) m.Add(os.Stdout) - total := len(m.writers) - - if total != 3 { + if total := len(m.writers); total != 3 { t.Errorf("expected m.Writers to be 3 %v", total) } } func TestRemoveWriter(t *testing.T) { mw := MultiWriter() - m := mw.(*multiWriter) + m := mw.(*multiWriter) // nolint // type assert not required m.Add(ioutil.Discard) m.Add(os.Stdin) @@ -148,8 +147,7 @@ func TestValidSubLogger(t *testing.T) { } func TestCloseLogger(t *testing.T) { - err := CloseLogger() - if err != nil { + if err := CloseLogger(); err != nil { t.Errorf("CloseLogger() failed %v", err) } } diff --git a/portfolio/portfolio_test.go b/portfolio/portfolio_test.go index 020d875c..1974c2da 100644 --- a/portfolio/portfolio_test.go +++ b/portfolio/portfolio_test.go @@ -27,7 +27,7 @@ func TestGetEthereumBalance(t *testing.T) { t.Error("Portfolio GetEthereumBalance() address invalid") } - response, err = b.GetEthereumBalance(nonsenseAddress) + _, err = b.GetEthereumBalance(nonsenseAddress) if !errors.Is(err, errNotEthAddress) { t.Errorf("received '%v', expected '%v'", err, errNotEthAddress) }