diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index c50b241..1648d4e 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,24 +7,38 @@ on: branches: [ master ] jobs: - build: runs-on: ubuntu-latest + strategy: + matrix: + go-version: [1.20.x, 1.21.x, 1.22.x, 1.23.x] steps: - - uses: actions/checkout@v2 - - - name: Set up Go - uses: actions/setup-go@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: '>=1.19' - - - name: Check format - run: | - gofmt -s -l . - if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then exit 1; fi - + go-version: ${{ matrix.go-version }} - name: Build run: go build -v ./... - + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.22 + - uses: golangci/golangci-lint-action@v6 + with: + version: v1.61 + - name: Build + run: go build -v ./... + - name: Test + run: go test -cover -timeout 6s ./... + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: 1.23 - name: Test - run: go test -timeout 6s ./... + run: go test -cover -timeout 6s ./... diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..0a58a59 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,367 @@ +# Source: https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322 +# +# This code is licensed under the terms of the MIT license https://opensource.org/license/mit +# Copyright (c) 2021 Marat Reymers + +## Golden config for golangci-lint v1.61.0 +# +# This is the best config for golangci-lint based on my experience and opinion. +# It is very strict, but not extremely strict. +# Feel free to adapt and change it for your needs. + +run: + # Timeout for analysis, e.g. 30s, 5m. + # Default: 1m + timeout: 3m + +# This file contains only configs which differ from defaults. +# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml +linters-settings: + cyclop: + # The maximal code complexity to report. + # Default: 10 + max-complexity: 30 + # The maximal average package complexity. + # If it's higher than 0.0 (float) the check is enabled + # Default: 0.0 + package-average: 10.0 + + errcheck: + # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. + # Such cases aren't reported by default. + # Default: false + check-type-assertions: true + + exhaustive: + # Program elements to check for exhaustiveness. + # Default: [ switch ] + check: + - switch + - map + + exhaustruct: + # List of regular expressions to exclude struct packages and their names from checks. + # Regular expressions must match complete canonical struct package/name/structname. + # Default: [] + exclude: + # std libs + - "^net/http.Client$" + - "^net/http.Cookie$" + - "^net/http.Request$" + - "^net/http.Response$" + - "^net/http.Server$" + - "^net/http.Transport$" + - "^net/url.URL$" + - "^os/exec.Cmd$" + - "^reflect.StructField$" + # public libs + - "^github.com/Shopify/sarama.Config$" + - "^github.com/Shopify/sarama.ProducerMessage$" + - "^github.com/mitchellh/mapstructure.DecoderConfig$" + - "^github.com/prometheus/client_golang/.+Opts$" + - "^github.com/spf13/cobra.Command$" + - "^github.com/spf13/cobra.CompletionOptions$" + - "^github.com/stretchr/testify/mock.Mock$" + - "^github.com/testcontainers/testcontainers-go.+Request$" + - "^github.com/testcontainers/testcontainers-go.FromDockerfile$" + - "^golang.org/x/tools/go/analysis.Analyzer$" + - "^google.golang.org/protobuf/.+Options$" + - "^gopkg.in/yaml.v3.Node$" + + funlen: + # Checks the number of lines in a function. + # If lower than 0, disable the check. + # Default: 60 + lines: 100 + # Checks the number of statements in a function. + # If lower than 0, disable the check. + # Default: 40 + statements: 50 + # Ignore comments when counting lines. + # Default false + ignore-comments: true + + gocognit: + # Minimal code complexity to report. + # Default: 30 (but we recommend 10-20) + min-complexity: 20 + + gocritic: + # Settings passed to gocritic. + # The settings key is the name of a supported gocritic checker. + # The list of supported checkers can be find in https://go-critic.github.io/overview. + settings: + captLocal: + # Whether to restrict checker to params only. + # Default: true + paramsOnly: false + underef: + # Whether to skip (*x).method() calls where x is a pointer receiver. + # Default: true + skipRecvDeref: false + + goimports: + local-prefixes: github.com/alpacahq/ + + gomodguard: + blocked: + # List of blocked modules. + # Default: [] + modules: + - github.com/golang/protobuf: + recommendations: + - google.golang.org/protobuf + reason: "see https://developers.google.com/protocol-buffers/docs/reference/go/faq#modules" + - github.com/satori/go.uuid: + recommendations: + - github.com/google/uuid + reason: "satori's package is not maintained" + - github.com/gofrs/uuid: + recommendations: + - github.com/gofrs/uuid/v5 + reason: "gofrs' package was not go module before v5" + + govet: + # Enable all analyzers. + # Default: false + enable-all: true + # Disable analyzers by name. + # Run `go tool vet help` to see all analyzers. + # Default: [] + disable: + - fieldalignment # too strict + # Settings per analyzer. + settings: + shadow: + # Whether to be strict about shadowing; can be noisy. + # Default: false + strict: true + + inamedparam: + # Skips check for interface methods with only a single parameter. + # Default: false + skip-single-param: true + + mnd: + # List of function patterns to exclude from analysis. + # Values always ignored: `time.Date`, + # `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`, + # `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`. + # Default: [] + ignored-functions: + - args.Error + - flag.Arg + - flag.Duration.* + - flag.Float.* + - flag.Int.* + - flag.Uint.* + - os.Chmod + - os.Mkdir.* + - os.OpenFile + - os.WriteFile + - prometheus.ExponentialBuckets.* + - prometheus.LinearBuckets + + nakedret: + # Make an issue if func has more lines of code than this setting, and it has naked returns. + # Default: 30 + max-func-lines: 0 + + nolintlint: + # Exclude following linters from requiring an explanation. + # Default: [] + allow-no-explanation: [ funlen, gocognit, lll ] + # Enable to require an explanation of nonzero length after each nolint directive. + # Default: false + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. + # Default: false + require-specific: true + + perfsprint: + # Optimizes into strings concatenation. + # Default: true + strconcat: false + + rowserrcheck: + # database/sql is always checked + # Default: [] + packages: + - github.com/jmoiron/sqlx + + sloglint: + # Enforce not using global loggers. + # Values: + # - "": disabled + # - "all": report all global loggers + # - "default": report only the default slog logger + # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global + # Default: "" + no-global: "all" + # Enforce using methods that accept a context. + # Values: + # - "": disabled + # - "all": report all contextless calls + # - "scope": report only if a context exists in the scope of the outermost function + # https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only + # Default: "" + context: "scope" + + tenv: + # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. + # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. + # Default: false + all: true + + # We don't do any calculations with the floats, we just decode them. + testifylint: + disable: + - float-compare + +linters: + disable-all: true + enable: + ## enabled by default + - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases + - gosimple # specializes in simplifying a code + - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - ineffassign # detects when assignments to existing variables are not used + - staticcheck # is a go vet on steroids, applying a ton of static analysis checks + - unused # checks for unused constants, variables, functions and types + ## disabled by default + - asasalint # checks for pass []any as any in variadic func(...any) + - asciicheck # checks that your code does not contain non-ASCII identifiers + - bidichk # checks for dangerous unicode character sequences + - bodyclose # checks whether HTTP response body is closed successfully + - cyclop # checks function and package cyclomatic complexity + - durationcheck # checks for two durations multiplied together + - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error + - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13 + - exhaustive # checks exhaustiveness of enum switch statements + - fatcontext # detects nested contexts in loops + - forbidigo # forbids identifiers + - funlen # tool for detection of long functions + - gocheckcompilerdirectives # validates go compiler directive comments (//go:) + - gochecknoinits # checks that no init functions are present in Go code + - gochecksumtype # checks exhaustiveness on Go "sum types" + - gocognit # computes and checks the cognitive complexity of functions + - goconst # finds repeated strings that could be replaced by a constant + - gocritic # provides diagnostics that check for bugs, performance and style issues + - gocyclo # computes and checks the cyclomatic complexity of functions + - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt + - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod + - gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations + - goprintffuncname # checks that printf-like functions are named with f at the end + - gosec # inspects source code for security problems + - lll # reports long lines + - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap) + - makezero # finds slice declarations with non-zero initial length + - mirror # reports wrong mirror patterns of bytes/strings usage + - nakedret # finds naked returns in functions greater than a specified function length + - nestif # reports deeply nested if statements + - nilerr # finds the code that returns nil even if it checks that the error is not nil + - nolintlint # reports ill-formed or insufficient nolint directives + - nonamedreturns # reports all named returns + - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL + - perfsprint # checks that fmt.Sprintf can be replaced with a faster alternative + - predeclared # finds code that shadows one of Go's predeclared identifiers + - promlinter # checks Prometheus metrics naming via promlint + - protogetter # reports direct reads from proto message fields when getters should be used + - reassign # checks that package variables are not reassigned + - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint + - rowserrcheck # checks whether Err of rows is checked successfully + - sloglint # ensure consistent code style when using log/slog + - spancheck # checks for mistakes with OpenTelemetry/Census spans + - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed + - stylecheck # is a replacement for golint + - tenv # detects using os.Setenv instead of t.Setenv since Go1.17 + - testableexamples # checks if examples are testable (have an expected output) + - testifylint # checks usage of github.com/stretchr/testify + - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes + - unconvert # removes unnecessary type conversions + - unparam # reports unused function parameters + - usestdlibvars # detects the possibility to use variables/constants from the Go standard library + - wastedassign # finds wasted assignment statements + - whitespace # detects leading and trailing whitespace + + ## you may want to enable + #- decorder # checks declaration order and count of types, constants, variables and functions + #- exhaustruct # [highly recommend to enable] checks if all structure fields are initialized + #- gci # controls golang package import order and makes it always deterministic + #- ginkgolinter # [if you use ginkgo/gomega] enforces standards of using ginkgo and gomega + #- gochecknoglobals # checks that no global variables exist + #- godox # detects FIXME, TODO and other comment keywords + #- goheader # checks is file header matches to pattern + #- inamedparam # [great idea, but too strict, need to ignore a lot of cases by default] reports interfaces with unnamed method parameters + #- interfacebloat # checks the number of methods inside an interface + #- ireturn # accept interfaces, return concrete types + #- nilnil # checks that there is no simultaneous return of nil error and an invalid value + #- prealloc # [premature optimization, but can be used in some cases] finds slice declarations that could potentially be preallocated + #- tagalign # checks that struct tags are well aligned + #- varnamelen # [great idea, but too many false positives] checks that the length of a variable's name matches its scope + #- wrapcheck # checks that errors returned from external packages are wrapped + #- zerologlint # detects the wrong usage of zerolog that a user forgets to dispatch zerolog.Event + + ## disabled in this repo + #- canonicalheader # we use non-canonical headers, e.g. APCA-API-SECRET-KEY + #- copyloopvar # we support go 1.19, enable this when / if updated to go 1.22 + #- dupl # unfortunately there are quite some duplications that are hard to refactor + #- godot # not much value + #- intrange # we support go 1.19, enable this when / if updated to go 1.22 + #- noctx # passing ctx would be backward incompatible. timeout can be set on the http client (and is set by default) + # - typecheck # doesn't seem to work in the CI + + ## disabled + #- containedctx # detects struct contained context.Context field + #- contextcheck # [too many false positives] checks the function whether use a non-inherited context + #- depguard # [replaced by gomodguard] checks if package imports are in a list of acceptable packages + #- dogsled # checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) + #- dupword # [useless without config] checks for duplicate words in the source code + #- err113 # [too strict] checks the errors handling expressions + #- errchkjson # [don't see profit + I'm against of omitting errors like in the first example https://github.com/breml/errchkjson] checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted + #- execinquery # [deprecated] checks query string in Query function which reads your Go src files and warning it finds + #- exportloopref # checks for pointers to enclosing loop variables: no longer relevant in go 1.22 + #- forbidigo # forbids identifiers + #- forcetypeassert # [replaced by errcheck] finds forced type assertions + #- gofmt # [replaced by goimports] checks whether code was gofmt-ed + #- gofumpt # [replaced by goimports, gofumports is not available yet] checks whether code was gofumpt-ed + #- gosmopolitan # reports certain i18n/l10n anti-patterns in your Go codebase + #- grouper # analyzes expression groups + #- importas # enforces consistent import aliases + #- exportloopref # checks for pointers to enclosing loop variables: no longer relevant in go 1.22 + #- maintidx # measures the maintainability index of each function + #- misspell # [useless] finds commonly misspelled English words in comments + #- musttag # enforces field tags in (un)marshaled structs + #- nlreturn # [too strict and mostly code is not more readable] checks for a new line before return and branch statements to increase code clarity + #- paralleltest # [too many false positives] detects missing usage of t.Parallel() method in your Go test + #- tagliatelle # checks the struct tags + #- testpackage # makes you use a separate _test package + #- thelper # detects golang test helpers without t.Helper() call and checks the consistency of test helpers + #- wsl # [too strict and mostly code is not more readable] whitespace linter forces you to use empty lines + + +issues: + # Maximum count of issues with the same text. + # Set to 0 to disable. + # Default: 3 + max-same-issues: 50 + + exclude-dirs: + - tmp + + exclude-rules: + - source: "(noinspection|TODO)" + linters: [ godot ] + - source: "//noinspection" + linters: [ gocritic ] + - path: "_test\\.go" + linters: + - bodyclose + - dupl + - funlen + - goconst + - gosec + - noctx + - wrapcheck + - text: 'shadow: declaration of "(err|ctx)" shadows declaration at' + linters: [ govet ] diff --git a/Makefile b/Makefile index e514fcd..f7fbaaa 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,24 @@ -generate: +# The default "help" goal nicely prints all the available goals based on the funny looking ## comments. +# Source: https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html +.DEFAULT_GOAL := help +.PHONY: help +help: ## Display this help + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +GOLANGCI_LINT_VERSION := v1.61.0 + +.PHONY: generate +generate: ## Generate easyjson go generate ./... -test: - go test ./... +.PHONY: golangci-lint +golangci-lint: + go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) + +.PHONY: lint +lint: golangci-lint ## Run the linters + golangci-lint run + +.PHONY: test +test: ## Run the unit tests + go test -cover ./... diff --git a/alpaca/entities.go b/alpaca/entities.go index 9c367a4..a7a53a4 100644 --- a/alpaca/entities.go +++ b/alpaca/entities.go @@ -9,10 +9,9 @@ import ( "time" "cloud.google.com/go/civil" - "github.com/shopspring/decimal" - // Required for easyjson generation _ "github.com/mailru/easyjson/gen" + "github.com/shopspring/decimal" ) //go:generate go install github.com/mailru/easyjson/...@v0.7.7 diff --git a/alpaca/rest.go b/alpaca/rest.go index 00a2a61..f406c4b 100644 --- a/alpaca/rest.go +++ b/alpaca/rest.go @@ -126,6 +126,7 @@ func (c *Client) GetAccount() (*Account, error) { if err != nil { return nil, err } + defer closeResp(resp) var account Account if err = unmarshal(resp, &account); err != nil { @@ -145,6 +146,7 @@ func (c *Client) GetAccountConfigurations() (*AccountConfigurations, error) { if err != nil { return nil, err } + defer closeResp(resp) var configs AccountConfigurations if err = unmarshal(resp, &configs); err != nil { @@ -172,6 +174,7 @@ func (c *Client) UpdateAccountConfigurations(req UpdateAccountConfigurationsRequ if err != nil { return nil, err } + defer closeResp(resp) var configs AccountConfigurations if err = unmarshal(resp, &configs); err != nil { @@ -229,6 +232,7 @@ func (c *Client) GetAccountActivities(req GetAccountActivitiesRequest) ([]Accoun if err != nil { return nil, err } + defer closeResp(resp) var activities accountSlice if err = unmarshal(resp, &activities); err != nil { @@ -268,6 +272,7 @@ func (c *Client) GetPortfolioHistory(req GetPortfolioHistoryRequest) (*Portfolio if err != nil { return nil, err } + defer closeResp(resp) var history PortfolioHistory if err = unmarshal(resp, &history); err != nil { @@ -287,6 +292,7 @@ func (c *Client) GetPositions() ([]Position, error) { if err != nil { return nil, err } + defer closeResp(resp) var positions positionSlice if err = unmarshal(resp, &positions); err != nil { @@ -310,6 +316,7 @@ func (c *Client) GetPosition(symbol string) (*Position, error) { if err != nil { return nil, err } + defer closeResp(resp) var position Position if err = unmarshal(resp, &position); err != nil { @@ -339,6 +346,7 @@ func (c *Client) CloseAllPositions(req CloseAllPositionsRequest) ([]Order, error if err != nil { return nil, err } + defer closeResp(resp) var closeAllPositions closeAllPositionsSlice if err = unmarshal(resp, &closeAllPositions); err != nil { @@ -399,6 +407,7 @@ func (c *Client) ClosePosition(symbol string, req ClosePositionRequest) (*Order, if err != nil { return nil, err } + defer closeResp(resp) var order Order if err = unmarshal(resp, &order); err != nil { @@ -418,6 +427,7 @@ func (c *Client) GetClock() (*Clock, error) { if err != nil { return nil, err } + defer closeResp(resp) var clock Clock if err = unmarshal(resp, &clock); err != nil { @@ -451,6 +461,7 @@ func (c *Client) GetCalendar(req GetCalendarRequest) ([]CalendarDay, error) { if err != nil { return nil, err } + defer closeResp(resp) var calendar calendarDaySlice if err = unmarshal(resp, &calendar); err != nil { @@ -509,6 +520,7 @@ func (c *Client) GetOrders(req GetOrdersRequest) ([]Order, error) { if err != nil { return nil, err } + defer closeResp(resp) var orders orderSlice if err = unmarshal(resp, &orders); err != nil { @@ -556,6 +568,7 @@ func (c *Client) PlaceOrder(req PlaceOrderRequest) (*Order, error) { if err != nil { return nil, err } + defer closeResp(resp) var order Order if err = unmarshal(resp, &order); err != nil { @@ -575,6 +588,7 @@ func (c *Client) GetOrder(orderID string) (*Order, error) { if err != nil { return nil, err } + defer closeResp(resp) var order Order if err = unmarshal(resp, &order); err != nil { @@ -598,6 +612,7 @@ func (c *Client) GetOrderByClientOrderID(clientOrderID string) (*Order, error) { if err != nil { return nil, err } + defer closeResp(resp) var order Order if err = unmarshal(resp, &order); err != nil { @@ -626,6 +641,7 @@ func (c *Client) ReplaceOrder(orderID string, req ReplaceOrderRequest) (*Order, if err != nil { return nil, err } + defer closeResp(resp) var order Order if err = unmarshal(resp, &order); err != nil { @@ -692,6 +708,7 @@ func (c *Client) GetAssets(req GetAssetsRequest) ([]Asset, error) { if err != nil { return nil, err } + defer closeResp(resp) var assets assetSlice if err = unmarshal(resp, &assets); err != nil { @@ -711,6 +728,7 @@ func (c *Client) GetAsset(symbol string) (*Asset, error) { if err != nil { return nil, err } + defer closeResp(resp) var asset Asset if err = unmarshal(resp, &asset); err != nil { @@ -759,6 +777,7 @@ func (c *Client) GetAnnouncements(req GetAnnouncementsRequest) ([]Announcement, if err != nil { return nil, err } + defer closeResp(resp) var announcements announcementSlice if err = unmarshal(resp, &announcements); err != nil { @@ -779,6 +798,7 @@ func (c *Client) GetAnnouncement(announcementID string) (*Announcement, error) { if err != nil { return nil, err } + defer closeResp(resp) var announcement Announcement if err = unmarshal(resp, &announcement); err != nil { @@ -798,6 +818,7 @@ func (c *Client) GetWatchlists() ([]Watchlist, error) { if err != nil { return nil, err } + defer closeResp(resp) var watchlists watchlistSlice if err = unmarshal(resp, &watchlists); err != nil { @@ -816,6 +837,7 @@ func (c *Client) CreateWatchlist(req CreateWatchlistRequest) (*Watchlist, error) if err != nil { return nil, err } + defer closeResp(resp) watchlist := &Watchlist{} if err = unmarshal(resp, watchlist); err != nil { @@ -834,6 +856,7 @@ func (c *Client) GetWatchlist(watchlistID string) (*Watchlist, error) { if err != nil { return nil, err } + defer closeResp(resp) watchlist := &Watchlist{} if err = unmarshal(resp, watchlist); err != nil { @@ -852,6 +875,7 @@ func (c *Client) UpdateWatchlist(watchlistID string, req UpdateWatchlistRequest) if err != nil { return nil, err } + defer closeResp(resp) watchlist := &Watchlist{} if err = unmarshal(resp, watchlist); err != nil { @@ -860,7 +884,7 @@ func (c *Client) UpdateWatchlist(watchlistID string, req UpdateWatchlistRequest) return watchlist, nil } -var ErrSymbolMissing = fmt.Errorf("symbol missing from request") +var ErrSymbolMissing = errors.New("symbol missing from request") func (c *Client) AddSymbolToWatchlist(watchlistID string, req AddSymbolToWatchlistRequest) (*Watchlist, error) { if req.Symbol == "" { @@ -876,6 +900,7 @@ func (c *Client) AddSymbolToWatchlist(watchlistID string, req AddSymbolToWatchli if err != nil { return nil, err } + defer closeResp(resp) watchlist := &Watchlist{} if err = unmarshal(resp, watchlist); err != nil { @@ -894,8 +919,12 @@ func (c *Client) RemoveSymbolFromWatchlist(watchlistID string, req RemoveSymbolF return err } - _, err = c.delete(u) - return err + resp, err := c.delete(u) + if err != nil { + return err + } + closeResp(resp) + return nil } func (c *Client) DeleteWatchlist(watchlistID string) error { @@ -904,8 +933,12 @@ func (c *Client) DeleteWatchlist(watchlistID string) error { return err } - _, err = c.delete(u) - return err + resp, err := c.delete(u) + if err != nil { + return err + } + closeResp(resp) + return nil } // GetAccount returns the user's account information @@ -1132,10 +1165,11 @@ func verify(resp *http.Response) error { } func unmarshal(resp *http.Response, v easyjson.Unmarshaler) error { - defer func() { - // The underlying TCP connection can not be reused if the body is not fully read - _, _ = io.Copy(io.Discard, resp.Body) - resp.Body.Close() - }() return easyjson.UnmarshalFromReader(resp.Body, v) } + +func closeResp(resp *http.Response) { + // The underlying TCP connection can not be reused if the body is not fully read + _, _ = io.Copy(io.Discard, resp.Body) + resp.Body.Close() +} diff --git a/alpaca/rest_test.go b/alpaca/rest_test.go index 99188d9..9b2feb0 100644 --- a/alpaca/rest_test.go +++ b/alpaca/rest_test.go @@ -32,7 +32,7 @@ func TestDefaultDo(t *testing.T) { RetryLimit: 2, BaseURL: ts.URL, }) - req, err := http.NewRequest("GET", ts.URL+"/custompath", nil) + req, err := http.NewRequest(http.MethodGet, ts.URL+"/custompath", nil) require.NoError(t, err) resp, err := defaultDo(c, req) require.NoError(t, err) @@ -43,7 +43,7 @@ func TestDefaultDo(t *testing.T) { func TestDefaultDo_SuccessfulRetries(t *testing.T) { i := 0 - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { if i < 3 { i++ http.Error(w, "too many requests", http.StatusTooManyRequests) @@ -54,7 +54,7 @@ func TestDefaultDo_SuccessfulRetries(t *testing.T) { c := NewClient(ClientOpts{ RetryDelay: time.Nanosecond, }) - req, err := http.NewRequest("GET", ts.URL, nil) + req, err := http.NewRequest(http.MethodGet, ts.URL, nil) require.NoError(t, err) resp, err := defaultDo(c, req) require.NoError(t, err) @@ -65,7 +65,7 @@ func TestDefaultDo_SuccessfulRetries(t *testing.T) { func TestDefaultDo_TooManyRetries(t *testing.T) { i := 0 - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { if i < 4 { i++ http.Error(w, "too many requests", http.StatusTooManyRequests) @@ -76,7 +76,7 @@ func TestDefaultDo_TooManyRetries(t *testing.T) { c := NewClient(ClientOpts{ RetryDelay: time.Nanosecond, }) - req, err := http.NewRequest("GET", ts.URL, nil) + req, err := http.NewRequest(http.MethodGet, ts.URL, nil) require.NoError(t, err) _, err = defaultDo(c, req) require.Error(t, err) @@ -84,11 +84,11 @@ func TestDefaultDo_TooManyRetries(t *testing.T) { func TestDefaultDo_Error(t *testing.T) { resp := `{"code":1234567,"message":"custom error message","other_field":"x"}` - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { http.Error(w, resp, http.StatusBadRequest) })) c := DefaultClient - req, err := http.NewRequest("GET", ts.URL, nil) + req, err := http.NewRequest(http.MethodGet, ts.URL, nil) require.NoError(t, err) _, err = defaultDo(c, req) var apiErr *APIError @@ -104,7 +104,7 @@ func TestGetAccount(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { account := Account{ ID: "some_id", } @@ -119,8 +119,8 @@ func TestGetAccount(t *testing.T) { assert.Equal(t, "some_id", acct.ID) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } _, err = c.GetAccount() @@ -131,7 +131,7 @@ func TestGetPositions(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { positions := []Position{ {Symbol: "APCA"}, } @@ -145,8 +145,8 @@ func TestGetPositions(t *testing.T) { assert.Len(t, positions, 1) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } positions, err = c.GetPositions() @@ -161,7 +161,7 @@ func TestCancelPosition(t *testing.T) { ClientOrderID: "0571ce61-bf65-4f0c-b3de-6f42de628422", Symbol: "AAPL", } - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/positions/AAPL", req.URL.Path) assert.Equal(t, http.MethodDelete, req.Method) assert.Equal(t, "0.12345678", req.URL.Query().Get("qty")) @@ -182,10 +182,18 @@ func TestCancelAllPositions(t *testing.T) { c := DefaultClient closeAllPositionsResponse := []closeAllPositionsResponse{ - {Symbol: "AAPL", Status: 200, Body: json.RawMessage(`{"id":"0571ce61-bf65-4f0c-b3de-6f42ce628422", "symbol": "AAPL"}`)}, - {Symbol: "TSLA", Status: 422, Body: json.RawMessage(`{"code": 42210000, "message": "error"}`)}, + { + Symbol: "AAPL", + Status: 200, + Body: json.RawMessage(`{"id":"0571ce61-bf65-4f0c-b3de-6f42ce628422", "symbol": "AAPL"}`), + }, + { + Symbol: "TSLA", + Status: 422, + Body: json.RawMessage(`{"code": 42210000, "message": "error"}`), + }, } - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/positions", req.URL.Path) assert.Equal(t, http.MethodDelete, req.Method) assert.Equal(t, "true", req.URL.Query().Get("cancel_orders")) @@ -204,7 +212,7 @@ func TestCancelAllPositions(t *testing.T) { func TestGetClock(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { clock := Clock{ Timestamp: time.Now(), IsOpen: true, @@ -222,8 +230,8 @@ func TestGetClock(t *testing.T) { assert.True(t, clock.IsOpen) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } _, err = c.GetClock() @@ -233,7 +241,7 @@ func TestGetClock(t *testing.T) { func TestGetCalendar(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "2018-01-01", req.URL.Query().Get("start")) assert.Equal(t, "2018-01-02", req.URL.Query().Get("end")) calendar := []CalendarDay{ @@ -256,8 +264,8 @@ func TestGetCalendar(t *testing.T) { assert.Len(t, calendar, 1) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } calendar, err = c.GetCalendar(GetCalendarRequest{}) @@ -267,7 +275,7 @@ func TestGetCalendar(t *testing.T) { func TestGetOrders_EmptyRequest(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/orders", req.URL.Path) assert.Equal(t, "", req.URL.Query().Get("status")) assert.Equal(t, "", req.URL.Query().Get("after")) @@ -298,7 +306,7 @@ func TestGetOrders_EmptyRequest(t *testing.T) { func TestGetOrders(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/orders", req.URL.Path) assert.Equal(t, "all", req.URL.Query().Get("status")) assert.Equal(t, "2021-04-03T00:00:00Z", req.URL.Query().Get("after")) @@ -335,8 +343,8 @@ func TestGetOrders(t *testing.T) { assert.Equal(t, "some_id", orders[0].ID) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } orders, err = c.GetOrders(req) @@ -347,7 +355,7 @@ func TestGetOrders(t *testing.T) { func TestPlaceOrder(t *testing.T) { c := DefaultClient // successful (w/ Qty) - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { por := PlaceOrderRequest{} if err := json.NewDecoder(req.Body).Decode(&por); err != nil { return nil, err @@ -396,8 +404,8 @@ func TestPlaceOrder(t *testing.T) { assert.Equal(t, req.Type, order.Type) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } _, err = c.PlaceOrder(req) @@ -407,7 +415,7 @@ func TestPlaceOrder(t *testing.T) { func TestGetOrder(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { order := Order{ ID: "some_order_id", } @@ -421,8 +429,8 @@ func TestGetOrder(t *testing.T) { assert.NotNil(t, order) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } _, err = c.GetOrder("some_order_id") @@ -432,7 +440,7 @@ func TestGetOrder(t *testing.T) { func TestGetOrderByClientOrderId(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { order := Order{ ClientOrderID: "some_client_order_id", } @@ -446,8 +454,8 @@ func TestGetOrderByClientOrderId(t *testing.T) { assert.NotNil(t, order) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } _, err = c.GetOrderByClientOrderID("some_client_order_id") @@ -457,9 +465,9 @@ func TestGetOrderByClientOrderId(t *testing.T) { func TestClient_GetAnnouncements(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/corporate_actions/announcements", req.URL.Path) - assert.Equal(t, "GET", req.Method) + assert.Equal(t, http.MethodGet, req.Method) assert.Equal(t, "AAPL", req.URL.Query().Get("symbol")) assert.Equal(t, "some_cusip", req.URL.Query().Get("cusip")) assert.Equal(t, "declaration_date", req.URL.Query().Get("date_type")) @@ -492,9 +500,9 @@ func TestClient_GetAnnouncements(t *testing.T) { func TestClient_GetAnnouncement(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/corporate_actions/announcements/123", req.URL.Path) - assert.Equal(t, "GET", req.Method) + assert.Equal(t, http.MethodGet, req.Method) announcement := Announcement{ ID: "some_id", @@ -513,9 +521,9 @@ func TestClient_GetAnnouncement(t *testing.T) { func TestClient_GetWatchlists(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/watchlists", req.URL.Path) - assert.Equal(t, "GET", req.Method) + assert.Equal(t, http.MethodGet, req.Method) watchlists := []Watchlist{ { @@ -545,7 +553,7 @@ func TestClient_GetWatchlists(t *testing.T) { func TestClient_CreateWatchlist(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/watchlists", req.URL.Path) assert.Equal(t, "POST", req.Method) @@ -582,9 +590,9 @@ func TestClient_CreateWatchlist(t *testing.T) { func TestClient_GetWatchlist(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/watchlists/123", req.URL.Path) - assert.Equal(t, "GET", req.Method) + assert.Equal(t, http.MethodGet, req.Method) watchlist := Watchlist{ AccountID: "123", @@ -616,7 +624,7 @@ func TestClient_GetWatchlist(t *testing.T) { func TestClient_UpdateWatchlist(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/watchlists/123", req.URL.Path) assert.Equal(t, "PUT", req.Method) @@ -653,7 +661,7 @@ func TestClient_UpdateWatchlist(t *testing.T) { func TestClient_DeleteWatchlist(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/watchlists/123", req.URL.Path) assert.Equal(t, "DELETE", req.Method) @@ -670,7 +678,7 @@ func TestClient_AddSymbolToWatchlist(t *testing.T) { t.Run("happy path", func(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/watchlists/123", req.URL.Path) assert.Equal(t, "POST", req.Method) @@ -706,7 +714,7 @@ func TestClient_AddSymbolToWatchlist(t *testing.T) { t.Run("error: symbol not found", func(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/watchlists/123", req.URL.Path) assert.Equal(t, "POST", req.Method) @@ -726,7 +734,7 @@ func TestClient_RemoveSymbolFromWatchlist(t *testing.T) { t.Run("happy path", func(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/watchlists/123/AAPL", req.URL.Path) assert.Equal(t, "DELETE", req.Method) @@ -744,7 +752,7 @@ func TestClient_RemoveSymbolFromWatchlist(t *testing.T) { t.Run("error: symbol is required", func(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/watchlists/123/AAPL", req.URL.Path) assert.Equal(t, "DELETE", req.Method) @@ -763,24 +771,24 @@ func TestClient_RemoveSymbolFromWatchlist(t *testing.T) { func TestCancelOrder(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { return &http.Response{}, nil } - assert.Nil(t, c.CancelOrder("some_order_id")) + require.NoError(t, c.CancelOrder("some_order_id")) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } - assert.NotNil(t, c.CancelOrder("some_order_id")) + assert.Error(t, c.CancelOrder("some_order_id")) } func TestGetAssets(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "active", req.URL.Query().Get("status")) assets := []Asset{ {ID: "some_id"}, @@ -798,8 +806,8 @@ func TestGetAssets(t *testing.T) { assert.Equal(t, "some_id", assets[0].ID) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } _, err = c.GetAssets(GetAssetsRequest{}) @@ -809,7 +817,7 @@ func TestGetAssets(t *testing.T) { func TestGetAsset(t *testing.T) { c := DefaultClient // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { asset := Asset{ID: "some_id"} return &http.Response{ Body: genBody(asset), @@ -821,8 +829,8 @@ func TestGetAsset(t *testing.T) { assert.NotNil(t, asset) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } asset, err = c.GetAsset("APCA") @@ -847,25 +855,25 @@ func TestGetAssetFromJSON(t *testing.T) { }` // successful - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { return &http.Response{ Body: io.NopCloser(strings.NewReader(assetJSON)), }, nil } asset, err := c.GetAsset("APCA") - assert.Nil(t, err) + require.NoError(t, err) assert.Equal(t, USEquity, asset.Class) assert.True(t, asset.Fractionable) assert.NotNil(t, asset) // api failure - c.do = func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } asset, err = c.GetAsset("APCA") - assert.NotNil(t, err) + require.Error(t, err) assert.Nil(t, asset) } @@ -875,7 +883,7 @@ func TestTestVerify(t *testing.T) { StatusCode: http.StatusOK, } - assert.Nil(t, verify(resp)) + require.NoError(t, verify(resp)) // 500 resp = &http.Response{ @@ -883,12 +891,12 @@ func TestTestVerify(t *testing.T) { Body: genBody(APIError{Code: 1010101, Message: "server is dead"}), } - assert.NotNil(t, verify(resp)) + assert.Error(t, verify(resp)) } func TestOTOCOOrders(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { or := PlaceOrderRequest{} if err := json.NewDecoder(req.Body).Decode(&or); err != nil { return nil, err @@ -930,7 +938,7 @@ func TestOTOCOOrders(t *testing.T) { func TestGetAccountActivities(t *testing.T) { c := DefaultClient // happy path - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { // https://alpaca.markets/docs/api-documentation/api-v2/account-activities/#nontradeactivity-entity nta := []map[string]interface{}{ { @@ -976,7 +984,7 @@ func TestGetAccountActivities(t *testing.T) { activities, err := c.GetAccountActivities(GetAccountActivitiesRequest{ ActivityTypes: []string{"DIV", "FILL"}, }) - assert.NoError(t, err) + require.NoError(t, err) assert.Len(t, activities, 3) activity1 := activities[0] assert.Equal(t, civil.Date{Year: 2019, Month: 8, Day: 1}, activity1.Date) @@ -1011,19 +1019,19 @@ func TestGetAccountActivities(t *testing.T) { assert.Equal(t, "partially_filled", activity3.OrderStatus) // error was returned - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { return &http.Response{}, &APIError{StatusCode: 500, Message: "internal server error"} } _, err = c.GetAccountActivities(GetAccountActivitiesRequest{}) - assert.NotNil(t, err) + require.Error(t, err) var apiErr *APIError - assert.ErrorAs(t, err, &apiErr) + require.ErrorAs(t, err, &apiErr) assert.Equal(t, 500, apiErr.StatusCode) assert.Equal(t, "internal server error", apiErr.Message) // test filter by date and URI - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { getQuery := req.URL.Query() assert.Equal(t, "/v2/account/activities", req.URL.Path) diff --git a/alpaca/stream.go b/alpaca/stream.go index d192285..c7a4c9e 100644 --- a/alpaca/stream.go +++ b/alpaca/stream.go @@ -23,9 +23,11 @@ type StreamTradeUpdatesRequest struct { } // StreamTradeUpdates streams the trade updates of the account. -func (c *Client) StreamTradeUpdates(ctx context.Context, handler func(TradeUpdate), req StreamTradeUpdatesRequest) error { +func (c *Client) StreamTradeUpdates( + ctx context.Context, handler func(TradeUpdate), req StreamTradeUpdatesRequest, +) error { transport := http.Transport{ - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + Dial: func(network, addr string) (net.Conn, error) { return net.DialTimeout(network, addr, 5*time.Second) }, } @@ -52,7 +54,7 @@ func (c *Client) StreamTradeUpdates(ctx context.Context, handler func(TradeUpdat } u.RawQuery = q.Encode() - request, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil) + request, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) if err != nil { return err } diff --git a/go.mod b/go.mod index e7123e4..eb1acdf 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/alpacahq/alpaca-trade-api-go/v3 -go 1.19 +go 1.20 require ( cloud.google.com/go v0.99.0 diff --git a/marketdata/entities.go b/marketdata/entities.go index e6057d5..c24c5ad 100644 --- a/marketdata/entities.go +++ b/marketdata/entities.go @@ -5,7 +5,6 @@ import ( "time" "cloud.google.com/go/civil" - // Required for easyjson generation _ "github.com/mailru/easyjson/gen" ) diff --git a/marketdata/options.go b/marketdata/options.go index fa55bdd..a23e52d 100644 --- a/marketdata/options.go +++ b/marketdata/options.go @@ -171,7 +171,9 @@ func (c *Client) GetLatestOptionTrade(symbol string, req GetLatestOptionTradeReq } // GetLatestOptionTrades returns the latest option trades for the given symbols -func (c *Client) GetLatestOptionTrades(symbols []string, req GetLatestOptionTradeRequest) (map[string]OptionTrade, error) { +func (c *Client) GetLatestOptionTrades( + symbols []string, req GetLatestOptionTradeRequest, +) (map[string]OptionTrade, error) { u, err := url.Parse(fmt.Sprintf("%s/%s/trades/latest", c.opts.BaseURL, optionPrefix)) if err != nil { return nil, err @@ -185,6 +187,7 @@ func (c *Client) GetLatestOptionTrades(symbols []string, req GetLatestOptionTrad if err != nil { return nil, err } + defer closeResp(resp) var latestTradesResp latestOptionTradesResponse if err = unmarshal(resp, &latestTradesResp); err != nil { @@ -211,7 +214,9 @@ func (c *Client) GetLatestOptionQuote(symbol string, req GetLatestOptionQuoteReq } // GetLatestOptionQuotes returns the latest option quotes for the given symbols -func (c *Client) GetLatestOptionQuotes(symbols []string, req GetLatestOptionQuoteRequest) (map[string]OptionQuote, error) { +func (c *Client) GetLatestOptionQuotes( + symbols []string, req GetLatestOptionQuoteRequest, +) (map[string]OptionQuote, error) { u, err := url.Parse(fmt.Sprintf("%s/%s/quotes/latest", c.opts.BaseURL, optionPrefix)) if err != nil { return nil, err @@ -225,6 +230,7 @@ func (c *Client) GetLatestOptionQuotes(symbols []string, req GetLatestOptionQuot if err != nil { return nil, err } + defer closeResp(resp) var latestQuotesResp latestOptionQuotesResponse if err = unmarshal(resp, &latestQuotesResp); err != nil { diff --git a/marketdata/options_test.go b/marketdata/options_test.go index a42af0f..9137179 100644 --- a/marketdata/options_test.go +++ b/marketdata/options_test.go @@ -15,7 +15,7 @@ import ( func TestGetOptionTrades(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { resp := `{"next_page_token":"QUFQTDI0MDMwOEMwMDE3MjUwMHwxNzA5NjU3OTk5MTA5MzE5NDI0fFU=","trades":{"AAPL240308C00172500":[{"c":"g","p":0.98,"s":4,"t":"2024-03-05T16:59:59.816906752Z","x":"C"},{"c":"I","p":0.99,"s":1,"t":"2024-03-05T16:59:59.109319424Z","x":"U"}]}}` //nolint:lll return &http.Response{ Body: io.NopCloser(strings.NewReader(resp)), @@ -48,7 +48,7 @@ func TestGetOptionTrades(t *testing.T) { func TestGetOptionBars(t *testing.T) { c := DefaultClient resp := `{"bars":{"AAPL240308C00172500":[{"c":1.1,"h":1.26,"l":1.1,"n":15,"o":1.23,"t":"2024-03-05T14:00:00Z","v":82,"vw":1.187683},{"c":0.99,"h":1.14,"l":0.83,"n":1545,"o":1,"t":"2024-03-05T15:00:00Z","v":9959,"vw":0.959978},{"c":0.98,"h":1.15,"l":0.85,"n":1075,"o":0.93,"t":"2024-03-05T16:00:00Z","v":7637,"vw":0.965448},{"c":0.97,"h":1.15,"l":0.93,"n":1096,"o":0.99,"t":"2024-03-05T17:00:00Z","v":8483,"vw":1.028201},{"c":0.91,"h":1.1,"l":0.88,"n":903,"o":0.96,"t":"2024-03-05T18:00:00Z","v":7925,"vw":0.96723},{"c":0.9,"h":1,"l":0.88,"n":423,"o":0.9,"t":"2024-03-05T19:00:00Z","v":2895,"vw":0.931516},{"c":0.97,"h":1,"l":0.87,"n":543,"o":0.92,"t":"2024-03-05T20:00:00Z","v":5669,"vw":0.9383}]},"next_page_token":null}` //nolint:lll - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v1beta1/options/bars", req.URL.Path) assert.Equal(t, "AAPL240308C00172500", req.URL.Query().Get("symbols")) assert.Equal(t, "2024-03-05T00:00:00Z", req.URL.Query().Get("start")) @@ -79,7 +79,8 @@ func TestGetOptionBars(t *testing.T) { func TestGetLatestOptionTrade(t *testing.T) { c := DefaultClient - c.do = mockResp(`{"trades":{"BABA260116P00125000":{"c":"f","p":49.61,"s":1,"t":"2024-02-26T18:23:18.79373184Z","x":"D"}}}`) + c.do = mockResp( + `{"trades":{"BABA260116P00125000":{"c":"f","p":49.61,"s":1,"t":"2024-02-26T18:23:18.79373184Z","x":"D"}}}`) got, err := c.GetLatestOptionTrade("BABA260116P00125000", GetLatestOptionTradeRequest{}) require.NoError(t, err) require.NotNil(t, got) @@ -101,7 +102,7 @@ func TestGetLatestOptionTrade(t *testing.T) { func TestGetLatestOptionQuote(t *testing.T) { c := DefaultClient - c.do = mockResp(`{"quotes":{"SPXW240327P04925000":{"ap":11.7,"as":103,"ax":"C","bp":11.4,"bs":172,"bx":"C","c":"A","t":"2024-03-07T13:54:51.985563136Z"}}}`) + c.do = mockResp(`{"quotes":{"SPXW240327P04925000":{"ap":11.7,"as":103,"ax":"C","bp":11.4,"bs":172,"bx":"C","c":"A","t":"2024-03-07T13:54:51.985563136Z"}}}`) //nolint:lll got, err := c.GetLatestOptionQuote("SPXW240327P04925000", GetLatestOptionQuoteRequest{}) require.NoError(t, err) require.NotNil(t, got) @@ -119,7 +120,7 @@ func TestGetLatestOptionQuote(t *testing.T) { func TestGetOptionSnapshot(t *testing.T) { c := DefaultClient - c.do = mockResp(`{"snapshots":{"SPXW240327P04925000":{"latestQuote":{"ap":11.6,"as":59,"ax":"C","bp":11.3,"bs":180,"bx":"C","c":"A","t":"2024-03-07T13:56:22.278961408Z"},"latestTrade":{"c":"g","p":14.85,"s":2,"t":"2024-03-05T16:36:57.709309696Z","x":"C"}}}}`) + c.do = mockResp(`{"snapshots":{"SPXW240327P04925000":{"latestQuote":{"ap":11.6,"as":59,"ax":"C","bp":11.3,"bs":180,"bx":"C","c":"A","t":"2024-03-07T13:56:22.278961408Z"},"latestTrade":{"c":"g","p":14.85,"s":2,"t":"2024-03-05T16:36:57.709309696Z","x":"C"}}}}`) //nolint:lll got, err := c.GetOptionSnapshot("SPXW240327P04925000", GetOptionSnapshotRequest{}) require.NoError(t, err) require.NotNil(t, got) @@ -164,7 +165,7 @@ func TestGetOptionChainWithFilters(t *testing.T) { //nolint:lll firstResp := `{"next_page_token":"QUFQTDI0MDQyNkMwMDE1MjUwMA==","snapshots":{"AAPL240426C00152500":{"latestQuote":{"ap":17,"as":91,"ax":"B","bp":16.25,"bs":80,"bx":"B","c":" ","t":"2024-04-24T19:59:59.782060288Z"},"latestTrade":{"c":"a","p":15.87,"s":1,"t":"2024-04-24T16:46:16.763406848Z","x":"I"}}}}` secondResp := `{"next_page_token":null,"snapshots":{"AAPL240426C00155000":{"greeks":{"delta":0.9567110374646104,"gamma":0.010515010903989475,"rho":0.004041091409185355,"theta":-0.42275702792812153,"vega":0.008131530784084512},"impliedVolatility":0.9871160931510816,"latestQuote":{"ap":14.5,"as":86,"ax":"Q","bp":13.6,"bs":91,"bx":"B","c":" ","t":"2024-04-24T19:59:59.794910976Z"},"latestTrade":{"c":"a","p":14.28,"s":1,"t":"2024-04-24T19:42:55.36938496Z","x":"X"}}}}` //nolint:lll - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v1beta1/options/snapshots/AAPL", req.URL.Path) q := req.URL.Query() assert.Equal(t, "1", q.Get("limit")) diff --git a/marketdata/rest.go b/marketdata/rest.go index 3039aba..2d5715c 100644 --- a/marketdata/rest.go +++ b/marketdata/rest.go @@ -2,6 +2,7 @@ package marketdata import ( "compress/gzip" + "errors" "fmt" "io" "net/http" @@ -191,7 +192,7 @@ func setQueryLimit(q url.Values, totalLimit, pageLimit, received, maxLimit int) } if limit != 0 { - q.Set("limit", fmt.Sprintf("%d", limit)) + q.Set("limit", strconv.Itoa(limit)) } } @@ -578,6 +579,7 @@ func (c *Client) GetLatestBars(symbols []string, req GetLatestBarRequest) (map[s if err != nil { return nil, err } + defer closeResp(resp) var latestBarsResp latestBarsResponse if err = unmarshal(resp, &latestBarsResp); err != nil { @@ -620,6 +622,7 @@ func (c *Client) GetLatestTrades(symbols []string, req GetLatestTradeRequest) (m if err != nil { return nil, err } + defer closeResp(resp) var latestTradesResp latestTradesResponse if err = unmarshal(resp, &latestTradesResp); err != nil { @@ -662,6 +665,7 @@ func (c *Client) GetLatestQuotes(symbols []string, req GetLatestQuoteRequest) (m if err != nil { return nil, err } + defer closeResp(resp) var latestQuotesResp latestQuotesResponse if err = unmarshal(resp, &latestQuotesResp); err != nil { @@ -700,6 +704,7 @@ func (c *Client) GetSnapshots(symbols []string, req GetSnapshotRequest) (map[str if err != nil { return nil, err } + defer closeResp(resp) var snapshots snapshotsResponse if err = unmarshal(resp, &snapshots); err != nil { @@ -1001,6 +1006,7 @@ func (c *Client) GetLatestCryptoBars(symbols []string, req GetLatestCryptoBarReq if err != nil { return nil, err } + defer closeResp(resp) var latestBarsResp latestCryptoBarsResponse if err = unmarshal(resp, &latestBarsResp); err != nil { @@ -1027,7 +1033,9 @@ func (c *Client) GetLatestCryptoTrade(symbol string, req GetLatestCryptoTradeReq } // GetLatestCryptoTrades returns the latest trades for the given crypto symbols -func (c *Client) GetLatestCryptoTrades(symbols []string, req GetLatestCryptoTradeRequest) (map[string]CryptoTrade, error) { +func (c *Client) GetLatestCryptoTrades( + symbols []string, req GetLatestCryptoTradeRequest, +) (map[string]CryptoTrade, error) { u, err := url.Parse(fmt.Sprintf("%s/%s/%s/latest/trades", c.opts.BaseURL, cryptoPrefix, c.cryptoFeed(req.CryptoFeed))) if err != nil { @@ -1041,6 +1049,7 @@ func (c *Client) GetLatestCryptoTrades(symbols []string, req GetLatestCryptoTrad if err != nil { return nil, err } + defer closeResp(resp) var latestTradesResp latestCryptoTradesResponse if err = unmarshal(resp, &latestTradesResp); err != nil { @@ -1067,7 +1076,9 @@ func (c *Client) GetLatestCryptoQuote(symbol string, req GetLatestCryptoQuoteReq } // GetLatestCryptoQuotes returns the latest quotes for the given crypto symbols -func (c *Client) GetLatestCryptoQuotes(symbols []string, req GetLatestCryptoQuoteRequest) (map[string]CryptoQuote, error) { +func (c *Client) GetLatestCryptoQuotes( + symbols []string, req GetLatestCryptoQuoteRequest, +) (map[string]CryptoQuote, error) { u, err := url.Parse(fmt.Sprintf("%s/%s/%s/latest/quotes", c.opts.BaseURL, cryptoPrefix, c.cryptoFeed(req.CryptoFeed))) if err != nil { @@ -1081,6 +1092,7 @@ func (c *Client) GetLatestCryptoQuotes(symbols []string, req GetLatestCryptoQuot if err != nil { return nil, err } + defer closeResp(resp) var latestQuotesResp latestCryptoQuotesResponse if err = unmarshal(resp, &latestQuotesResp); err != nil { @@ -1121,6 +1133,7 @@ func (c *Client) GetCryptoSnapshots(symbols []string, req GetCryptoSnapshotReque if err != nil { return nil, err } + defer closeResp(resp) var snapshots CryptoSnapshots if err = unmarshal(resp, &snapshots); err != nil { @@ -1197,13 +1210,13 @@ func (c *Client) setNewsQuery(q url.Values, p GetNewsRequest) { // GetNews returns the news articles based on the given req. func (c *Client) GetNews(req GetNewsRequest) ([]News, error) { if req.TotalLimit < 0 { - return nil, fmt.Errorf("negative total limit") + return nil, errors.New("negative total limit") } if req.PageLimit < 0 { - return nil, fmt.Errorf("negative page limit") + return nil, errors.New("negative page limit") } if req.NoTotalLimit && req.TotalLimit != 0 { - return nil, fmt.Errorf("both NoTotalLimit and non-zero TotalLimit specified") + return nil, errors.New("both NoTotalLimit and non-zero TotalLimit specified") } u, err := url.Parse(fmt.Sprintf("%s/v1beta1/news", c.opts.BaseURL)) if err != nil { @@ -1513,11 +1526,6 @@ func (c *Client) get(u *url.URL) (*http.Response, error) { } func unmarshal(resp *http.Response, v easyjson.Unmarshaler) error { - defer func() { - // The underlying TCP connection can not be reused if the body is not fully read - _, _ = io.Copy(io.Discard, resp.Body) - resp.Body.Close() - }() var ( reader io.ReadCloser err error @@ -1534,3 +1542,9 @@ func unmarshal(resp *http.Response, v easyjson.Unmarshaler) error { } return easyjson.UnmarshalFromReader(reader, v) } + +func closeResp(resp *http.Response) { + // The underlying TCP connection can not be reused if the body is not fully read + _, _ = io.Copy(io.Discard, resp.Body) + resp.Body.Close() +} diff --git a/marketdata/rest_test.go b/marketdata/rest_test.go index 713525f..2092973 100644 --- a/marketdata/rest_test.go +++ b/marketdata/rest_test.go @@ -2,6 +2,7 @@ package marketdata import ( + "errors" "fmt" "io" "net/http" @@ -17,7 +18,7 @@ import ( ) func TestDefaultDo(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { fmt.Fprint(w, `{"bars":{"SPY":{"t":"2021-11-20T00:59:00Z","o":469.18,"h":469.18,"l":469.11,"c":469.17,"v":740,"n":11,"vw":469.1355}}}`) })) defer server.Close() @@ -30,14 +31,11 @@ func TestDefaultDo(t *testing.T) { } func TestDefaultDo_InternalServerError(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { http.Error(w, "internal server error", http.StatusInternalServerError) })) defer server.Close() - // instead of using the BaseURL opts, we test setting the base URL via environment variables - originalDataURL := os.Getenv("APCA_API_DATA_URL") - defer func() { os.Setenv("APCA_API_DATA_URL", originalDataURL) }() - require.NoError(t, os.Setenv("APCA_API_DATA_URL", server.URL)) + t.Setenv("APCA_API_DATA_URL", server.URL) client := NewClient(ClientOpts{ OAuth: "myoauthkey", RetryDelay: time.Nanosecond, @@ -49,7 +47,7 @@ func TestDefaultDo_InternalServerError(t *testing.T) { func TestDefaultDo_Retry(t *testing.T) { tryCount := 0 - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { switch tryCount { case 0, 2: http.Error(w, "too many requests", http.StatusTooManyRequests) @@ -73,7 +71,7 @@ func TestDefaultDo_Retry(t *testing.T) { func TestDefaultDo_TooMany429s(t *testing.T) { called := 0 - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { called++ http.Error(w, "too many requests", http.StatusTooManyRequests) })) @@ -91,7 +89,7 @@ func TestDefaultDo_TooMany429s(t *testing.T) { } func TestDefaultDo_Timeout(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { time.Sleep(time.Second) fmt.Fprint(w, `{"bars":{"SPY":{"t":"2021-11-20T00:59:00Z","o":469.18,"h":469.18,"l":469.11,"c":469.17,"v":740,"n":11,"vw":469.1355}}}`) })) @@ -106,17 +104,17 @@ func TestDefaultDo_Timeout(t *testing.T) { assert.Contains(t, err.Error(), "Timeout") } -func mockResp(resp string) func(c *Client, req *http.Request) (*http.Response, error) { - return func(c *Client, req *http.Request) (*http.Response, error) { +func mockResp(resp string) func(_ *Client, req *http.Request) (*http.Response, error) { + return func(_ *Client, _ *http.Request) (*http.Response, error) { return &http.Response{ Body: io.NopCloser(strings.NewReader(resp)), }, nil } } -func mockErrResp() func(c *Client, req *http.Request) (*http.Response, error) { - return func(c *Client, req *http.Request) (*http.Response, error) { - return &http.Response{}, fmt.Errorf("fail") +func mockErrResp() func(_ *Client, _ *http.Request) (*http.Response, error) { + return func(_ *Client, _ *http.Request) (*http.Response, error) { + return &http.Response{}, errors.New("fail") } } @@ -127,7 +125,7 @@ func TestGetTrades_Gzip(t *testing.T) { f, err := os.Open("testdata/trades.json.gz") require.NoError(t, err) - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "gzip", req.Header.Get("Accept-Encoding")) assert.Equal(t, "sip", req.URL.Query().Get("feed")) return &http.Response{ @@ -153,7 +151,7 @@ func TestGetTrades_Gzip(t *testing.T) { func TestGetTrades(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "gzip", req.Header.Get("Accept-Encoding")) resp := `{"trades":{"AAPL":[{"t":"2021-10-13T08:00:00.08960768Z","x":"P","p":140.2,"s":595,"c":["@","T"],"i":1,"z":"C"}]},"next_page_token":"QUFQTHwyMDIxLTEwLTEzVDA4OjAwOjAwLjA4OTYwNzY4MFp8UHwwOTIyMzM3MjAzNjg1NDc3NTgwOQ=="}` // Even though we request gzip encoding, the server may decide to not use it @@ -177,7 +175,7 @@ func TestGetTrades_Currency(t *testing.T) { Feed: "sip", Currency: "JPY", }) - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "sip", req.URL.Query().Get("feed")) assert.Equal(t, "JPY", req.URL.Query().Get("currency")) resp := `{"trades":{"AAPL":[{"t":"2021-10-13T08:00:00.08960768Z","x":"P","p":15922.93,"s":595,"c":["@","T"],"i":1,"z":"C"}]},"currency":"JPY","next_page_token":"QUFQTHwyMDIxLTEwLTEzVDA4OjAwOjAwLjA4OTYwNzY4MFp8UHwwOTIyMzM3MjAzNjg1NDc3NTgwOQ=="}` @@ -195,7 +193,7 @@ func TestGetTrades_Currency(t *testing.T) { trade := got[0] assert.Equal(t, 15922.93, trade.Price) - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { resp := `{"trades":{},"currency":"MXN","next_page_token":null}` assert.Equal(t, "sip", req.URL.Query().Get("feed")) assert.Equal(t, "MXN", req.URL.Query().Get("currency")) @@ -211,14 +209,14 @@ func TestGetTrades_Currency(t *testing.T) { Currency: "MXN", }) require.NoError(t, err) - assert.Len(t, got, 0) + assert.Empty(t, got, 0) } func TestGetTrades_InvalidURL(t *testing.T) { c := NewClient(ClientOpts{ BaseURL: string([]byte{0, 1, 2, 3}), }) - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { require.Fail(t, "the server should not have been called") return nil, nil } @@ -256,7 +254,7 @@ func TestGetTrades_InvalidResponse(t *testing.T) { func TestGetMultiTrades(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/stocks/trades", req.URL.Path) assert.Equal(t, "2018-06-04T19:18:17Z", req.URL.Query().Get("start")) assert.Equal(t, "2018-06-04T19:18:19Z", req.URL.Query().Get("end")) @@ -289,7 +287,7 @@ func TestGetMultiTrades(t *testing.T) { func TestGetQuotes(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/stocks/quotes", req.URL.Path) assert.Equal(t, "IBM", req.URL.Query().Get("symbols")) assert.Equal(t, "2021-10-04T18:00:14Z", req.URL.Query().Get("start")) @@ -328,7 +326,7 @@ func TestGetQuotes(t *testing.T) { func TestGetQuotes_SortDesc(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "desc", req.URL.Query().Get("sort")) resp := `{"next_page_token":"VFNMQXw3NTMxMTU5NjM2OTY0OTE0ODc5fFV8MjI4LjMzfDF8UHwyMjguMzV8NXxS","quotes":{"TSLA":[{"ap":228.35,"as":5,"ax":"P","bp":228.34,"bs":2,"bx":"Q","c":["R"],"t":"2023-08-16T18:59:59.185551091Z","z":"C"},{"ap":228.35,"as":5,"ax":"P","bp":228.33,"bs":1,"bx":"U","c":["R"],"t":"2023-08-16T18:59:59.035085121Z","z":"C"}]}}` return &http.Response{ @@ -350,7 +348,7 @@ func TestGetQuotes_SortDesc(t *testing.T) { func TestGetMultiQuotes(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/stocks/quotes", req.URL.Path) assert.Equal(t, "6", req.URL.Query().Get("limit")) assert.Equal(t, "BA,DIS", req.URL.Query().Get("symbols")) @@ -379,7 +377,7 @@ func TestGetAuctions(t *testing.T) { firstResp := `{"auctions":{"AAPL":[{"d":"2022-10-17","o":[{"t":"2022-10-17T13:30:00.189598208Z","x":"P","p":141.13,"s":10,"c":"Q"},{"t":"2022-10-17T13:30:01.329947459Z","x":"Q","p":141.07,"s":1103165,"c":"O"},{"t":"2022-10-17T13:30:01.334218355Z","x":"Q","p":141.07,"s":1103165,"c":"Q"}],"c":[{"t":"2022-10-17T20:00:00.155310848Z","x":"P","p":142.4,"s":100,"c":"M"},{"t":"2022-10-17T20:00:01.135646791Z","x":"Q","p":142.41,"s":7927137,"c":"6"},{"t":"2022-10-17T20:00:01.742162179Z","x":"Q","p":142.41,"s":7927137,"c":"M"}]},{"d":"2022-10-18","o":[{"t":"2022-10-18T13:30:00.193677568Z","x":"P","p":145.42,"s":1,"c":"Q"},{"t":"2022-10-18T13:30:01.662931714Z","x":"Q","p":145.49,"s":793345,"c":"O"},{"t":"2022-10-18T13:30:01.67388499Z","x":"Q","p":145.49,"s":793345,"c":"Q"}],"c":[{"t":"2022-10-18T20:00:00.15542272Z","x":"P","p":143.79,"s":100,"c":"M"},{"t":"2022-10-18T20:00:00.63129591Z","x":"Q","p":143.75,"s":3979281,"c":"6"},{"t":"2022-10-18T20:00:00.631313365Z","x":"Q","p":143.75,"s":3979281,"c":"M"}]}]},"next_page_token":"QUFQTHwyMDIyLTEwLTE4VDIwOjAwOjAwLjYzMTMxMzM2NVp8UXxATXwxNDMuNzU="}` secondResp := `{"auctions":{"AAPL":[{"d":"2022-10-19","o":[{"t":"2022-10-19T13:30:00.206482688Z","x":"P","p":141.69,"s":4,"c":"Q"},{"t":"2022-10-19T13:30:01.350685708Z","x":"Q","p":141.5,"s":517006,"c":"O"},{"t":"2022-10-19T13:30:01.351159286Z","x":"Q","p":141.5,"s":517006,"c":"Q"}],"c":[{"t":"2022-10-19T20:00:00.143265536Z","x":"P","p":143.9,"s":400,"c":"M"},{"t":"2022-10-19T20:00:01.384247418Z","x":"Q","p":143.86,"s":4006543,"c":"6"},{"t":"2022-10-19T20:00:01.384266818Z","x":"Q","p":143.86,"s":4006543,"c":"M"}]},{"d":"2022-10-20","o":[{"t":"2022-10-20T13:30:00.172134656Z","x":"P","p":143.03,"s":6,"c":"Q"},{"t":"2022-10-20T13:30:01.664127742Z","x":"Q","p":142.98,"s":663728,"c":"O"},{"t":"2022-10-20T13:30:01.664575417Z","x":"Q","p":142.98,"s":663728,"c":"Q"}],"c":[{"t":"2022-10-20T20:00:00.137319424Z","x":"P","p":143.33,"s":362,"c":"M"},{"t":"2022-10-20T20:00:00.212258037Z","x":"Q","p":143.39,"s":5250532,"c":"6"},{"t":"2022-10-20T20:00:00.212282215Z","x":"Q","p":143.39,"s":5250532,"c":"M"}]}]},"next_page_token":"QUFQTHwyMDIyLTEwLTIwVDIwOjAwOjAwLjIxMjI4MjIxNVp8UXxATXwxNDMuMzk="}` thirdResp := `{"auctions":{"AAPL":[{"d":"2022-10-21","o":[{"t":"2022-10-21T13:30:00.18449664Z","x":"P","p":142.96,"s":59,"c":"Q"},{"t":"2022-10-21T13:30:01.013655041Z","x":"Q","p":142.81,"s":4643721,"c":"O"},{"t":"2022-10-21T13:30:01.025412599Z","x":"Q","p":142.81,"s":4643721,"c":"Q"}],"c":[{"t":"2022-10-21T20:00:00.151828992Z","x":"P","p":147.27,"s":8147,"c":"M"},{"t":"2022-10-21T20:00:00.551850227Z","x":"Q","p":147.27,"s":6395818,"c":"6"},{"t":"2022-10-21T20:00:00.551870027Z","x":"Q","p":147.27,"s":6395818,"c":"M"}]}]},"next_page_token":"QUFQTHwyMDIyLTEwLTIxVDIwOjAwOjAwLjU1MTg3MDAyN1p8UXxATXwxNDcuMjc="}` - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/stocks/auctions", req.URL.Path) assert.Equal(t, "AAPL", req.URL.Query().Get("symbols")) assert.Equal(t, "2022-10-17T00:00:00Z", req.URL.Query().Get("start")) @@ -429,7 +427,7 @@ func TestGetAuctions(t *testing.T) { func TestGetMultiAuctions(t *testing.T) { c := DefaultClient resp := `{"auctions":{"AAPL":[{"d":"2022-10-17","o":[{"t":"2022-10-17T13:30:00.189598208Z","x":"P","p":141.13,"s":10,"c":"Q"},{"t":"2022-10-17T13:30:01.329947459Z","x":"Q","p":141.07,"s":1103165,"c":"O"},{"t":"2022-10-17T13:30:01.334218355Z","x":"Q","p":141.07,"s":1103165,"c":"Q"}],"c":[{"t":"2022-10-17T20:00:00.155310848Z","x":"P","p":142.4,"s":100,"c":"M"},{"t":"2022-10-17T20:00:01.135646791Z","x":"Q","p":142.41,"s":7927137,"c":"6"},{"t":"2022-10-17T20:00:01.742162179Z","x":"Q","p":142.41,"s":7927137,"c":"M"}]}],"IBM":[{"d":"2022-10-17","o":[{"t":"2022-10-17T13:30:00.75936768Z","x":"P","p":121.8,"s":100,"c":"Q"},{"t":"2022-10-17T13:30:00.916387328Z","x":"N","p":121.82,"s":62168,"c":"O"},{"t":"2022-10-17T13:30:00.916387328Z","x":"N","p":121.82,"s":62168,"c":"Q"},{"t":"2022-10-17T13:30:01.093145723Z","x":"T","p":121.66,"s":100,"c":"Q"}],"c":[{"t":"2022-10-17T20:00:00.190113536Z","x":"P","p":121.595,"s":100,"c":"M"},{"t":"2022-10-17T20:00:01.746899562Z","x":"T","p":121.57,"s":4,"c":"M"},{"t":"2022-10-17T20:00:02.02300032Z","x":"N","p":121.52,"s":959421,"c":"6"},{"t":"2022-10-17T20:00:02.136344832Z","x":"N","p":121.52,"s":959421,"c":"M"}]}]},"next_page_token":"SUJNfDIwMjItMTAtMTdUMjM6MDA6MDAuMDAyMjYzODA4WnxOfCBNfDEyMS41Mg=="}` - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/stocks/auctions", req.URL.Path) assert.Equal(t, "2", req.URL.Query().Get("limit")) assert.Equal(t, "AAPL,IBM,TSLA", req.URL.Query().Get("symbols")) @@ -477,7 +475,7 @@ func TestGetBars(t *testing.T) { func TestGetBars_Asof(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v2/stocks/bars", req.URL.Path) assert.Equal(t, "2022-06-09", req.URL.Query().Get("asof")) resp := `{"bars":{"META":[{"t":"2022-06-08T04:00:00Z","o":194.67,"h":202.03,"l":194.41,"c":196.64,"v":22211813,"n":246906,"vw":198.364578},{"t":"2022-06-09T04:00:00Z","o":194.28,"h":199.45,"l":183.68,"c":184,"v":23458984,"n":281546,"vw":190.750577}]},"next_page_token":"TUVUQXxEfDIwMjItMDYtMDlUMDQ6MDA6MDAuMDAwMDAwMDAwWg=="}` @@ -547,14 +545,14 @@ func TestLatestBar(t *testing.T) { // api failure c.do = mockErrResp() got, err = c.GetLatestBar("AAPL", GetLatestBarRequest{}) - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, got) } func TestLatestBar_Feed(t *testing.T) { c := NewClient(ClientOpts{Feed: "iex"}) - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "iex", req.URL.Query().Get("feed")) return &http.Response{ Body: io.NopCloser(strings.NewReader( @@ -565,7 +563,7 @@ func TestLatestBar_Feed(t *testing.T) { _, err := c.GetLatestBar("AAPL", GetLatestBarRequest{}) require.NoError(t, err) - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "sip", req.URL.Query().Get("feed")) return &http.Response{ Body: io.NopCloser(strings.NewReader( @@ -582,7 +580,7 @@ func TestLatestBar_Feed(t *testing.T) { func TestLatestBar_Currency(t *testing.T) { c := NewClient(ClientOpts{Currency: "MXN"}) - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "MXN", req.URL.Query().Get("currency")) return &http.Response{ Body: io.NopCloser(strings.NewReader( @@ -596,7 +594,7 @@ func TestLatestBar_Currency(t *testing.T) { assert.Equal(t, 2536.46, bar.High) assert.Equal(t, 2536.19, bar.VWAP) - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "JPY", req.URL.Query().Get("currency")) return &http.Response{ Body: io.NopCloser(strings.NewReader( @@ -637,7 +635,7 @@ func TestLatestBars(t *testing.T) { // api failure c.do = mockErrResp() got, err = c.GetLatestBars([]string{"IBM", "MSFT"}, GetLatestBarRequest{}) - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, got) } @@ -662,7 +660,7 @@ func TestLatestTrade(t *testing.T) { // api failure c.do = mockErrResp() got, err = c.GetLatestTrade("AAPL", GetLatestTradeRequest{}) - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, got) } @@ -689,7 +687,7 @@ func TestLatestTrades(t *testing.T) { // api failure c.do = mockErrResp() got, err = c.GetLatestTrades([]string{"IBM", "MSFT"}, GetLatestTradeRequest{}) - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, got) } @@ -715,7 +713,7 @@ func TestLatestQuote(t *testing.T) { // api failure c.do = mockErrResp() got, err = c.GetLatestQuote("AAPL", GetLatestQuoteRequest{}) - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, got) } @@ -745,7 +743,7 @@ func TestLatestQuotes(t *testing.T) { // api failure c.do = mockErrResp() got, err = c.GetLatestQuotes([]string{"F", "GE", "TSLA"}, GetLatestQuoteRequest{}) - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, got) } @@ -806,7 +804,7 @@ func TestSnapshot(t *testing.T) { // api failure c.do = mockErrResp() got, err = c.GetSnapshot("AAPL", GetSnapshotRequest{}) - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, got) } @@ -831,13 +829,13 @@ func TestSnapshots(t *testing.T) { // api failure c.do = mockErrResp() got, err = c.GetSnapshots([]string{"AAPL", "CLDR"}, GetSnapshotRequest{}) - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, got) } func TestGetCryptoTrades(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v1beta3/crypto/us/trades", req.URL.Path) assert.Equal(t, "BTC/USD", req.URL.Query().Get("symbols")) assert.Equal(t, "2021-09-08T05:04:03Z", req.URL.Query().Get("start")) @@ -863,7 +861,7 @@ func TestGetCryptoTrades(t *testing.T) { func TestGetCryptoMultiTrades(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v1beta3/crypto/us/trades", req.URL.Path) assert.Equal(t, "SUSHI/USD,BAT/USD", req.URL.Query().Get("symbols")) assert.Equal(t, "2023-01-01T20:00:00Z", req.URL.Query().Get("start")) @@ -899,7 +897,7 @@ func TestGetCryptoMultiTrades(t *testing.T) { func TestCryptoQuotes(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v1beta3/crypto/us/quotes", req.URL.Path) assert.Equal(t, "ETH/USD", req.URL.Query().Get("symbols")) assert.Equal(t, "2023-08-16T00:00:00Z", req.URL.Query().Get("start")) @@ -969,7 +967,7 @@ func TestGetCryptoMultiBars(t *testing.T) { func TestLatestCryptoBar(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v1beta3/crypto/us/latest/bars", req.URL.Path) resp := `{"bars":{"BTC/USD":{"t":"2022-02-25T12:50:00Z","o":38899.6,"h":39300,"l":38892.2,"c":39278.88,"v":74.02830613,"n":1086,"vw":39140.0960796263}}}` return &http.Response{ @@ -1130,7 +1128,7 @@ func TestCryptoSnapshot(t *testing.T) { // api failure c.do = mockErrResp() got, err = c.GetCryptoSnapshot("ETH/USD", GetCryptoSnapshotRequest{}) - assert.Error(t, err) + require.Error(t, err) assert.Nil(t, got) } @@ -1148,7 +1146,7 @@ func TestGetNews(t *testing.T) { c := DefaultClient firstResp := `{"news":[{"id":20472678,"headline":"CEO John Krafcik Leaves Waymo","author":"Bibhu Pattnaik","created_at":"2021-04-03T15:35:21Z","updated_at":"2021-04-03T15:35:21Z","summary":"Waymo\u0026#39;s chief technology officer and its chief operating officer will serve as co-CEOs.","url":"https://www.benzinga.com/news/21/04/20472678/ceo-john-krafcik-leaves-waymo","images":[{"size":"large","url":"https://cdn.benzinga.com/files/imagecache/2048x1536xUP/images/story/2012/waymo_2.jpeg"},{"size":"small","url":"https://cdn.benzinga.com/files/imagecache/1024x768xUP/images/story/2012/waymo_2.jpeg"},{"size":"thumb","url":"https://cdn.benzinga.com/files/imagecache/250x187xUP/images/story/2012/waymo_2.jpeg"}],"symbols":["GOOG","GOOGL","TSLA"]},{"id":20472512,"headline":"Benzinga's Bulls And Bears Of The Week: Apple, GM, JetBlue, Lululemon, Tesla And More","author":"Nelson Hem","created_at":"2021-04-03T15:20:12Z","updated_at":"2021-04-03T15:20:12Z","summary":"\n\tBenzinga has examined the prospects for many investor favorite stocks over the past week. \n\tThe past week\u0026#39;s bullish calls included airlines, Chinese EV makers and a consumer electronics giant.\n","url":"https://www.benzinga.com/trading-ideas/long-ideas/21/04/20472512/benzingas-bulls-and-bears-of-the-week-apple-gm-jetblue-lululemon-tesla-and-more","images":[{"size":"large","url":"https://cdn.benzinga.com/files/imagecache/2048x1536xUP/images/story/2012/pexels-burst-373912_0.jpg"},{"size":"small","url":"https://cdn.benzinga.com/files/imagecache/1024x768xUP/images/story/2012/pexels-burst-373912_0.jpg"},{"size":"thumb","url":"https://cdn.benzinga.com/files/imagecache/250x187xUP/images/story/2012/pexels-burst-373912_0.jpg"}],"symbols":["AAPL","ARKX","BMY","CS","GM","JBLU","JCI","LULU","NIO","TSLA","XPEV"]}],"next_page_token":"MTYxNzQ2MzIxMjAwMDAwMDAwMHwyMDQ3MjUxMg=="}` secondResp := `{"news":[{"id":20471562,"headline":"Is Now The Time To Buy Stock In Tesla, Netflix, Alibaba, Ford Or Facebook?","author":"Henry Khederian","created_at":"2021-04-03T12:31:15Z","updated_at":"2021-04-03T12:31:16Z","summary":"One of the most common questions traders have about stocks is “Why Is It Moving?”\n\nThat’s why Benzinga created the Why Is It Moving, or WIIM, feature in Benzinga Pro. WIIMs are a one-sentence description as to why that stock is moving.","url":"https://www.benzinga.com/analyst-ratings/analyst-color/21/04/20471562/is-now-the-time-to-buy-stock-in-tesla-netflix-alibaba-ford-or-facebook","images":[{"size":"large","url":"https://cdn.benzinga.com/files/imagecache/2048x1536xUP/images/story/2012/freestocks-11sgh7u6tmi-unsplash_3_0_0.jpg"},{"size":"small","url":"https://cdn.benzinga.com/files/imagecache/1024x768xUP/images/story/2012/freestocks-11sgh7u6tmi-unsplash_3_0_0.jpg"},{"size":"thumb","url":"https://cdn.benzinga.com/files/imagecache/250x187xUP/images/story/2012/freestocks-11sgh7u6tmi-unsplash_3_0_0.jpg"}],"symbols":["BABA","NFLX","TSLA"]}],"next_page_token":null}` - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v1beta1/news", req.URL.Path) assert.Equal(t, "2021-04-03T00:00:00Z", req.URL.Query().Get("start")) assert.Equal(t, "2021-04-04T05:00:00Z", req.URL.Query().Get("end")) @@ -1199,7 +1197,7 @@ func TestGetNews(t *testing.T) { func TestGetNews_ClientSideValidationErrors(t *testing.T) { c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, _ *http.Request) (*http.Response, error) { assert.Fail(t, "the server should not have been called") return nil, nil } @@ -1226,7 +1224,7 @@ func TestGetNews_ClientSideValidationErrors(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { _, err := c.GetNews(tc.params) - assert.Error(t, err) + require.Error(t, err) assert.Contains(t, err.Error(), tc.expectedError) }) } @@ -1338,7 +1336,7 @@ func TestGetCorporateActions(t *testing.T) { "next_page_token": null }` c := DefaultClient - c.do = func(c *Client, req *http.Request) (*http.Response, error) { + c.do = func(_ *Client, req *http.Request) (*http.Response, error) { assert.Equal(t, "/v1beta1/corporate-actions", req.URL.Path) assert.Equal(t, "forward_split,name_change,worthless_removal,stock_merger", req.URL.Query().Get("types")) diff --git a/marketdata/stream/client.go b/marketdata/stream/client.go index 5305618..8172f29 100644 --- a/marketdata/stream/client.go +++ b/marketdata/stream/client.go @@ -358,7 +358,7 @@ func (c *client) Terminated() <-chan error { // and recreates them if there was an error as long as reconnectLimit consecutive // connection initialization errors don't occur. It sends the first connection // initialization's result to initialResultCh. -func (c *client) maintainConnection(ctx context.Context, u url.URL, initialResultCh chan<- error) { +func (c *client) maintainConnection(ctx context.Context, u url.URL, initialResultCh chan<- error) { //nolint:funlen,gocognit,lll // TODO: Refactor this. var connError error failedAttemptsInARow := 0 connectedAtLeastOnce := false @@ -423,7 +423,8 @@ func (c *client) maintainConnection(ctx context.Context, u url.URL, initialResul } time.Sleep(time.Duration(failedAttemptsInARow) * c.reconnectDelay) failedAttemptsInARow++ - c.logger.Infof("datav2stream: connecting to %s, attempt %d/%d ...", u.String(), failedAttemptsInARow, c.reconnectLimit) + c.logger.Infof("datav2stream: connecting to %s, attempt %d/%d ...", + u.String(), failedAttemptsInARow, c.reconnectLimit) conn, err := c.connCreator(ctx, u) if err != nil { connError = err diff --git a/marketdata/stream/client_test.go b/marketdata/stream/client_test.go index 2b88476..dca4332 100644 --- a/marketdata/stream/client_test.go +++ b/marketdata/stream/client_test.go @@ -3,7 +3,6 @@ package stream import ( "context" "errors" - "fmt" "net/url" "testing" "time" @@ -37,7 +36,7 @@ func TestConnectFails(t *testing.T) { t.Run(tt.name, func(t *testing.T) { connection := newMockConn() defer connection.close() - connCreator := func(ctx context.Context, u url.URL) (conn, error) { + connCreator := func(_ context.Context, _ url.URL) (conn, error) { return connection, nil } @@ -65,8 +64,8 @@ func TestConnectFails(t *testing.T) { err := c.Connect(ctx) - assert.Error(t, err) - assert.True(t, errors.Is(err, ErrNoConnected)) + require.Error(t, err) + require.ErrorIs(t, err, ErrNoConnected) }) } } @@ -93,7 +92,7 @@ func TestConnectWithInvalidURL(t *testing.T) { err := c.Connect(ctx) - assert.Error(t, err) + require.Error(t, err) }) } } @@ -114,7 +113,7 @@ func TestConnectImmediatelyFailsAfterIrrecoverableErrors(t *testing.T) { t.Run(tt.name+"/"+ie.msg, func(t *testing.T) { connection := newMockConn() defer connection.close() - connCreator := func(ctx context.Context, u url.URL) (conn, error) { + connCreator := func(_ context.Context, _ url.URL) (conn, error) { return connection, nil } @@ -140,7 +139,7 @@ func TestConnectImmediatelyFailsAfterIrrecoverableErrors(t *testing.T) { // server rejects the credentials connection.readCh <- serializeToMsgpack(t, []errorWithT{ { - Type: "error", + Type: msgTypeError, Code: ie.code, Msg: ie.msg, }, @@ -150,8 +149,8 @@ func TestConnectImmediatelyFailsAfterIrrecoverableErrors(t *testing.T) { err := c.Connect(ctx) - assert.Error(t, err) - assert.True(t, errors.Is(err, ie.err)) + require.Error(t, err) + require.ErrorIs(t, err, ie.err) }) } } @@ -162,7 +161,7 @@ func TestContextCancelledBeforeConnect(t *testing.T) { t.Run(tt.name, func(t *testing.T) { connection := newMockConn() defer connection.close() - connCreator := func(ctx context.Context, u url.URL) (conn, error) { + connCreator := func(_ context.Context, _ url.URL) (conn, error) { return connection, nil } @@ -182,7 +181,7 @@ func TestContextCancelledBeforeConnect(t *testing.T) { cancel() err := c.Connect(ctx) - assert.Error(t, err) + require.Error(t, err) assert.Error(t, <-c.Terminated()) }) } @@ -193,7 +192,7 @@ func TestConnectSucceeds(t *testing.T) { t.Run(tt.name, func(t *testing.T) { connection := newMockConn() defer connection.close() - connCreator := func(ctx context.Context, u url.URL) (conn, error) { + connCreator := func(_ context.Context, _ url.URL) (conn, error) { return connection, nil } @@ -239,7 +238,7 @@ func TestCallbacksCalledOnConnectAndDisconnect(t *testing.T) { t.Run(tt.name, func(t *testing.T) { connection := newMockConn() defer connection.close() - connCreator := func(ctx context.Context, u url.URL) (conn, error) { + connCreator := func(_ context.Context, _ url.URL) (conn, error) { return connection, nil } @@ -290,19 +289,19 @@ func TestCallbacksCalledOnConnectAndDisconnect(t *testing.T) { func TestSubscribeBeforeConnectStocks(t *testing.T) { c := NewStocksClient(marketdata.IEX) - err := c.SubscribeToTrades(func(trade Trade) {}) + err := c.SubscribeToTrades(func(_ Trade) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToQuotes(func(quote Quote) {}) + err = c.SubscribeToQuotes(func(_ Quote) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToBars(func(bar Bar) {}) + err = c.SubscribeToBars(func(_ Bar) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToUpdatedBars(func(bar Bar) {}) + err = c.SubscribeToUpdatedBars(func(_ Bar) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToDailyBars(func(bar Bar) {}) + err = c.SubscribeToDailyBars(func(_ Bar) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToStatuses(func(ts TradingStatus) {}) + err = c.SubscribeToStatuses(func(_ TradingStatus) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToLULDs(func(luld LULD) {}) + err = c.SubscribeToLULDs(func(_ LULD) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) err = c.UnsubscribeFromTrades() assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) @@ -321,17 +320,17 @@ func TestSubscribeBeforeConnectStocks(t *testing.T) { func TestSubscribeBeforeConnectCrypto(t *testing.T) { c := NewCryptoClient(marketdata.US) - err := c.SubscribeToTrades(func(trade CryptoTrade) {}) + err := c.SubscribeToTrades(func(_ CryptoTrade) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToQuotes(func(quote CryptoQuote) {}) + err = c.SubscribeToQuotes(func(_ CryptoQuote) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToBars(func(bar CryptoBar) {}) + err = c.SubscribeToBars(func(_ CryptoBar) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToUpdatedBars(func(bar CryptoBar) {}) + err = c.SubscribeToUpdatedBars(func(_ CryptoBar) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToDailyBars(func(bar CryptoBar) {}) + err = c.SubscribeToDailyBars(func(_ CryptoBar) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) - err = c.SubscribeToOrderbooks(func(ob CryptoOrderbook) {}) + err = c.SubscribeToOrderbooks(func(_ CryptoOrderbook) {}) assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) err = c.UnsubscribeFromTrades() assert.Equal(t, ErrSubscriptionChangeBeforeConnect, err) @@ -352,7 +351,7 @@ func TestSubscribeMultipleCallsStocks(t *testing.T) { defer connection.close() writeInitialFlowMessagesToConn(t, connection, subscriptions{}) - c := NewStocksClient(marketdata.IEX, withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + c := NewStocksClient(marketdata.IEX, withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return connection, nil })) ctx, cancel := context.WithCancel(context.Background()) @@ -363,19 +362,19 @@ func TestSubscribeMultipleCallsStocks(t *testing.T) { subErrCh := make(chan error, 2) subFunc := func() { - subErrCh <- c.SubscribeToTrades(func(trade Trade) {}, "ALPACA") + subErrCh <- c.SubscribeToTrades(func(_ Trade) {}, "ALPACA") } // calling two Subscribes at the same time and also calling a sub change // without modifying symbols (should succeed immediately) go subFunc() - err = c.SubscribeToTrades(func(trade Trade) {}) - assert.NoError(t, err) + err = c.SubscribeToTrades(func(_ Trade) {}) + require.NoError(t, err) go subFunc() err = <-subErrCh - assert.Error(t, err) - assert.True(t, errors.Is(err, ErrSubscriptionChangeAlreadyInProgress)) + require.Error(t, err) + require.ErrorIs(t, err, ErrSubscriptionChangeAlreadyInProgress) } func TestSubscribeCalledButClientTerminatesCrypto(t *testing.T) { @@ -385,7 +384,7 @@ func TestSubscribeCalledButClientTerminatesCrypto(t *testing.T) { c := NewCryptoClient(marketdata.US, WithCredentials("my_key", "my_secret"), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return connection, nil })) @@ -397,7 +396,7 @@ func TestSubscribeCalledButClientTerminatesCrypto(t *testing.T) { checkInitialMessagesSentByClient(t, connection, "my_key", "my_secret", c.sub) subErrCh := make(chan error, 1) subFunc := func() { - subErrCh <- c.SubscribeToTrades(func(trade CryptoTrade) {}, "PACOIN") + subErrCh <- c.SubscribeToTrades(func(_ CryptoTrade) {}, "PACOIN") } // calling Subscribe @@ -410,13 +409,13 @@ func TestSubscribeCalledButClientTerminatesCrypto(t *testing.T) { cancel() err = <-subErrCh - assert.Error(t, err) - assert.True(t, errors.Is(err, ErrSubscriptionChangeInterrupted)) + require.Error(t, err) + require.ErrorIs(t, err, ErrSubscriptionChangeInterrupted) // Subscribing after the client has terminated results in an error - err = c.SubscribeToQuotes(func(quote CryptoQuote) {}, "BTC/USD", "ETC/USD") - assert.Error(t, err) - assert.True(t, errors.Is(err, ErrSubscriptionChangeAfterTerminated)) + err = c.SubscribeToQuotes(func(_ CryptoQuote) {}, "BTC/USD", "ETC/USD") + require.Error(t, err) + require.ErrorIs(t, err, ErrSubscriptionChangeAfterTerminated) } func TestSubscriptionTimeout(t *testing.T) { @@ -425,7 +424,7 @@ func TestSubscriptionTimeout(t *testing.T) { writeInitialFlowMessagesToConn(t, connection, subscriptions{}) mockTimeAfterCh := make(chan time.Time) - timeAfter = func(d time.Duration) <-chan time.Time { + timeAfter = func(_ time.Duration) <-chan time.Time { return mockTimeAfterCh } defer func() { @@ -434,7 +433,7 @@ func TestSubscriptionTimeout(t *testing.T) { c := NewStocksClient(marketdata.IEX, WithCredentials("a", "b"), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return connection, nil })) ctx, cancel := context.WithCancel(context.Background()) @@ -446,7 +445,7 @@ func TestSubscriptionTimeout(t *testing.T) { subErrCh := make(chan error, 2) subFunc := func() { - subErrCh <- c.SubscribeToTrades(func(trade Trade) {}, "ALPACA") + subErrCh <- c.SubscribeToTrades(func(_ Trade) {}, "ALPACA") } go subFunc() @@ -456,8 +455,8 @@ func TestSubscriptionTimeout(t *testing.T) { mockTimeAfterCh <- time.Now() err = <-subErrCh - assert.Error(t, err) - assert.True(t, errors.Is(err, ErrSubscriptionChangeTimeout), "actual: %s", err) + require.Error(t, err) + require.ErrorIs(t, err, ErrSubscriptionChangeTimeout, "actual: %s", err) // after a timeout we should be able to send a new request go subFunc() @@ -481,7 +480,7 @@ func TestSubscriptionChangeInvalid(t *testing.T) { c := NewStocksClient(marketdata.IEX, WithCredentials("a", "b"), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return connection, nil })) ctx, cancel := context.WithCancel(context.Background()) @@ -493,7 +492,7 @@ func TestSubscriptionChangeInvalid(t *testing.T) { subErrCh := make(chan error, 2) subFunc := func() { - subErrCh <- c.SubscribeToTrades(func(trade Trade) {}, "ALPACA") + subErrCh <- c.SubscribeToTrades(func(_ Trade) {}, "ALPACA") } go subFunc() @@ -502,14 +501,14 @@ func TestSubscriptionChangeInvalid(t *testing.T) { require.ElementsMatch(t, []string{"ALPACA"}, subMsg["trades"]) connection.readCh <- serializeToMsgpack(t, []errorWithT{ { - Type: "error", + Type: msgTypeError, Code: 410, Msg: "invalid subscribe action for this feed", }, }) err = <-subErrCh - assert.Error(t, err) - assert.True(t, errors.Is(err, ErrSubscriptionChangeInvalidForFeed), "actual: %s", err) + require.Error(t, err) + require.ErrorIs(t, err, ErrSubscriptionChangeInvalidForFeed, "actual: %s", err) } func TestSubscriptionAcrossConnectionIssues(t *testing.T) { @@ -520,7 +519,7 @@ func TestSubscriptionAcrossConnectionIssues(t *testing.T) { secret := "testsecret" c := NewStocksClient(marketdata.IEX, WithCredentials(key, secret), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return conn1, nil })) ctx, cancel := context.WithCancel(context.Background()) @@ -535,7 +534,7 @@ func TestSubscriptionAcrossConnectionIssues(t *testing.T) { trades1 := []string{"AL", "PACA"} subRes := make(chan error) go func() { - subRes <- c.SubscribeToTrades(func(trade Trade) {}, "AL", "PACA") + subRes <- c.SubscribeToTrades(func(_ Trade) {}, "AL", "PACA") }() sub := expectWrite(t, conn1) require.Equal(t, "subscribe", sub["action"]) @@ -544,7 +543,7 @@ func TestSubscriptionAcrossConnectionIssues(t *testing.T) { // shutting down the first connection conn2 := newMockConn() writeInitialFlowMessagesToConn(t, conn2, subscriptions{}) - c.connCreator = func(ctx context.Context, u url.URL) (conn, error) { + c.connCreator = func(_ context.Context, _ url.URL) (conn, error) { return conn2, nil } conn1.close() @@ -570,7 +569,7 @@ func TestSubscriptionAcrossConnectionIssues(t *testing.T) { // the connection is shut down and the new one isn't established for a while conn3 := newMockConn() defer conn3.close() - c.connCreator = func(ctx context.Context, u url.URL) (conn, error) { + c.connCreator = func(_ context.Context, _ url.URL) (conn, error) { time.Sleep(100 * time.Millisecond) writeInitialFlowMessagesToConn(t, conn3, subscriptions{trades: trades1}) return conn3, nil @@ -604,7 +603,7 @@ func TestSubscriptionAcrossConnectionIssues(t *testing.T) { func TestSubscriptionTwiceAcrossConnectionIssues(t *testing.T) { mockTimeAfterCh := make(chan time.Time) - timeAfter = func(d time.Duration) <-chan time.Time { + timeAfter = func(_ time.Duration) <-chan time.Time { return mockTimeAfterCh } defer func() { @@ -630,7 +629,7 @@ func TestSubscriptionTwiceAcrossConnectionIssues(t *testing.T) { secret := "testsecret" c := NewStocksClient(marketdata.IEX, WithCredentials(key, secret), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return conn1, nil }), WithReconnectSettings(0, 150*time.Millisecond), @@ -651,7 +650,7 @@ func TestSubscriptionTwiceAcrossConnectionIssues(t *testing.T) { trades1 := []string{"AL", "PACA"} subRes := make(chan error) subFunc := func() { - subRes <- c.SubscribeToTrades(func(trade Trade) {}, "AL", "PACA") + subRes <- c.SubscribeToTrades(func(_ Trade) {}, "AL", "PACA") } go subFunc() sub := expectWrite(t, conn1) @@ -668,8 +667,8 @@ func TestSubscriptionTwiceAcrossConnectionIssues(t *testing.T) { require.NoError(t, err) // shutting down the first connection - c.connCreator = func(ctx context.Context, u url.URL) (conn, error) { - return nil, fmt.Errorf("connection failed") + c.connCreator = func(_ context.Context, _ url.URL) (conn, error) { + return nil, errors.New("connection failed") } conn1.close() // wait disconnect callback @@ -680,21 +679,21 @@ func TestSubscriptionTwiceAcrossConnectionIssues(t *testing.T) { mockTimeAfterCh <- time.Now() err = <-subRes - assert.Error(t, err) - assert.True(t, errors.Is(err, ErrSubscriptionChangeTimeout), "actual: %s", err) + require.Error(t, err) + require.ErrorIs(t, err, ErrSubscriptionChangeTimeout, "actual: %s", err) // after a timeout we should be able to get timed out again go subFunc() mockTimeAfterCh <- time.Now() err = <-subRes - assert.Error(t, err) - assert.True(t, errors.Is(err, ErrSubscriptionChangeTimeout), "actual: %s", err) + require.Error(t, err) + require.ErrorIs(t, err, ErrSubscriptionChangeTimeout, "actual: %s", err) // establish 2nd connection conn2 := newMockConn() writeInitialFlowMessagesToConn(t, conn2, subscriptions{trades: trades1}) - c.connCreator = func(ctx context.Context, u url.URL) (conn, error) { + c.connCreator = func(_ context.Context, _ url.URL) (conn, error) { return conn2, nil } // wait connect callback @@ -723,7 +722,7 @@ func TestSubscriptionTwiceAcrossConnectionIssues(t *testing.T) { // the connection is shut down and the new one isn't established for a while conn3 := newMockConn() defer conn3.close() - c.connCreator = func(ctx context.Context, u url.URL) (conn, error) { + c.connCreator = func(_ context.Context, _ url.URL) (conn, error) { time.Sleep(100 * time.Millisecond) writeInitialFlowMessagesToConn(t, conn3, subscriptions{trades: trades1}) return conn3, nil @@ -762,7 +761,7 @@ func TestSubscribeFailsDueToError(t *testing.T) { c := NewCryptoClient(marketdata.US, WithCredentials("my_key", "my_secret"), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return connection, nil })) @@ -777,7 +776,7 @@ func TestSubscribeFailsDueToError(t *testing.T) { // attempting sub change subRes := make(chan error) subFunc := func() { - subRes <- c.SubscribeToTrades(func(trade CryptoTrade) {}, "PACOIN") + subRes <- c.SubscribeToTrades(func(_ CryptoTrade) {}, "PACOIN") } go subFunc() // wait for message to be written @@ -788,7 +787,7 @@ func TestSubscribeFailsDueToError(t *testing.T) { // sub change request fails connection.readCh <- serializeToMsgpack(t, []errorWithT{ { - Type: "error", + Type: msgTypeError, Code: 405, Msg: "symbol limit exceeded", }, @@ -797,7 +796,7 @@ func TestSubscribeFailsDueToError(t *testing.T) { // making sure the subscription request has failed err = <-subRes require.Error(t, err) - require.True(t, errors.Is(err, ErrSymbolLimitExceeded)) + require.ErrorIs(t, err, ErrSymbolLimitExceeded) // attempting another sub change go subFunc() @@ -809,7 +808,7 @@ func TestSubscribeFailsDueToError(t *testing.T) { // sub change request interrupted by slow client connection.readCh <- serializeToMsgpack(t, []errorWithT{ { - Type: "error", + Type: msgTypeError, Code: 407, Msg: "slow client", }, @@ -818,7 +817,7 @@ func TestSubscribeFailsDueToError(t *testing.T) { // making sure the subscription request has failed err = <-subRes require.Error(t, err) - require.True(t, errors.Is(err, ErrSlowClient)) + require.ErrorIs(t, err, ErrSlowClient) // attempting another sub change go subFunc() @@ -830,7 +829,7 @@ func TestSubscribeFailsDueToError(t *testing.T) { // sub change request fails due to incorrect due to incorrect subscription for feed connection.readCh <- serializeToMsgpack(t, []errorWithT{ { - Type: "error", + Type: msgTypeError, Code: 410, Msg: "invalid subscribe action for this feed", }, @@ -839,7 +838,7 @@ func TestSubscribeFailsDueToError(t *testing.T) { // making sure the subscription request has failed err = <-subRes require.Error(t, err) - require.True(t, errors.Is(err, ErrSubscriptionChangeInvalidForFeed)) + require.ErrorIs(t, err, ErrSubscriptionChangeInvalidForFeed) } func assertBufferFills(t *testing.T, bufferFills, trades chan Trade, minID, maxID, minTrades int) { @@ -900,7 +899,7 @@ func TestCallbacksCalledOnBufferFill(t *testing.T) { Symbol: trades[0].Symbol, } }), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { return connection, nil }), + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return connection, nil }), WithTrades(func(t Trade) { trades <- t }, "ALPACA"), ) require.NoError(t, c.Connect(ctx)) @@ -925,7 +924,7 @@ func TestPingFails(t *testing.T) { t.Run(tt.name, func(t *testing.T) { connection := newMockConn() defer connection.close() - connCreator := func(ctx context.Context, u url.URL) (conn, error) { + connCreator := func(_ context.Context, _ url.URL) (conn, error) { return connection, nil } @@ -956,11 +955,11 @@ func TestPingFails(t *testing.T) { connErr := errors.New("no connection") switch tt.name { case stocksTests: - c.(*StocksClient).connCreator = func(ctx context.Context, u url.URL) (conn, error) { + c.(*StocksClient).connCreator = func(_ context.Context, _ url.URL) (conn, error) { return nil, connErr } case cryptoTests: - c.(*CryptoClient).connCreator = func(ctx context.Context, u url.URL) (conn, error) { + c.(*CryptoClient).connCreator = func(_ context.Context, _ url.URL) (conn, error) { return nil, connErr } } @@ -970,8 +969,8 @@ func TestPingFails(t *testing.T) { testTicker.Tick() err = <-c.Terminated() - assert.Error(t, err) - assert.True(t, errors.Is(err, connErr)) + require.Error(t, err) + require.ErrorIs(t, err, connErr) }) } } @@ -1008,7 +1007,7 @@ func TestCoreFunctionalityStocks(t *testing.T) { WithLULDs(func(l LULD) { lulds <- l }, "ALPACA"), WithCancelErrors(func(tce TradeCancelError) { cancelErrors <- tce }), WithCorrections(func(tc TradeCorrection) { corrections <- tc }), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return connection, nil })) ctx, cancel := context.WithCancel(context.Background()) @@ -1213,7 +1212,7 @@ func TestCoreFunctionalityCrypto(t *testing.T) { WithCryptoUpdatedBars(func(b CryptoBar) { updatedBars <- b }, "BCH/USD"), WithCryptoDailyBars(func(b CryptoBar) { dailyBars <- b }, "BCH/USD"), WithCryptoOrderbooks(func(ob CryptoOrderbook) { orderbooks <- ob }, "SHIB/USD"), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return connection, nil })) ctx, cancel := context.WithCancel(context.Background()) @@ -1268,7 +1267,7 @@ func TestCoreFunctionalityCrypto(t *testing.T) { Exchange: "TST", Price: 4123.123, Size: 34.876, - Id: 25, + ID: 25, TakerSide: "S", }, }) @@ -1340,8 +1339,8 @@ func TestCoreFunctionalityCrypto(t *testing.T) { select { case ob := <-orderbooks: assert.Equal(t, "SHIB/USD", ob.Symbol) - assert.Equal(t, 2, len(ob.Bids)) - assert.Equal(t, 2, len(ob.Asks)) + assert.Len(t, ob.Bids, 2) + assert.Len(t, ob.Asks, 2) case <-time.After(time.Second): require.Fail(t, "no orderbook received in time") } @@ -1362,7 +1361,7 @@ func TestCoreFunctionalityOption(t *testing.T) { c := NewOptionClient(marketdata.US, WithOptionTrades(func(t OptionTrade) { trades <- t }, spx1), WithOptionQuotes(func(q OptionQuote) { quotes <- q }, spx2), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return connection, nil })) ctx, cancel := context.WithCancel(context.Background()) @@ -1430,7 +1429,7 @@ func TestCoreFunctionalityNews(t *testing.T) { news := make(chan News, 10) c := NewNewsClient( WithNews(func(n News) { news <- n }, "AAPL"), - withConnCreator(func(ctx context.Context, u url.URL) (conn, error) { + withConnCreator(func(_ context.Context, _ url.URL) (conn, error) { return connection, nil })) ctx, cancel := context.WithCancel(context.Background()) diff --git a/marketdata/stream/conn_coder.go b/marketdata/stream/conn_coder.go index 987dbbe..c9e0e5b 100644 --- a/marketdata/stream/conn_coder.go +++ b/marketdata/stream/conn_coder.go @@ -24,6 +24,7 @@ func newCoderWebsocketConn(ctx context.Context, u url.URL) (conn, error) { reqHeader := http.Header{} reqHeader.Set("Content-Type", "application/msgpack") reqHeader.Set("User-Agent", alpaca.Version()) + //nolint:bodyclose // According to its docs: you never need to close resp.Body yourself conn, _, err := websocket.Dial(ctxWithTimeout, u.String(), &websocket.DialOptions{ CompressionMode: websocket.CompressionContextTakeover, HTTPHeader: reqHeader, @@ -55,8 +56,8 @@ func (c *coderWebsocketConn) ping(ctx context.Context) error { } // readMessage blocks until it reads a single message -func (c *coderWebsocketConn) readMessage(ctx context.Context) (data []byte, err error) { - _, data, err = c.conn.Read(ctx) +func (c *coderWebsocketConn) readMessage(ctx context.Context) ([]byte, error) { + _, data, err := c.conn.Read(ctx) return data, err } diff --git a/marketdata/stream/conn_test.go b/marketdata/stream/conn_test.go index c724eb0..75b96a4 100644 --- a/marketdata/stream/conn_test.go +++ b/marketdata/stream/conn_test.go @@ -42,7 +42,7 @@ func (c *mockConn) close() error { return nil } -func (c *mockConn) ping(ctx context.Context) error { +func (c *mockConn) ping(_ context.Context) error { if c.pingDisabled { return errPingDisabled } @@ -55,7 +55,7 @@ func (c *mockConn) ping(ctx context.Context) error { return nil } -func (c *mockConn) readMessage(ctx context.Context) (data []byte, err error) { +func (c *mockConn) readMessage(ctx context.Context) ([]byte, error) { select { case <-ctx.Done(): return nil, ctx.Err() @@ -66,7 +66,7 @@ func (c *mockConn) readMessage(ctx context.Context) (data []byte, err error) { } } -func (c *mockConn) writeMessage(ctx context.Context, data []byte) error { +func (c *mockConn) writeMessage(_ context.Context, data []byte) error { select { case <-c.closeCh: return errClose diff --git a/marketdata/stream/entities.go b/marketdata/stream/entities.go index 5845cf3..6e6d556 100644 --- a/marketdata/stream/entities.go +++ b/marketdata/stream/entities.go @@ -19,18 +19,6 @@ type Trade struct { Timestamp time.Time Conditions []string Tape string - - internal tradeInternal -} - -type tradeInternal struct { - ReceivedAt time.Time -} - -// Internal contains internal fields. There aren't any behavioural or backward compatibility -// promises for them: they can be empty or removed in the future. You should not use them at all. -func (t Trade) Internal() tradeInternal { - return t.internal } // Quote is a stock quote from the market @@ -45,18 +33,6 @@ type Quote struct { Timestamp time.Time Conditions []string Tape string - - internal quoteInternal -} - -type quoteInternal struct { - ReceivedAt time.Time -} - -// Internal contains internal fields. There aren't any behavioural or backward compatibility -// promises for them: they can be empty or removed in the future. You should not use them at all. -func (q Quote) Internal() quoteInternal { - return q.internal } // Bar is an aggregate of trades @@ -202,7 +178,7 @@ type News struct { } // errorMessage is an error received from the server -type errorMessage struct { +type errorMessage struct { //nolint:errname // Not an actual error. msg string code int } diff --git a/marketdata/stream/entities_easyjson.go b/marketdata/stream/entities_easyjson.go index 370198a..f261dde 100644 --- a/marketdata/stream/entities_easyjson.go +++ b/marketdata/stream/entities_easyjson.go @@ -17,7 +17,7 @@ var ( _ easyjson.Marshaler ) -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(in *jlexer.Lexer, out *tradeInternal) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(in *jlexer.Lexer, out *errorMessage) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -36,78 +36,6 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(i continue } switch key { - case "received_at": - if data := in.Raw(); in.Ok() { - in.AddError((out.ReceivedAt).UnmarshalJSON(data)) - } - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(out *jwriter.Writer, in tradeInternal) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"received_at\":" - out.RawString(prefix[1:]) - out.Raw((in.ReceivedAt).MarshalJSON()) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v tradeInternal) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v tradeInternal) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *tradeInternal) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *tradeInternal) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(l, v) -} -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(in *jlexer.Lexer, out *quoteInternal) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - case "received_at": - if data := in.Raw(); in.Ok() { - in.AddError((out.ReceivedAt).UnmarshalJSON(data)) - } default: in.SkipRecursive() } @@ -118,71 +46,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1( in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(out *jwriter.Writer, in quoteInternal) { - out.RawByte('{') - first := true - _ = first - { - const prefix string = ",\"received_at\":" - out.RawString(prefix[1:]) - out.Raw((in.ReceivedAt).MarshalJSON()) - } - out.RawByte('}') -} - -// MarshalJSON supports json.Marshaler interface -func (v quoteInternal) MarshalJSON() ([]byte, error) { - w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(&w, v) - return w.Buffer.BuildBytes(), w.Error -} - -// MarshalEasyJSON supports easyjson.Marshaler interface -func (v quoteInternal) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(w, v) -} - -// UnmarshalJSON supports json.Unmarshaler interface -func (v *quoteInternal) UnmarshalJSON(data []byte) error { - r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(&r, v) - return r.Error() -} - -// UnmarshalEasyJSON supports easyjson.Unmarshaler interface -func (v *quoteInternal) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(l, v) -} -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(in *jlexer.Lexer, out *errorMessage) { - isTopLevel := in.IsStart() - if in.IsNull() { - if isTopLevel { - in.Consumed() - } - in.Skip() - return - } - in.Delim('{') - for !in.IsDelim('}') { - key := in.UnsafeFieldName(false) - in.WantColon() - if in.IsNull() { - in.Skip() - in.WantComma() - continue - } - switch key { - default: - in.SkipRecursive() - } - in.WantComma() - } - in.Delim('}') - if isTopLevel { - in.Consumed() - } -} -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(out *jwriter.Writer, in errorMessage) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(out *jwriter.Writer, in errorMessage) { out.RawByte('{') first := true _ = first @@ -192,27 +56,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2( // MarshalJSON supports json.Marshaler interface func (v errorMessage) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v errorMessage) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *errorMessage) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *errorMessage) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(in *jlexer.Lexer, out *TradingStatus) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(in *jlexer.Lexer, out *TradingStatus) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -257,7 +121,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3( in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(out *jwriter.Writer, in TradingStatus) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(out *jwriter.Writer, in TradingStatus) { out.RawByte('{') first := true _ = first @@ -302,27 +166,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3( // MarshalJSON supports json.Marshaler interface func (v TradingStatus) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v TradingStatus) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *TradingStatus) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *TradingStatus) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream1(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(in *jlexer.Lexer, out *TradeCorrection) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(in *jlexer.Lexer, out *TradeCorrection) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -419,7 +283,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4( in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(out *jwriter.Writer, in TradeCorrection) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(out *jwriter.Writer, in TradeCorrection) { out.RawByte('{') first := true _ = first @@ -511,27 +375,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4( // MarshalJSON supports json.Marshaler interface func (v TradeCorrection) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v TradeCorrection) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *TradeCorrection) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *TradeCorrection) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream2(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(in *jlexer.Lexer, out *TradeCancelError) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(in *jlexer.Lexer, out *TradeCancelError) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -578,7 +442,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5( in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(out *jwriter.Writer, in TradeCancelError) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(out *jwriter.Writer, in TradeCancelError) { out.RawByte('{') first := true _ = first @@ -628,27 +492,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5( // MarshalJSON supports json.Marshaler interface func (v TradeCancelError) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v TradeCancelError) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *TradeCancelError) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *TradeCancelError) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream3(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(in *jlexer.Lexer, out *Trade) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(in *jlexer.Lexer, out *Trade) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -716,7 +580,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6( in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(out *jwriter.Writer, in Trade) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(out *jwriter.Writer, in Trade) { out.RawByte('{') first := true _ = first @@ -777,27 +641,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6( // MarshalJSON supports json.Marshaler interface func (v Trade) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v Trade) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *Trade) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *Trade) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream4(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(in *jlexer.Lexer, out *Quote) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(in *jlexer.Lexer, out *Quote) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -869,7 +733,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7( in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(out *jwriter.Writer, in Quote) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(out *jwriter.Writer, in Quote) { out.RawByte('{') first := true _ = first @@ -940,27 +804,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7( // MarshalJSON supports json.Marshaler interface func (v Quote) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v Quote) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *Quote) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *Quote) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream5(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(in *jlexer.Lexer, out *OptionTrade) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(in *jlexer.Lexer, out *OptionTrade) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -1003,7 +867,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8( in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(out *jwriter.Writer, in OptionTrade) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(out *jwriter.Writer, in OptionTrade) { out.RawByte('{') first := true _ = first @@ -1043,27 +907,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8( // MarshalJSON supports json.Marshaler interface func (v OptionTrade) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v OptionTrade) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *OptionTrade) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *OptionTrade) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream6(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(in *jlexer.Lexer, out *OptionQuote) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(in *jlexer.Lexer, out *OptionQuote) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -1112,7 +976,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9( in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(out *jwriter.Writer, in OptionQuote) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(out *jwriter.Writer, in OptionQuote) { out.RawByte('{') first := true _ = first @@ -1167,27 +1031,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9( // MarshalJSON supports json.Marshaler interface func (v OptionQuote) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v OptionQuote) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *OptionQuote) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *OptionQuote) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream7(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(in *jlexer.Lexer, out *News) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(in *jlexer.Lexer, out *News) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -1259,7 +1123,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10 in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(out *jwriter.Writer, in News) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(out *jwriter.Writer, in News) { out.RawByte('{') first := true _ = first @@ -1325,27 +1189,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10 // MarshalJSON supports json.Marshaler interface func (v News) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v News) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *News) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *News) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream8(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(in *jlexer.Lexer, out *LULD) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(in *jlexer.Lexer, out *LULD) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -1388,7 +1252,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11 in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(out *jwriter.Writer, in LULD) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(out *jwriter.Writer, in LULD) { out.RawByte('{') first := true _ = first @@ -1428,27 +1292,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11 // MarshalJSON supports json.Marshaler interface func (v LULD) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v LULD) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *LULD) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *LULD) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream9(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(in *jlexer.Lexer, out *CryptoTrade) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(in *jlexer.Lexer, out *CryptoTrade) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -1493,7 +1357,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12 in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(out *jwriter.Writer, in CryptoTrade) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(out *jwriter.Writer, in CryptoTrade) { out.RawByte('{') first := true _ = first @@ -1538,27 +1402,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12 // MarshalJSON supports json.Marshaler interface func (v CryptoTrade) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v CryptoTrade) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *CryptoTrade) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *CryptoTrade) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream10(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(in *jlexer.Lexer, out *CryptoQuote) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(in *jlexer.Lexer, out *CryptoQuote) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -1603,7 +1467,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13 in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(out *jwriter.Writer, in CryptoQuote) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(out *jwriter.Writer, in CryptoQuote) { out.RawByte('{') first := true _ = first @@ -1648,27 +1512,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13 // MarshalJSON supports json.Marshaler interface func (v CryptoQuote) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v CryptoQuote) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *CryptoQuote) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *CryptoQuote) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream11(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(in *jlexer.Lexer, out *CryptoOrderbookEntry) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(in *jlexer.Lexer, out *CryptoOrderbookEntry) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -1701,7 +1565,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14 in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(out *jwriter.Writer, in CryptoOrderbookEntry) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(out *jwriter.Writer, in CryptoOrderbookEntry) { out.RawByte('{') first := true _ = first @@ -1721,27 +1585,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14 // MarshalJSON supports json.Marshaler interface func (v CryptoOrderbookEntry) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v CryptoOrderbookEntry) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *CryptoOrderbookEntry) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *CryptoOrderbookEntry) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream12(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(in *jlexer.Lexer, out *CryptoOrderbook) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(in *jlexer.Lexer, out *CryptoOrderbook) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -1826,7 +1690,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15 in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(out *jwriter.Writer, in CryptoOrderbook) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(out *jwriter.Writer, in CryptoOrderbook) { out.RawByte('{') first := true _ = first @@ -1888,27 +1752,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15 // MarshalJSON supports json.Marshaler interface func (v CryptoOrderbook) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v CryptoOrderbook) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *CryptoOrderbook) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *CryptoOrderbook) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream13(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream16(in *jlexer.Lexer, out *CryptoBar) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(in *jlexer.Lexer, out *CryptoBar) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -1959,7 +1823,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream16 in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream16(out *jwriter.Writer, in CryptoBar) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(out *jwriter.Writer, in CryptoBar) { out.RawByte('{') first := true _ = first @@ -2019,27 +1883,27 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream16 // MarshalJSON supports json.Marshaler interface func (v CryptoBar) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream16(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v CryptoBar) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream16(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *CryptoBar) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream16(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *CryptoBar) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream16(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream14(l, v) } -func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream17(in *jlexer.Lexer, out *Bar) { +func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(in *jlexer.Lexer, out *Bar) { isTopLevel := in.IsStart() if in.IsNull() { if isTopLevel { @@ -2088,7 +1952,7 @@ func easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream17 in.Consumed() } } -func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream17(out *jwriter.Writer, in Bar) { +func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(out *jwriter.Writer, in Bar) { out.RawByte('{') first := true _ = first @@ -2143,23 +2007,23 @@ func easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream17 // MarshalJSON supports json.Marshaler interface func (v Bar) MarshalJSON() ([]byte, error) { w := jwriter.Writer{} - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream17(&w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(&w, v) return w.Buffer.BuildBytes(), w.Error } // MarshalEasyJSON supports easyjson.Marshaler interface func (v Bar) MarshalEasyJSON(w *jwriter.Writer) { - easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream17(w, v) + easyjson3e8ab7adEncodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(w, v) } // UnmarshalJSON supports json.Unmarshaler interface func (v *Bar) UnmarshalJSON(data []byte) error { r := jlexer.Lexer{Data: data} - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream17(&r, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(&r, v) return r.Error() } // UnmarshalEasyJSON supports easyjson.Unmarshaler interface func (v *Bar) UnmarshalEasyJSON(l *jlexer.Lexer) { - easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream17(l, v) + easyjson3e8ab7adDecodeGithubComAlpacahqAlpacaTradeApiGoV3MarketdataStream15(l, v) } diff --git a/marketdata/stream/flow.go b/marketdata/stream/flow.go index 7acb076..a9eeff1 100644 --- a/marketdata/stream/flow.go +++ b/marketdata/stream/flow.go @@ -9,9 +9,11 @@ import ( "github.com/vmihailenco/msgpack/v5" ) -var initializeTimeout = 3 * time.Second -var authRetryDelayMultiplier = 1 -var authRetryCount = 15 +var ( + initializeTimeout = 3 * time.Second + authRetryDelayMultiplier = 1 + authRetryCount = 15 +) // initialize performs the initial flow: // 1. wait to be welcomed @@ -20,10 +22,10 @@ var authRetryCount = 15 // // If it runs into retriable issues during the flow it retries for a while func (c *client) initialize(ctx context.Context) error { - ctxWithTimeout, cancel := context.WithTimeout(ctx, initializeTimeout) - defer cancel() + readConnectedCtx, cancelReadConnected := context.WithTimeout(ctx, initializeTimeout) + defer cancelReadConnected() - if err := c.readConnected(ctxWithTimeout); err != nil { + if err := c.readConnected(readConnectedCtx); err != nil { return fmt.Errorf("failed to read connected: %w", err) } @@ -40,15 +42,15 @@ func (c *client) initialize(ctx context.Context) error { c.logger.Infof("datav2stream: retrying auth in %s, attempt %d/%d", sleepDuration, i+1, authRetryCount+1) time.Sleep(sleepDuration) } - ctxWithTimeout, cancel := context.WithTimeout(ctx, initializeTimeout) - defer cancel() - if err := c.writeAuth(ctxWithTimeout); err != nil { + writeAuthCtx, cancelWriteAuth := context.WithTimeout(ctx, initializeTimeout) + defer cancelWriteAuth() + if err := c.writeAuth(writeAuthCtx); err != nil { return fmt.Errorf("failed to write auth: %w", err) } - ctxWithTimeoutResp, cancelResp := context.WithTimeout(ctx, initializeTimeout) - defer cancelResp() - retryErr = c.readAuthResponse(ctxWithTimeoutResp) + readAuthRespCtx, cancelReadAuthResp := context.WithTimeout(ctx, initializeTimeout) + defer cancelReadAuthResp() + retryErr = c.readAuthResponse(readAuthRespCtx) if retryErr == nil { break } @@ -66,15 +68,15 @@ func (c *client) initialize(ctx context.Context) error { return nil } - ctxWithTimeoutWriteSub, cancelWriteSub := context.WithTimeout(ctx, initializeTimeout) + writeSubCtx, cancelWriteSub := context.WithTimeout(ctx, initializeTimeout) defer cancelWriteSub() - if err := c.writeSub(ctxWithTimeoutWriteSub); err != nil { + if err := c.writeSub(writeSubCtx); err != nil { return fmt.Errorf("failed to write subscribe: %w", err) } - ctxWithTimeoutReadSub, cancelReadSub := context.WithTimeout(ctx, initializeTimeout) + readRespCtx, cancelReadSub := context.WithTimeout(ctx, initializeTimeout) defer cancelReadSub() - if err := c.readSubResponse(ctxWithTimeoutReadSub); err != nil { + if err := c.readSubResponse(readRespCtx); err != nil { return fmt.Errorf("failed to read subscribe response: %w", err) } @@ -139,7 +141,7 @@ func (c *client) readAuthResponse(ctx context.Context) error { resp := resps[0] - if resp.T == "error" { + if resp.T == msgTypeError { return errorMessage{ msg: resp.Msg, code: resp.Code, @@ -189,7 +191,7 @@ func (c *client) readSubResponse(ctx context.Context) error { } resp := resps[0] - if resp.T == "error" { + if resp.T == msgTypeError { return errorMessage{ msg: resp.Msg, code: resp.Code, diff --git a/marketdata/stream/flow_test.go b/marketdata/stream/flow_test.go index e2143a1..ed3dc59 100644 --- a/marketdata/stream/flow_test.go +++ b/marketdata/stream/flow_test.go @@ -2,7 +2,6 @@ package stream import ( "context" - "errors" "testing" "github.com/stretchr/testify/assert" @@ -54,15 +53,15 @@ func TestInitializeAuthError(t *testing.T) { // server rejects the authentication attempt - 402 conn.readCh <- serializeToMsgpack(t, []map[string]interface{}{ { - "T": "error", + "T": msgTypeError, "code": 402, "msg": "auth failed", }, }) err := <-res - assert.Error(t, err) - assert.True(t, errors.Is(err, ErrInvalidCredentials)) + require.Error(t, err) + require.ErrorIs(t, err, ErrInvalidCredentials) } func TestInitializeAuthRetryFails(t *testing.T) { @@ -96,7 +95,7 @@ func TestInitializeAuthRetryFails(t *testing.T) { // client attempts to authenticate - 406 conn.readCh <- serializeToMsgpack(t, []map[string]interface{}{ { - "T": "error", + "T": msgTypeError, "code": 406, "msg": "connection limit exceeded", }, @@ -104,15 +103,15 @@ func TestInitializeAuthRetryFails(t *testing.T) { // client attempts to authenticate - 406 again conn.readCh <- serializeToMsgpack(t, []map[string]interface{}{ { - "T": "error", + "T": msgTypeError, "code": 406, "msg": "connection limit exceeded", }, }) err := <-res - assert.Error(t, err) - assert.True(t, errors.Is(err, ErrConnectionLimitExceeded)) + require.Error(t, err) + require.ErrorIs(t, err, ErrConnectionLimitExceeded) } func TestInitializeAuthRetrySucceeds(t *testing.T) { @@ -129,13 +128,13 @@ func TestInitializeAuthRetrySucceeds(t *testing.T) { c := NewStocksClient( marketdata.SIP, WithCredentials("testkey", "testsecret"), - WithTrades(func(t Trade) {}, trades...), - WithQuotes(func(q Quote) {}, quotes...), - WithBars(func(b Bar) {}, bars...), - WithUpdatedBars(func(b Bar) {}, updatedBars...), - WithDailyBars(func(db Bar) {}, dailyBars...), - WithStatuses(func(ts TradingStatus) {}, statuses...), - WithLULDs(func(l LULD) {}, lulds...), + WithTrades(func(_ Trade) {}, trades...), + WithQuotes(func(_ Quote) {}, quotes...), + WithBars(func(_ Bar) {}, bars...), + WithUpdatedBars(func(_ Bar) {}, updatedBars...), + WithDailyBars(func(_ Bar) {}, dailyBars...), + WithStatuses(func(_ TradingStatus) {}, statuses...), + WithLULDs(func(_ LULD) {}, lulds...), ) c.conn = conn ordm := authRetryDelayMultiplier @@ -161,7 +160,7 @@ func TestInitializeAuthRetrySucceeds(t *testing.T) { // client attempts to authenticate - 406 conn.readCh <- serializeToMsgpack(t, []map[string]interface{}{ { - "T": "error", + "T": msgTypeError, "code": 406, "msg": "connection limit exceeded", }, @@ -169,7 +168,7 @@ func TestInitializeAuthRetrySucceeds(t *testing.T) { // client attempts to authenticate - 406 again conn.readCh <- serializeToMsgpack(t, []map[string]interface{}{ { - "T": "error", + "T": msgTypeError, "code": 406, "msg": "connection limit exceeded", }, @@ -198,7 +197,7 @@ func TestInitializeAuthRetrySucceeds(t *testing.T) { }, }) - assert.NoError(t, <-res) + require.NoError(t, <-res) assert.ElementsMatch(t, trades, c.sub.trades) assert.ElementsMatch(t, quotes, c.sub.quotes) assert.ElementsMatch(t, bars, c.sub.bars) @@ -274,7 +273,7 @@ func TestInitializeSubError(t *testing.T) { // client subscription fails conn.readCh <- serializeToMsgpack(t, []map[string]interface{}{ { - "T": "error", + "T": msgTypeError, "code": 405, "msg": "symbol limit exceeded", }, @@ -291,7 +290,7 @@ func TestReadConnectedCancelled(t *testing.T) { cancel() err := c.readConnected(ctx) - assert.Error(t, err) + require.Error(t, err) } func TestReadConnectedContents(t *testing.T) { @@ -374,7 +373,7 @@ func TestReadConnectedContents(t *testing.T) { err := c.readConnected(context.Background()) if test.expectError { - assert.Error(t, err) + require.Error(t, err) } else { assert.NoError(t, err) } @@ -390,7 +389,7 @@ func TestWriteAuthCancelled(t *testing.T) { err := c.writeAuth(context.Background()) - assert.Error(t, err) + require.Error(t, err) } func TestWriteAuthContents(t *testing.T) { @@ -418,7 +417,7 @@ func TestReadAuthResponseCancelled(t *testing.T) { cancel() err := c.readAuthResponse(ctx) - assert.Error(t, err) + require.Error(t, err) } func TestReadAuthResponseContents(t *testing.T) { @@ -474,7 +473,7 @@ func TestReadAuthResponseContents(t *testing.T) { name: "should_retry", message: serializeToMsgpack(t, []map[string]interface{}{ { - "T": "error", + "T": msgTypeError, "msg": "connection limit exceeded", "code": 406, }, @@ -486,7 +485,7 @@ func TestReadAuthResponseContents(t *testing.T) { name: "should_not_retry_1", message: serializeToMsgpack(t, []map[string]interface{}{ { - "T": "error", + "T": msgTypeError, "code": 401, }, }), @@ -497,7 +496,7 @@ func TestReadAuthResponseContents(t *testing.T) { name: "should_not_retry_2", message: serializeToMsgpack(t, []map[string]interface{}{ { - "T": "error", + "T": msgTypeError, }, }), expectError: true, @@ -541,7 +540,7 @@ func TestReadAuthResponseContents(t *testing.T) { err := c.readAuthResponse(context.Background()) if test.expectError { - assert.Error(t, err) + require.Error(t, err) assert.Equal(t, test.shouldRetry, isErrorRetriable(err)) } else { assert.NoError(t, err) @@ -558,7 +557,7 @@ func TestWriteSubCancelled(t *testing.T) { err := c.writeSub(context.Background()) - assert.Error(t, err) + require.Error(t, err) } func TestWriteSubContents(t *testing.T) { @@ -651,7 +650,7 @@ func TestReadSubResponseCancelled(t *testing.T) { cancel() err := c.readSubResponse(ctx) - assert.Error(t, err) + require.Error(t, err) } func TestReadSubResponseContents(t *testing.T) { @@ -682,10 +681,10 @@ func TestReadSubResponseContents(t *testing.T) { expectError: true, }, { - name: "error", + name: msgTypeError, message: serializeToMsgpack(t, []map[string]interface{}{ { - "T": "error", + "T": msgTypeError, "code": 402, "msg": "auth failed", }, @@ -747,9 +746,9 @@ func TestReadSubResponseContents(t *testing.T) { err := c.readSubResponse(context.Background()) if test.expectError { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) assert.ElementsMatch(t, test.trades, c.sub.trades) assert.ElementsMatch(t, test.quotes, c.sub.quotes) assert.ElementsMatch(t, test.bars, c.sub.bars) @@ -770,8 +769,6 @@ func expectWrite(t *testing.T, mockConn *mockConn) map[string]interface{} { func serializeToMsgpack(t *testing.T, v interface{}) []byte { m, err := msgpack.Marshal(v) - if err != nil { - require.Failf(t, "msgpack marshal error", "v", err) - } + require.NoError(t, err) return m } diff --git a/marketdata/stream/log.go b/marketdata/stream/log.go index 41a5668..e08c78c 100644 --- a/marketdata/stream/log.go +++ b/marketdata/stream/log.go @@ -35,8 +35,8 @@ type errorOnlyLogger struct{} var _ Logger = (*errorOnlyLogger)(nil) -func (*errorOnlyLogger) Infof(format string, v ...interface{}) {} -func (*errorOnlyLogger) Warnf(format string, v ...interface{}) {} +func (*errorOnlyLogger) Infof(_ string, _ ...interface{}) {} +func (*errorOnlyLogger) Warnf(_ string, _ ...interface{}) {} func (*errorOnlyLogger) Errorf(format string, v ...interface{}) { log.Printf(format, v...) } diff --git a/marketdata/stream/message.go b/marketdata/stream/message.go index 9745a6d..a0ab5c7 100644 --- a/marketdata/stream/message.go +++ b/marketdata/stream/message.go @@ -51,43 +51,13 @@ func (c *client) handleMessage(b []byte) error { if key != "T" { return fmt.Errorf("first key is not T but: %s", key) } - T, err := d.DecodeString() + msgType, err := d.DecodeString() if err != nil { return err } n-- // T already processed - switch T { - case "t": - err = c.handler.handleTrade(d, n) - case "q": - err = c.handler.handleQuote(d, n) - case "b": - err = c.handler.handleBar(d, n) - case "u": - err = c.handler.handleUpdatedBar(d, n) - case "d": - err = c.handler.handleDailyBar(d, n) - case "s": - err = c.handler.handleTradingStatus(d, n) - case "l": - err = c.handler.handleLULD(d, n) - case "x": - err = c.handler.handleCancelError(d, n) - case "c": - err = c.handler.handleCorrection(d, n) - case "o": - err = c.handler.handleOrderbook(d, n) - case "n": - err = c.handler.handleNews(d, n) - case "subscription": - err = c.handleSubscriptionMessage(d, n) - case "error": - err = c.handleErrorMessage(d, n) - default: - err = c.handleOther(d, n) - } - if err != nil { + if err := c.handleMessageType(msgType, d, n); err != nil { return err } } @@ -95,6 +65,41 @@ func (c *client) handleMessage(b []byte) error { return nil } +const msgTypeError = "error" + +func (c *client) handleMessageType(msgType string, d *msgpack.Decoder, n int) error { + switch msgType { + case "t": + return c.handler.handleTrade(d, n) + case "q": + return c.handler.handleQuote(d, n) + case "b": + return c.handler.handleBar(d, n) + case "u": + return c.handler.handleUpdatedBar(d, n) + case "d": + return c.handler.handleDailyBar(d, n) + case "s": + return c.handler.handleTradingStatus(d, n) + case "l": + return c.handler.handleLULD(d, n) + case "x": + return c.handler.handleCancelError(d, n) + case "c": + return c.handler.handleCorrection(d, n) + case "o": + return c.handler.handleOrderbook(d, n) + case "n": + return c.handler.handleNews(d, n) + case "subscription": + return c.handleSubscriptionMessage(d, n) + case msgTypeError: + return c.handleErrorMessage(d, n) + default: + return c.handleOther(d, n) + } +} + type stocksMsgHandler struct { mu sync.RWMutex tradeHandler func(trade Trade) @@ -130,8 +135,6 @@ func (h *stocksMsgHandler) handleTrade(d *msgpack.Decoder, n int) error { trade.Size, err = d.DecodeUint32() case "t": trade.Timestamp, err = d.DecodeTime() - case "r": - trade.internal.ReceivedAt, err = d.DecodeTime() case "c": trade.Conditions, err = decodeStringSlice(d) case "z": @@ -174,8 +177,6 @@ func (h *stocksMsgHandler) handleQuote(d *msgpack.Decoder, n int) error { quote.AskSize, err = d.DecodeUint32() case "t": quote.Timestamp, err = d.DecodeTime() - case "r": - quote.internal.ReceivedAt, err = d.DecodeTime() case "c": quote.Conditions, err = decodeStringSlice(d) case "z": @@ -1013,11 +1014,11 @@ func decodeStringSlice(d *msgpack.Decoder) ([]string, error) { } res := make([]string, length) for i := 0; i < length; i++ { - if s, err := d.DecodeString(); err != nil { + s, err := d.DecodeString() + if err != nil { return nil, err - } else { - res[i] = s } + res[i] = s } return res, nil } @@ -1033,11 +1034,11 @@ func decodeCryptoOrderbookEntrySlice(d *msgpack.Decoder) ([]CryptoOrderbookEntry } res := make([]CryptoOrderbookEntry, length) for i := 0; i < length; i++ { - if e, err := decodeCryptoOrderbookEntry(d); err != nil { + e, err := decodeCryptoOrderbookEntry(d) + if err != nil { return nil, err - } else { - res[i] = e } + res[i] = e } return res, nil } diff --git a/marketdata/stream/message_test.go b/marketdata/stream/message_test.go index 4dc614a..5a85110 100644 --- a/marketdata/stream/message_test.go +++ b/marketdata/stream/message_test.go @@ -128,7 +128,7 @@ type cryptoTradeWithT struct { Price float64 `msgpack:"p"` Size float64 `msgpack:"s"` Timestamp time.Time `msgpack:"t"` - Id int64 `msgpack:"i"` + ID int64 `msgpack:"i"` TakerSide string `msgpack:"tks"` // NewField is for testing correct handling of added fields in the future NewField uint64 `msgpack:"n"` @@ -377,7 +377,7 @@ var testCryptoTrade = cryptoTradeWithT{ Price: 100, Size: 10.1, Timestamp: testTime, - Id: 32, + ID: 32, TakerSide: "B", } @@ -441,7 +441,7 @@ var testOther = other{ } var testError = errorWithT{ - Type: "error", + Type: msgTypeError, Msg: "test", Code: 322, } @@ -492,11 +492,11 @@ func TestHandleMessagesStocks(t *testing.T) { subscriptionMessages := make([]subscriptions, 0) var em errorMessage - errMessageHandler = func(c *client, e errorMessage) error { + errMessageHandler = func(_ *client, e errorMessage) error { em = e return nil } - subMessageHandler = func(c *client, s subscriptions) error { + subMessageHandler = func(_ *client, s subscriptions) error { subscriptionMessages = append(subscriptionMessages, s) return nil } @@ -548,7 +548,6 @@ func TestHandleMessagesStocks(t *testing.T) { assert.EqualValues(t, testTrade.Price, trade.Price) assert.EqualValues(t, testTrade.Size, trade.Size) assert.True(t, trade.Timestamp.Equal(testTime)) - assert.True(t, trade.Internal().ReceivedAt.Equal(testTime2)) assert.EqualValues(t, testTrade.Conditions, trade.Conditions) assert.EqualValues(t, testTrade.Tape, trade.Tape) @@ -602,7 +601,6 @@ func TestHandleMessagesStocks(t *testing.T) { assert.EqualValues(t, testQuote.AskPrice, quote.AskPrice) assert.EqualValues(t, testQuote.AskSize, quote.AskSize) assert.True(t, quote.Timestamp.Equal(testTime)) - assert.True(t, quote.Internal().ReceivedAt.Equal(testTime2)) assert.EqualValues(t, testQuote.Conditions, quote.Conditions) assert.EqualValues(t, testQuote.Tape, quote.Tape) @@ -671,11 +669,11 @@ func TestHandleMessagesCrypto(t *testing.T) { subscriptionMessages := make([]subscriptions, 0) var em errorMessage - errMessageHandler = func(c *client, e errorMessage) error { + errMessageHandler = func(_ *client, e errorMessage) error { em = e return nil } - subMessageHandler = func(c *client, s subscriptions) error { + subMessageHandler = func(_ *client, s subscriptions) error { subscriptionMessages = append(subscriptionMessages, s) return nil } @@ -713,7 +711,7 @@ func TestHandleMessagesCrypto(t *testing.T) { assert.EqualValues(t, testCryptoTrade.Exchange, trade.Exchange) assert.EqualValues(t, testCryptoTrade.Price, trade.Price) assert.EqualValues(t, testCryptoTrade.Size, trade.Size) - assert.EqualValues(t, testCryptoTrade.Id, trade.ID) + assert.EqualValues(t, testCryptoTrade.ID, trade.ID) assert.EqualValues(t, testCryptoTrade.TakerSide, trade.TakerSide) assert.True(t, trade.Timestamp.Equal(testTime)) @@ -779,9 +777,9 @@ func BenchmarkHandleMessages(b *testing.B) { msgs, _ := msgpack.Marshal([]interface{}{testTrade, testQuote, testBar}) c := &client{ handler: &stocksMsgHandler{ - tradeHandler: func(trade Trade) {}, - quoteHandler: func(quote Quote) {}, - barHandler: func(bar Bar) {}, + tradeHandler: func(_ Trade) {}, + quoteHandler: func(_ Quote) {}, + barHandler: func(_ Bar) {}, }, } diff --git a/marketdata/stream/options.go b/marketdata/stream/options.go index c28106f..94a96cf 100644 --- a/marketdata/stream/options.go +++ b/marketdata/stream/options.go @@ -212,15 +212,15 @@ func defaultStockOptions() *stockOptions { }, connCreator: newCoderWebsocketConn, }, - tradeHandler: func(t Trade) {}, - quoteHandler: func(q Quote) {}, - barHandler: func(b Bar) {}, - updatedBarHandler: func(b Bar) {}, - dailyBarHandler: func(b Bar) {}, - tradingStatusHandler: func(ts TradingStatus) {}, - luldHandler: func(l LULD) {}, - cancelErrorHandler: func(tce TradeCancelError) {}, - correctionHandler: func(tc TradeCorrection) {}, + tradeHandler: func(_ Trade) {}, + quoteHandler: func(_ Quote) {}, + barHandler: func(_ Bar) {}, + updatedBarHandler: func(_ Bar) {}, + dailyBarHandler: func(_ Bar) {}, + tradingStatusHandler: func(_ TradingStatus) {}, + luldHandler: func(_ LULD) {}, + cancelErrorHandler: func(_ TradeCancelError) {}, + correctionHandler: func(_ TradeCorrection) {}, } } @@ -357,12 +357,12 @@ func defaultCryptoOptions() *cryptoOptions { }, connCreator: newCoderWebsocketConn, }, - tradeHandler: func(t CryptoTrade) {}, - quoteHandler: func(q CryptoQuote) {}, - barHandler: func(b CryptoBar) {}, - updatedBarHandler: func(b CryptoBar) {}, - dailyBarHandler: func(b CryptoBar) {}, - orderbookHandler: func(ob CryptoOrderbook) {}, + tradeHandler: func(_ CryptoTrade) {}, + quoteHandler: func(_ CryptoQuote) {}, + barHandler: func(_ CryptoBar) {}, + updatedBarHandler: func(_ CryptoBar) {}, + dailyBarHandler: func(_ CryptoBar) {}, + orderbookHandler: func(_ CryptoOrderbook) {}, } } @@ -468,8 +468,8 @@ func defaultOptionOptions() *optionOptions { }, connCreator: newCoderWebsocketConn, }, - tradeHandler: func(t OptionTrade) {}, - quoteHandler: func(q OptionQuote) {}, + tradeHandler: func(_ OptionTrade) {}, + quoteHandler: func(_ OptionQuote) {}, } } @@ -532,7 +532,7 @@ func defaultNewsOptions() *newsOptions { }, connCreator: newCoderWebsocketConn, }, - newsHandler: func(n News) {}, + newsHandler: func(_ News) {}, } } diff --git a/marketdata/stream/options_test.go b/marketdata/stream/options_test.go index 7895e89..2612f5f 100644 --- a/marketdata/stream/options_test.go +++ b/marketdata/stream/options_test.go @@ -1,7 +1,6 @@ package stream import ( - "os" "testing" "time" @@ -25,9 +24,9 @@ func TestDefaultOptions(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - os.Setenv("APCA_API_KEY_ID", "testkey") - os.Setenv("APCA_API_SECRET_KEY", "testsecret") - os.Setenv("DATA_PROXY_WS", test.dataProxyWSVal) + t.Setenv("APCA_API_KEY_ID", "testkey") + t.Setenv("APCA_API_SECRET_KEY", "testsecret") + t.Setenv("DATA_PROXY_WS", test.dataProxyWSVal) o := defaultStockOptions() @@ -65,15 +64,15 @@ func TestConfigureStocks(t *testing.T) { WithReconnectSettings(42, 322*time.Nanosecond), WithProcessors(322), WithBufferSize(1000000), - WithTrades(func(t Trade) {}, "ALPACA"), - WithQuotes(func(q Quote) {}, "AL", "PACA"), - WithBars(func(b Bar) {}, "ALP", "ACA"), - WithUpdatedBars(func(b Bar) {}, "AAPL"), - WithDailyBars(func(b Bar) {}, "LPACA"), - WithStatuses(func(ts TradingStatus) {}, "ALPACA"), - WithLULDs(func(l LULD) {}, "ALPA", "CA"), - WithCancelErrors(func(tce TradeCancelError) {}), - WithCorrections(func(tc TradeCorrection) {}), + WithTrades(func(_ Trade) {}, "ALPACA"), + WithQuotes(func(_ Quote) {}, "AL", "PACA"), + WithBars(func(_ Bar) {}, "ALP", "ACA"), + WithUpdatedBars(func(_ Bar) {}, "AAPL"), + WithDailyBars(func(_ Bar) {}, "LPACA"), + WithStatuses(func(_ TradingStatus) {}, "ALPACA"), + WithLULDs(func(_ LULD) {}, "ALPA", "CA"), + WithCancelErrors(func(_ TradeCancelError) {}), + WithCorrections(func(_ TradeCorrection) {}), ) assert.EqualValues(t, logger, c.logger)