├── .github ├── CODEOWNERS ├── dependabot.yml ├── workflows │ ├── test.yml │ ├── lint.yml │ ├── codeql.yml │ └── test-coverage.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── rest ├── models │ ├── summaries_test.go │ ├── exchanges_test.go │ ├── conditions_test.go │ ├── quotes_test.go │ ├── trades_test.go │ ├── snapshot_deprecated.go │ ├── markets.go │ ├── exchanges.go │ ├── splits_test.go │ ├── summaries.go │ ├── response.go │ ├── financials_test.go │ ├── aggs_test.go │ ├── types_test.go │ ├── economy.go │ ├── contracts_test.go │ ├── request.go │ ├── dividends_test.go │ ├── splits.go │ ├── snapshot_test.go │ ├── conditions.go │ └── tickers_test.go ├── example │ ├── forex │ │ ├── market-status │ │ │ └── main.go │ │ ├── market-holidays │ │ │ └── main.go │ │ ├── exchanges │ │ │ └── main.go │ │ ├── snapshots-universal │ │ │ └── main.go │ │ ├── technical-indicators-rsi │ │ │ └── main.go │ │ ├── technical-indicators-sma │ │ │ └── main.go │ │ ├── technical-indicators-ema │ │ │ └── main.go │ │ ├── previous-close │ │ │ └── main.go │ │ ├── last-quote-for-a-currency-pair │ │ │ └── main.go │ │ ├── conditions │ │ │ └── main.go │ │ ├── real-time-currency-conversion │ │ │ └── main.go │ │ ├── snapshots-all-tickers │ │ │ └── main.go │ │ ├── snapshots-ticker │ │ │ └── main.go │ │ ├── technical-indicators-macd │ │ │ └── main.go │ │ ├── tickers │ │ │ └── main.go │ │ ├── snapshots-gainers-losers │ │ │ └── main.go │ │ ├── grouped-daily-bars │ │ │ └── main.go │ │ ├── quotes │ │ │ └── main.go │ │ └── aggregates-bars │ │ │ └── main.go │ ├── crypto │ │ ├── market-status │ │ │ └── main.go │ │ ├── market-holidays │ │ │ └── main.go │ │ ├── exchanges │ │ │ └── main.go │ │ ├── snapshots-universal │ │ │ └── main.go │ │ ├── technical-indicators-sma │ │ │ └── main.go │ │ ├── technical-indicators-ema │ │ │ └── main.go │ │ ├── technical-indicators-rsi │ │ │ └── main.go │ │ ├── previous-close │ │ │ └── main.go │ │ ├── last-trade-for-a-crypto-pair │ │ │ └── main.go │ │ ├── conditions │ │ │ └── main.go │ │ ├── snapshots-ticker-full-book-l2 │ │ │ └── main.go │ │ ├── trades │ │ │ └── main.go │ │ ├── snapshots-all-tickers │ │ │ └── main.go │ │ ├── snapshots-ticker │ │ │ └── main.go │ │ ├── daily-open-close │ │ │ └── main.go │ │ ├── technical-indicators-macd │ │ │ └── main.go │ │ ├── tickers │ │ │ └── main.go │ │ ├── snapshots-gainers-losers │ │ │ └── main.go │ │ ├── grouped-daily-bars │ │ │ └── main.go │ │ └── aggregates-bars │ │ │ └── main.go │ ├── indices │ │ ├── market-status │ │ │ └── main.go │ │ ├── market-holidays │ │ │ └── main.go │ │ ├── snapshots-universal │ │ │ └── main.go │ │ ├── ticker-types │ │ │ └── main.go │ │ ├── technical-indicators-sma │ │ │ └── main.go │ │ ├── technical-indicators-ema │ │ │ └── main.go │ │ ├── technical-indicators-rsi │ │ │ └── main.go │ │ ├── previous-close │ │ │ └── main.go │ │ ├── daily-open-close │ │ │ └── main.go │ │ ├── snapshots │ │ │ └── main.go │ │ ├── technical-indicators-macd │ │ │ └── main.go │ │ ├── tickers │ │ │ └── main.go │ │ └── aggregates-bars │ │ │ └── main.go │ ├── stocks │ │ ├── market-status │ │ │ └── main.go │ │ ├── market-holidays │ │ │ └── main.go │ │ ├── exchanges │ │ │ └── main.go │ │ ├── snapshots-universal │ │ │ └── main.go │ │ ├── ipos │ │ │ └── main.go │ │ ├── short-volume │ │ │ └── main.go │ │ ├── short-interest │ │ │ └── main.go │ │ ├── last-trade │ │ │ └── main.go │ │ ├── last-quote │ │ │ └── main.go │ │ ├── ticker-events │ │ │ └── main.go │ │ ├── previous-close │ │ │ └── main.go │ │ ├── related-companies │ │ │ └── main.go │ │ ├── technical-indicators-rsi │ │ │ └── main.go │ │ ├── technical-indicators-sma │ │ │ └── main.go │ │ ├── ticker-types │ │ │ └── main.go │ │ ├── technical-indicators-ema │ │ │ └── main.go │ │ ├── stock-financials │ │ │ └── main.go │ │ ├── trades │ │ │ └── main.go │ │ ├── ticker-details │ │ │ └── main.go │ │ ├── snapshots-all │ │ │ └── main.go │ │ ├── snapshots-ticker │ │ │ └── main.go │ │ ├── conditions │ │ │ └── main.go │ │ ├── daily-open-close │ │ │ └── main.go │ │ ├── snapshots-gainers-losers │ │ │ └── main.go │ │ ├── dividends │ │ │ └── main.go │ │ ├── technical-indicators-macd │ │ │ └── main.go │ │ ├── grouped-daily-bars │ │ │ └── main.go │ │ ├── tickers │ │ │ └── main.go │ │ ├── quotes │ │ │ └── main.go │ │ ├── ticker-news │ │ │ └── main.go │ │ ├── stock-splits │ │ │ └── main.go │ │ └── aggregates-bars │ │ │ └── main.go │ ├── options │ │ ├── market-status │ │ │ └── main.go │ │ ├── market-holidays │ │ │ └── main.go │ │ ├── snapshots-universal │ │ │ └── main.go │ │ ├── last-trade │ │ │ └── main.go │ │ ├── exchanges │ │ │ └── main.go │ │ ├── technical-indicators-rsi │ │ │ └── main.go │ │ ├── technical-indicators-sma │ │ │ └── main.go │ │ ├── technical-indicators-ema │ │ │ └── main.go │ │ ├── previous-close │ │ │ └── main.go │ │ ├── technical-indicators-macd │ │ │ └── main.go │ │ ├── quotes │ │ │ └── main.go │ │ ├── trades │ │ │ └── main.go │ │ ├── ticker-details │ │ │ └── main.go │ │ ├── conditions │ │ │ └── main.go │ │ ├── snapshots-option-contract │ │ │ └── main.go │ │ ├── contract │ │ │ └── main.go │ │ ├── contracts │ │ │ └── main.go │ │ ├── daily-open-close │ │ │ └── main.go │ │ ├── tickers │ │ │ └── main.go │ │ ├── ticker-news │ │ │ └── main.go │ │ ├── snapshots-options-chain │ │ │ └── main.go │ │ └── aggregates-bars │ │ │ └── main.go │ ├── economy │ │ └── treasury-yields │ │ │ └── main.go │ ├── launchpad │ │ ├── main.go │ │ └── README.md │ └── main.go ├── summaries.go ├── massive.go ├── iter │ └── iter.go ├── trades.go ├── vx.go ├── quotes.go ├── indicators.go ├── client │ └── client.go ├── encoder │ ├── encoder.go │ └── encoder_test.go ├── summaries_test.go ├── aggs.go └── trades_test.go ├── .massive └── rest.go ├── go.mod ├── LICENSE ├── websocket ├── example │ ├── fmv │ │ └── main.go │ ├── main.go │ └── launchpad │ │ └── main.go ├── subscription.go ├── subscription_test.go └── client_test.go └── Makefile /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @morningvera @mmoghaddam385 @jbonzo @justinpolygon @kschoche @HunterL @lukeoleson 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool 12 | *.out 13 | 14 | # Dependency directories 15 | vendor/ 16 | 17 | # IntelliJ project settings 18 | .idea 19 | -------------------------------------------------------------------------------- /rest/models/summaries_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/massive-com/client-go/v2/rest/models" 7 | ) 8 | 9 | func TestGetSummaryParams(t *testing.T) { 10 | tickers := "AAPL,GOOL,TSLA" 11 | expect := models.GetSummaryParams{ 12 | TickerAnyOf: &tickers, 13 | } 14 | actual := models.GetSummaryParams{}.WithTickerAnyOf(tickers) 15 | checkParams(t, expect, *actual) 16 | } 17 | -------------------------------------------------------------------------------- /rest/models/exchanges_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/massive-com/client-go/v2/rest/models" 7 | ) 8 | 9 | func TestGetExchangesParams(t *testing.T) { 10 | assetClass := models.AssetStocks 11 | locale := models.US 12 | expect := models.GetExchangesParams{ 13 | AssetClass: &assetClass, 14 | Locale: &locale, 15 | } 16 | actual := models.GetExchangesParams{}. 17 | WithAssetClass(assetClass). 18 | WithLocale(locale) 19 | checkParams(t, expect, *actual) 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | branches: 7 | - master 8 | pull_request: 9 | permissions: 10 | contents: read 11 | jobs: 12 | test: 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest, macos-latest, windows-latest] 16 | go-version: [1.19.x, 1.20.x, 1.21.x] 17 | name: go-test 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - uses: actions/setup-go@v4 21 | with: 22 | go-version: ${{ matrix.go-version }} 23 | - uses: actions/checkout@v4 24 | - name: go-test 25 | run: go test -race -v ./... 26 | -------------------------------------------------------------------------------- /rest/example/forex/market-status/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Market Status 2 | // https://massive.com/docs/forex/get_v1_marketstatus_now 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | ) 13 | 14 | func main() { 15 | // init client 16 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 17 | 18 | // make request 19 | res, err := c.GetMarketStatus(context.Background()) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | // do something with the result 25 | log.Print(res) 26 | } 27 | -------------------------------------------------------------------------------- /rest/example/crypto/market-status/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Market Status 2 | // https://massive.com/docs/crypto/get_v1_marketstatus_now 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | ) 13 | 14 | func main() { 15 | // init client 16 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 17 | 18 | // make request 19 | res, err := c.GetMarketStatus(context.Background()) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | // do something with the result 25 | log.Print(res) 26 | } 27 | -------------------------------------------------------------------------------- /rest/example/indices/market-status/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Market Status 2 | // https://massive.com/docs/indices/get_v1_marketstatus_now 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | ) 13 | 14 | func main() { 15 | // init client 16 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 17 | 18 | // make request 19 | res, err := c.GetMarketStatus(context.Background()) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | // do something with the result 25 | log.Print(res) 26 | } 27 | -------------------------------------------------------------------------------- /rest/example/forex/market-holidays/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Market Holidays 2 | // https://massive.com/docs/forex/get_v1_marketstatus_upcoming 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | ) 13 | 14 | func main() { 15 | // init client 16 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 17 | 18 | // make request 19 | res, err := c.GetMarketHolidays(context.Background()) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | // do something with the result 25 | log.Print(res) 26 | } 27 | -------------------------------------------------------------------------------- /rest/example/crypto/market-holidays/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Market Holidays 2 | // https://massive.com/docs/crypto/get_v1_marketstatus_upcoming 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | ) 13 | 14 | func main() { 15 | // init client 16 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 17 | 18 | // make request 19 | res, err := c.GetMarketHolidays(context.Background()) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | // do something with the result 25 | log.Print(res) 26 | } 27 | -------------------------------------------------------------------------------- /rest/example/stocks/market-status/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Market Status 2 | // https://massive.com/docs/stocks/get_v1_marketstatus_now 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | ) 13 | 14 | func main() { 15 | 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // make request 20 | res, err := c.GetMarketStatus(context.Background()) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // do something with the result 26 | log.Print(res) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /rest/example/indices/market-holidays/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Market Holidays 2 | // https://massive.com/docs/indices/get_v1_marketstatus_upcoming 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | ) 13 | 14 | func main() { 15 | // init client 16 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 17 | 18 | // make request 19 | res, err := c.GetMarketHolidays(context.Background()) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | // do something with the result 25 | log.Print(res) 26 | } 27 | -------------------------------------------------------------------------------- /rest/example/options/market-status/main.go: -------------------------------------------------------------------------------- 1 | // Options - Market Status 2 | // https://massive.com/docs/options/get_v1_marketstatus_now 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | ) 13 | 14 | func main() { 15 | 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // make request 20 | res, err := c.GetMarketStatus(context.Background()) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // do something with the result 26 | log.Print(res) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /rest/example/stocks/market-holidays/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Market Holidays 2 | // https://massive.com/docs/stocks/get_v1_marketstatus_upcoming 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | ) 13 | 14 | func main() { 15 | 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // make request 20 | res, err := c.GetMarketHolidays(context.Background()) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // do something with the result 26 | log.Print(res) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /rest/example/options/market-holidays/main.go: -------------------------------------------------------------------------------- 1 | // Options - Market Holidays 2 | // https://massive.com/docs/options/get_v1_marketstatus_upcoming 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | ) 13 | 14 | func main() { 15 | 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // make request 20 | res, err := c.GetMarketHolidays(context.Background()) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // do something with the result 26 | log.Print(res) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /rest/example/stocks/exchanges/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Exchanges 2 | // https://massive.com/docs/stocks/get_v3_reference_exchanges 3 | package main 4 | 5 | import ( 6 | "context" 7 | "log" 8 | "os" 9 | 10 | massive "github.com/massive-com/client-go/v2/rest" 11 | "github.com/massive-com/client-go/v2/rest/models" 12 | ) 13 | 14 | func main() { 15 | 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetExchangesParams{} 21 | 22 | // make request 23 | res, err := c.GetExchanges(context.Background(), params) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // do something with the result 29 | log.Print(res) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /rest/example/stocks/snapshots-universal/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | massive "github.com/massive-com/client-go/v2/rest" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | func main() { 13 | // Init client 14 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 15 | 16 | // Set parameters 17 | params := models.ListUniversalSnapshotsParams{}.WithTickerAnyOf("AAPL,META,F") 18 | 19 | // Make request 20 | iter := c.ListUniversalSnapshots(context.Background(), params) 21 | 22 | // do something with the result 23 | for iter.Next() { 24 | log.Println(iter.Item()) 25 | } 26 | if iter.Err() != nil { 27 | log.Fatal(iter.Err()) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /rest/example/forex/exchanges/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Exchanges 2 | // https://massive.com/docs/forex/get_v3_reference_exchanges 3 | package main 4 | 5 | import ( 6 | "context" 7 | "log" 8 | "os" 9 | 10 | massive "github.com/massive-com/client-go/v2/rest" 11 | "github.com/massive-com/client-go/v2/rest/models" 12 | ) 13 | 14 | func main() { 15 | // init client 16 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 17 | 18 | // set params 19 | params := models.GetExchangesParams{}. 20 | WithAssetClass(models.AssetFx) 21 | 22 | // make request 23 | res, err := c.GetExchanges(context.Background(), params) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // do something with the result 29 | log.Print(res) 30 | } 31 | -------------------------------------------------------------------------------- /rest/example/crypto/exchanges/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Exchanges 2 | // https://massive.com/docs/crypto/get_v3_reference_exchanges 3 | package main 4 | 5 | import ( 6 | "context" 7 | "log" 8 | "os" 9 | 10 | massive "github.com/massive-com/client-go/v2/rest" 11 | "github.com/massive-com/client-go/v2/rest/models" 12 | ) 13 | 14 | func main() { 15 | // init client 16 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 17 | 18 | // set params 19 | params := models.GetExchangesParams{}. 20 | WithAssetClass(models.AssetCrypto) 21 | 22 | // make request 23 | res, err := c.GetExchanges(context.Background(), params) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // do something with the result 29 | log.Print(res) 30 | } 31 | -------------------------------------------------------------------------------- /rest/example/indices/snapshots-universal/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | massive "github.com/massive-com/client-go/v2/rest" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | func main() { 13 | // Init client 14 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 15 | 16 | // Set parameters 17 | params := models.ListUniversalSnapshotsParams{}. 18 | WithTickerAnyOf("I:SPX,I:DJI,I:A1BSC") 19 | 20 | // Make request 21 | iter := c.ListUniversalSnapshots(context.Background(), params) 22 | 23 | // do something with the result 24 | for iter.Next() { 25 | log.Println(iter.Item()) 26 | } 27 | if iter.Err() != nil { 28 | log.Fatal(iter.Err()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /rest/example/crypto/snapshots-universal/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | massive "github.com/massive-com/client-go/v2/rest" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | func main() { 13 | // Init client 14 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 15 | 16 | // Set parameters 17 | params := models.ListUniversalSnapshotsParams{}. 18 | WithTickerAnyOf("X:BTCUSD,X:ETHUSD,X:FLOWUSD") 19 | 20 | // Make request 21 | iter := c.ListUniversalSnapshots(context.Background(), params) 22 | 23 | // do something with the result 24 | for iter.Next() { 25 | log.Println(iter.Item()) 26 | } 27 | if iter.Err() != nil { 28 | log.Fatal(iter.Err()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /rest/example/forex/snapshots-universal/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | massive "github.com/massive-com/client-go/v2/rest" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | func main() { 13 | // Init client 14 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 15 | 16 | // Set parameters 17 | params := models.ListUniversalSnapshotsParams{}. 18 | WithTickerAnyOf("C:USDCAD,C:USDEUR,C:USDAUD") 19 | 20 | // Make request 21 | iter := c.ListUniversalSnapshots(context.Background(), params) 22 | 23 | // do something with the result 24 | for iter.Next() { 25 | log.Println(iter.Item()) 26 | } 27 | if iter.Err() != nil { 28 | log.Fatal(iter.Err()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /rest/example/stocks/ipos/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | massive "github.com/massive-com/client-go/v2/rest" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | func main() { 13 | // Initialize client 14 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 15 | 16 | // Set parameters 17 | params := models.ListIPOsParams{}. 18 | WithTicker("RAPP"). 19 | WithListingDate(models.EQ, "2024-06-07"). 20 | WithOrder(models.Desc). 21 | WithLimit(1000) 22 | 23 | // Make request 24 | iter := c.VX.ListIPOs(context.Background(), params) 25 | 26 | // Process results 27 | for iter.Next() { 28 | log.Print(iter.Item()) 29 | } 30 | if iter.Err() != nil { 31 | log.Fatal(iter.Err()) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | branches: 7 | - master 8 | pull_request: 9 | permissions: 10 | contents: read 11 | jobs: 12 | golangci: 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest] 16 | go-version: [1.21.x] 17 | name: golangci-lint 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - uses: actions/setup-go@v4 21 | with: 22 | go-version: ${{ matrix.go-version }} 23 | - uses: actions/checkout@v4 24 | - name: golangci-lint 25 | uses: golangci/golangci-lint-action@v3 26 | with: 27 | version: v1.54 28 | - name: gofmt 29 | run: | 30 | go fmt ./... 31 | git diff --exit-code 32 | -------------------------------------------------------------------------------- /rest/example/options/snapshots-universal/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | massive "github.com/massive-com/client-go/v2/rest" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | func main() { 13 | // Init client 14 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 15 | 16 | // Set parameters 17 | params := models.ListUniversalSnapshotsParams{}. 18 | WithTickerAnyOf("O:AAPL230512C00050000,O:META230512C00020000,O:F230512C00005000") 19 | 20 | // Make request 21 | iter := c.ListUniversalSnapshots(context.Background(), params) 22 | 23 | // do something with the result 24 | for iter.Next() { 25 | log.Println(iter.Item()) 26 | } 27 | if iter.Err() != nil { 28 | log.Fatal(iter.Err()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 14 | 15 | **To Reproduce** 16 | 23 | 24 | **Expected behavior** 25 | 28 | 29 | **Screenshots** 30 | 33 | 34 | **Additional context** 35 | 38 | -------------------------------------------------------------------------------- /rest/example/stocks/short-volume/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | massive "github.com/massive-com/client-go/v2/rest" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | func main() { 13 | // Initialize client 14 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 15 | 16 | // Set parameters 17 | params := models.ListShortVolumeParams{}. 18 | WithTicker(models.EQ, "A"). 19 | WithDate(models.EQ, "2025-03-25"). 20 | WithOrder(models.Asc). 21 | WithLimit(50000) 22 | 23 | // Make request 24 | iter := c.ListShortVolume(context.Background(), params) 25 | 26 | // Process results 27 | for iter.Next() { 28 | log.Print(iter.Item()) 29 | } 30 | if iter.Err() != nil { 31 | log.Fatal(iter.Err()) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | 14 | 15 | **Describe the solution you'd like** 16 | 19 | 20 | **Describe alternatives you've considered** 21 | 24 | 25 | **Additional context** 26 | 29 | -------------------------------------------------------------------------------- /rest/example/economy/treasury-yields/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | massive "github.com/massive-com/client-go/v2/rest" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | func main() { 13 | // Initialize client 14 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 15 | 16 | // Set parameters 17 | params := models.ListTreasuryYieldsParams{}. 18 | WithDate(models.GTE, "2024-12-15"). 19 | WithDate(models.LTE, "2024-12-31"). 20 | WithOrder(models.Asc). 21 | WithLimit(50000) 22 | 23 | // Make request 24 | iter := c.ListTreasuryYields(context.Background(), params) 25 | 26 | // Process results 27 | for iter.Next() { 28 | log.Print(iter.Item()) 29 | } 30 | if iter.Err() != nil { 31 | log.Fatal(iter.Err()) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rest/example/stocks/short-interest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | massive "github.com/massive-com/client-go/v2/rest" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | func main() { 13 | // Initialize client 14 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 15 | 16 | // Set parameters 17 | params := models.ListShortInterestParams{}. 18 | WithTicker(models.EQ, "A"). 19 | WithSettlementDate(models.EQ, "2025-03-14"). 20 | WithOrder(models.Asc). 21 | WithLimit(50000) 22 | 23 | // Make request 24 | iter := c.ListShortInterest(context.Background(), params) 25 | 26 | // Process results 27 | for iter.Next() { 28 | log.Print(iter.Item()) 29 | } 30 | if iter.Err() != nil { 31 | log.Fatal(iter.Err()) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rest/example/indices/ticker-types/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Ticker Types 2 | // https://massive.com/docs/indices/get_v3_reference_tickers_types 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.GetTickerTypesParams{}.WithAssetClass("indices") 21 | 22 | // make request 23 | res, err := c.GetTickerTypes(context.Background(), params) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | // do something with the result 29 | log.Print(res) 30 | } 31 | -------------------------------------------------------------------------------- /rest/example/stocks/last-trade/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Last Trade 2 | // https://massive.com/docs/stocks/get_v2_last_trade__stocksticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/trades.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetLastTradeParams{ 22 | Ticker: "AAPL", 23 | } 24 | 25 | // make request 26 | res, err := c.GetLastTrade(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/stocks/last-quote/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Last Quote (NBBO) 2 | // https://massive.com/docs/stocks/get_v2_last_nbbo__stocksticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/quotes.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetLastQuoteParams{ 22 | Ticker: "AAPL", 23 | } 24 | 25 | // make request 26 | res, err := c.GetLastQuote(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/forex/technical-indicators-rsi/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Relative Strength Index (RSI) 2 | // https://massive.com/docs/forex/get_v1_indicators_rsi__fxticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetRSIParams{ 21 | Ticker: "C:EURUSD", 22 | } 23 | 24 | // make request 25 | res, err := c.GetRSI(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/forex/technical-indicators-sma/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Simple Moving Average (SMA) 2 | // https://massive.com/docs/forex/get_v1_indicators_sma__fxticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetSMAParams{ 21 | Ticker: "C:EURUSD", 22 | } 23 | 24 | // make request 25 | res, err := c.GetSMA(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/stocks/ticker-events/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Ticker Events 2 | // https://massive.com/docs/stocks/get_vx_reference_tickers__id__events 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/vx.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetTickerEventsParams{ 22 | ID: "TSLA", 23 | } 24 | 25 | // make request 26 | res, err := c.VX.GetTickerEvents(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/crypto/technical-indicators-sma/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Simple Moving Average (SMA) 2 | // https://massive.com/docs/crypto/get_v1_indicators_sma__cryptoticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetSMAParams{ 21 | Ticker: "X:BTCUSD", 22 | } 23 | 24 | // make request 25 | res, err := c.GetSMA(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/forex/technical-indicators-ema/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Exponential Moving Average (EMA) 2 | // https://massive.com/docs/forex/get_v1_indicators_ema__fxticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetEMAParams{ 21 | Ticker: "C:EURUSD", 22 | } 23 | 24 | // make request 25 | res, err := c.GetEMA(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/indices/technical-indicators-sma/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Simple Moving Average (SMA) 2 | // https://massive.com/docs/indices/get_v1_indicators_sma__indicesticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetSMAParams{ 21 | Ticker: "I:SPX", 22 | } 23 | 24 | // make request 25 | res, err := c.GetSMA(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/crypto/technical-indicators-ema/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Exponential Moving Average (EMA) 2 | // https://massive.com/docs/crypto/get_v1_indicators_ema__cryptoticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetEMAParams{ 21 | Ticker: "X:BTCUSD", 22 | } 23 | 24 | // make request 25 | res, err := c.GetEMA(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/crypto/technical-indicators-rsi/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Relative Strength Index (RSI) 2 | // https://massive.com/docs/crypto/get_v1_indicators_rsi__cryptoticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetRSIParams{ 21 | Ticker: "X:BTCUSD", 22 | } 23 | 24 | // make request 25 | res, err := c.GetRSI(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/indices/technical-indicators-ema/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Exponential Moving Average (EMA) 2 | // https://massive.com/docs/indices/get_v1_indicators_ema__indicesticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetEMAParams{ 21 | Ticker: "I:SPX", 22 | } 23 | 24 | // make request 25 | res, err := c.GetEMA(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/indices/technical-indicators-rsi/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Relative Strength Index (RSI) 2 | // https://massive.com/docs/indices/get_v1_indicators_rsi__indicesticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetRSIParams{ 21 | Ticker: "I:SPX", 22 | } 23 | 24 | // make request 25 | res, err := c.GetRSI(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/options/last-trade/main.go: -------------------------------------------------------------------------------- 1 | // Options - Last Trade 2 | // https://massive.com/docs/options/get_v2_last_trade__optionsticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/trades.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetLastTradeParams{ 22 | Ticker: "O:TSLA210903C00700000", 23 | } 24 | 25 | // make request 26 | res, err := c.GetLastTrade(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/forex/previous-close/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Previous Close 2 | // https://massive.com/docs/forex/get_v2_aggs_ticker__forexticker__prev 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.GetPreviousCloseAggParams{ 21 | Ticker: "C:EURUSD", 22 | }.WithAdjusted(true) 23 | 24 | // make request 25 | res, err := c.GetPreviousCloseAgg(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/crypto/previous-close/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Previous Close 2 | // https://massive.com/docs/crypto/get_v2_aggs_ticker__cryptoticker__prev 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.GetPreviousCloseAggParams{ 21 | Ticker: "X:BTCUSD", 22 | }.WithAdjusted(true) 23 | 24 | // make request 25 | res, err := c.GetPreviousCloseAgg(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/indices/previous-close/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Previous Close 2 | // https://massive.com/docs/indices/get_v2_aggs_ticker__indicesticker__prev 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.GetPreviousCloseAggParams{ 21 | Ticker: "I:SPX", 22 | }.WithAdjusted(true) 23 | 24 | // make request 25 | res, err := c.GetPreviousCloseAgg(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/launchpad/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | massive "github.com/massive-com/client-go/v2/rest" 10 | "github.com/massive-com/client-go/v2/rest/models" 11 | ) 12 | 13 | func main() { 14 | getAggregateBarsLaunchpad() 15 | } 16 | 17 | func getAggregateBarsLaunchpad() { 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | params := &models.ListAggsParams{ 21 | Ticker: "CORN", 22 | Multiplier: 1, 23 | Timespan: models.Day, 24 | From: models.Millis(time.Now().AddDate(0, 0, -7)), 25 | To: models.Millis(time.Now()), 26 | } 27 | 28 | iter := c.ListAggs(context.TODO(), params) 29 | for iter.Next() { 30 | log.Print(iter.Item()) // do something with the current value 31 | } 32 | if iter.Err() != nil { 33 | log.Fatal(iter.Err()) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/options/exchanges/main.go: -------------------------------------------------------------------------------- 1 | // Options - Exchanges 2 | // https://massive.com/docs/options/get_v3_reference_exchanges 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetExchangesParams{}. 22 | WithAssetClass(models.AssetOptions). 23 | WithLocale(models.US) 24 | 25 | // make request 26 | res, err := c.GetExchanges(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/stocks/previous-close/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Previous Close 2 | // https://massive.com/docs/stocks/get_v2_aggs_ticker__stocksticker__prev 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetPreviousCloseAggParams{ 22 | Ticker: "AAPL", 23 | }.WithAdjusted(true) 24 | 25 | // make request 26 | res, err := c.GetPreviousCloseAgg(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/stocks/related-companies/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Related Companies 2 | // https://massive.com/docs/stocks/get_v1_related-companies__ticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetTickerRelatedCompaniesParams{ 22 | Ticker: "AAPL", 23 | } 24 | 25 | // make request 26 | res, err := c.GetTickerRelatedCompanies(context.Background(), ¶ms) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/stocks/technical-indicators-rsi/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Relative Strength Index (RSI) 2 | // https://massive.com/docs/stocks/get_v1_indicators_rsi__stockticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetRSIParams{ 22 | Ticker: "AAPL", 23 | }.WithLimit(1000) 24 | 25 | // make request 26 | res, err := c.GetRSI(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/stocks/technical-indicators-sma/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Simple Moving Average (SMA) 2 | // https://massive.com/docs/stocks/get_v1_indicators_sma__stockticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetSMAParams{ 22 | Ticker: "AAPL", 23 | }.WithLimit(1000) 24 | 25 | // make request 26 | res, err := c.GetSMA(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/stocks/ticker-types/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Ticker Types 2 | // https://massive.com/docs/stocks/get_v3_reference_tickers_types 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetTickerTypesParams{}. 22 | WithAssetClass("stocks"). 23 | WithLocale(models.US) 24 | 25 | // make request 26 | res, err := c.GetTickerTypes(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/options/technical-indicators-rsi/main.go: -------------------------------------------------------------------------------- 1 | // Options - Relative Strength Index (RSI) 2 | // https://massive.com/docs/options/get_v1_indicators_rsi__optionsticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetRSIParams{ 22 | Ticker: "O:SPY241220P00720000", 23 | } 24 | 25 | // make request 26 | res, err := c.GetRSI(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/options/technical-indicators-sma/main.go: -------------------------------------------------------------------------------- 1 | // Options - Simple Moving Average (SMA) 2 | // https://massive.com/docs/options/get_v1_indicators_sma__optionsticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetSMAParams{ 22 | Ticker: "O:SPY241220P00720000", 23 | } 24 | 25 | // make request 26 | res, err := c.GetSMA(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/stocks/technical-indicators-ema/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Exponential Moving Average (EMA) 2 | // https://massive.com/docs/stocks/get_v1_indicators_ema__stockticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetEMAParams{ 22 | Ticker: "AAPL", 23 | }.WithLimit(1000) 24 | 25 | // make request 26 | res, err := c.GetEMA(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/crypto/last-trade-for-a-crypto-pair/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Last Trade for a Crypto Pair 2 | // https://massive.com/docs/crypto/get_v1_last_crypto__from___to 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/trades.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetLastCryptoTradeParams{ 21 | From: "BTC", 22 | To: "USD", 23 | } 24 | 25 | // make request 26 | res, err := c.GetLastCryptoTrade(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | } 34 | -------------------------------------------------------------------------------- /rest/example/options/technical-indicators-ema/main.go: -------------------------------------------------------------------------------- 1 | // Options - Exponential Moving Average (EMA) 2 | // https://massive.com/docs/options/get_v1_indicators_ema__optionsticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetEMAParams{ 22 | Ticker: "O:SPY241220P00720000", 23 | } 24 | 25 | // make request 26 | res, err := c.GetEMA(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/forex/last-quote-for-a-currency-pair/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Last Quote for a Currency Pair 2 | // https://massive.com/docs/forex/get_v1_last_quote_currencies__from___to 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/quotes.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetLastForexQuoteParams{ 21 | From: "AUD", 22 | To: "USD", 23 | } 24 | 25 | // make request 26 | res, err := c.GetLastForexQuote(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | } 34 | -------------------------------------------------------------------------------- /rest/example/options/previous-close/main.go: -------------------------------------------------------------------------------- 1 | // Options - Previous Close 2 | // https://massive.com/docs/options/get_v2_aggs_ticker__optionsticker__prev 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetPreviousCloseAggParams{ 22 | Ticker: "O:SPY251219C00650000", 23 | }.WithAdjusted(true) 24 | 25 | // make request 26 | res, err := c.GetPreviousCloseAgg(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/options/technical-indicators-macd/main.go: -------------------------------------------------------------------------------- 1 | // Options - Moving Average Convergence/Divergence (MACD) 2 | // https://massive.com/docs/options/get_v1_indicators_macd__optionsticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetMACDParams{ 22 | Ticker: "O:SPY241220P00720000", 23 | } 24 | 25 | // make request 26 | res, err := c.GetMACD(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: codeql 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | schedule: 10 | - cron: "24 21 * * 0" 11 | jobs: 12 | analyze: 13 | name: analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | language: ["go"] 23 | steps: 24 | - name: Check out the code 25 | uses: actions/checkout@v4 26 | - name: Initialize CodeQL 27 | uses: github/codeql-action/init@v3 28 | with: 29 | languages: ${{ matrix.language }} 30 | - name: Autobuild 31 | uses: github/codeql-action/autobuild@v3 32 | - name: Perform CodeQL Analysis 33 | uses: github/codeql-action/analyze@v3 34 | -------------------------------------------------------------------------------- /rest/example/forex/conditions/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Conditions 2 | // https://massive.com/docs/forex/get_v3_reference_conditions 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.ListConditionsParams{}. 21 | WithAssetClass(models.AssetFx). 22 | WithLimit(1000) 23 | 24 | // make request 25 | iter := c.ListConditions(context.Background(), params) 26 | 27 | // do something with the result 28 | for iter.Next() { 29 | log.Print(iter.Item()) 30 | } 31 | if iter.Err() != nil { 32 | log.Fatal(iter.Err()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/forex/real-time-currency-conversion/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Real-time Currency Conversion 2 | // https://massive.com/docs/forex/get_v1_conversion__from___to 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/quotes.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetRealTimeCurrencyConversionParams{ 21 | From: "AUD", 22 | To: "USD", 23 | } 24 | 25 | // make request 26 | res, err := c.GetRealTimeCurrencyConversion(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | } 34 | -------------------------------------------------------------------------------- /rest/example/stocks/stock-financials/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Stock Financials vX 2 | // https://massive.com/docs/stocks/get_vx_reference_financials 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/vx.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListStockFinancialsParams{}. 22 | WithTicker("AAPL") 23 | 24 | // make request 25 | iter := c.VX.ListStockFinancials(context.Background(), params) 26 | 27 | // do something with the result 28 | for iter.Next() { 29 | log.Print(iter.Item()) 30 | } 31 | if iter.Err() != nil { 32 | log.Fatal(iter.Err()) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/crypto/conditions/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Conditions 2 | // https://massive.com/docs/crypto/get_v3_reference_conditions 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.ListConditionsParams{}. 21 | WithAssetClass(models.AssetCrypto). 22 | WithLimit(1000) 23 | 24 | // make request 25 | iter := c.ListConditions(context.Background(), params) 26 | 27 | // do something with the result 28 | for iter.Next() { 29 | log.Print(iter.Item()) 30 | } 31 | if iter.Err() != nil { 32 | log.Fatal(iter.Err()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rest/models/conditions_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/massive-com/client-go/v2/rest/models" 7 | ) 8 | 9 | func TestListConditionsParams(t *testing.T) { 10 | assetClass := models.AssetStocks 11 | dataType := models.DataTrade 12 | id := int64(1) 13 | sip := models.CTA 14 | order := models.Asc 15 | limit := 20 16 | sort := models.Name 17 | 18 | expect := models.ListConditionsParams{ 19 | AssetClass: &assetClass, 20 | DataType: &dataType, 21 | ID: &id, 22 | SIP: &sip, 23 | Order: &order, 24 | Limit: &limit, 25 | Sort: &sort, 26 | } 27 | actual := models.ListConditionsParams{}. 28 | WithAssetClass(assetClass). 29 | WithDataType(dataType). 30 | WithID(id). 31 | WithSIP(sip). 32 | WithOrder(order). 33 | WithLimit(limit). 34 | WithSort(sort) 35 | checkParams(t, expect, *actual) 36 | } 37 | -------------------------------------------------------------------------------- /rest/summaries.go: -------------------------------------------------------------------------------- 1 | package massive 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/massive-com/client-go/v2/rest/client" 8 | "github.com/massive-com/client-go/v2/rest/models" 9 | ) 10 | 11 | const ( 12 | GetSummariesPath = "/v1/summaries" 13 | ) 14 | 15 | // SummariesClient defines a REST client for the Massive Snapshot Summary API. 16 | type SummariesClient struct { 17 | client.Client 18 | } 19 | 20 | // GetSummaries retrieves summaries for the ticker list with the given params. 21 | // For more details see https://massive.com/docs/stocks/get_v1_summaries. 22 | func (ic *SummariesClient) GetSummaries(ctx context.Context, params *models.GetSummaryParams, opts ...models.RequestOption) (*models.GetSummaryResponse, error) { 23 | res := &models.GetSummaryResponse{} 24 | err := ic.Call(ctx, http.MethodGet, GetSummariesPath, params, res, opts...) 25 | return res, err 26 | } 27 | -------------------------------------------------------------------------------- /rest/example/crypto/snapshots-ticker-full-book-l2/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Ticker Full Book (L2) 2 | // https://massive.com/docs/crypto/get_v2_snapshot_locale_global_markets_crypto_tickers__ticker__book 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetCryptoFullBookSnapshotParams{ 21 | Ticker: "X:BTCUSD", 22 | } 23 | 24 | // make request 25 | res, err := c.GetCryptoFullBookSnapshot(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | // do something with the result 31 | log.Print(res) 32 | } 33 | -------------------------------------------------------------------------------- /rest/example/crypto/trades/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Trades 2 | // https://massive.com/docs/crypto/get_v3_trades__cryptoticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/trades.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.ListTradesParams{ 21 | Ticker: "X:BTC-USD", 22 | }.WithDay(2023, 4, 13).WithLimit(50000).WithOrder(models.Asc) 23 | 24 | // make request 25 | iter := c.ListTrades(context.Background(), params) 26 | 27 | // do something with the result 28 | for iter.Next() { 29 | log.Print(iter.Item()) 30 | } 31 | if iter.Err() != nil { 32 | log.Fatal(iter.Err()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/forex/snapshots-all-tickers/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Snapshot All Tickers 2 | // https://massive.com/docs/forex/get_v2_snapshot_locale_global_markets_forex_tickers 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetAllTickersSnapshotParams{ 21 | Locale: models.Global, 22 | MarketType: models.Forex, 23 | } 24 | 25 | // make request 26 | res, err := c.GetAllTickersSnapshot(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | } 34 | -------------------------------------------------------------------------------- /rest/example/crypto/snapshots-all-tickers/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Snapshot All Tickers 2 | // https://massive.com/docs/crypto/get_v2_snapshot_locale_global_markets_crypto_tickers 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetAllTickersSnapshotParams{ 21 | Locale: models.Global, 22 | MarketType: models.Crypto, 23 | } 24 | 25 | // make request 26 | res, err := c.GetAllTickersSnapshot(context.Background(), params) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // do something with the result 32 | log.Print(res) 33 | } 34 | -------------------------------------------------------------------------------- /rest/example/options/quotes/main.go: -------------------------------------------------------------------------------- 1 | // Options - Quotes 2 | // https://massive.com/docs/options/get_v3_quotes__optionsticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/quotes.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListQuotesParams{ 22 | Ticker: "O:SPY241220P00720000", 23 | }.WithLimit(50000).WithOrder(models.Asc) 24 | 25 | // make request 26 | iter := c.ListQuotes(context.Background(), params) 27 | 28 | // do something with the result 29 | for iter.Next() { 30 | log.Print(iter.Item()) 31 | } 32 | if iter.Err() != nil { 33 | log.Fatal(iter.Err()) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /rest/example/options/trades/main.go: -------------------------------------------------------------------------------- 1 | // Options - Trades 2 | // https://massive.com/docs/options/get_v3_trades__optionsticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/trades.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListTradesParams{ 22 | Ticker: "O:TSLA210903C00700000", 23 | }.WithLimit(50000).WithOrder(models.Asc) 24 | 25 | // make request 26 | iter := c.ListTrades(context.Background(), params) 27 | 28 | // do something with the result 29 | for iter.Next() { 30 | log.Print(iter.Item()) 31 | } 32 | if iter.Err() != nil { 33 | log.Fatal(iter.Err()) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /rest/example/stocks/trades/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Trades 2 | // https://massive.com/docs/stocks/get_v3_trades__stockticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/trades.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListTradesParams{ 22 | Ticker: "IBIO", 23 | }.WithDay(2023, 2, 1).WithLimit(50000).WithOrder(models.Asc) 24 | 25 | // make request 26 | iter := c.ListTrades(context.Background(), params) 27 | 28 | // do something with the result 29 | for iter.Next() { 30 | log.Print(iter.Item()) 31 | } 32 | if iter.Err() != nil { 33 | log.Fatal(iter.Err()) 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /rest/example/options/ticker-details/main.go: -------------------------------------------------------------------------------- 1 | // Options - Ticker Details v3 2 | // https://massive.com/docs/options/get_v3_reference_tickers__ticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.GetTickerDetailsParams{ 23 | Ticker: "TSLA", 24 | }.WithDate(models.Date(time.Date(2023, 3, 9, 0, 0, 0, 0, time.UTC))) 25 | 26 | // make request 27 | res, err := c.GetTickerDetails(context.Background(), params) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | // do something with the result 33 | log.Print(res) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/stocks/ticker-details/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Ticker Details v3 2 | // https://massive.com/docs/stocks/get_v3_reference_tickers__ticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.GetTickerDetailsParams{ 23 | Ticker: "AAPL", 24 | }.WithDate(models.Date(time.Date(2023, 3, 9, 0, 0, 0, 0, time.UTC))) 25 | 26 | // make request 27 | res, err := c.GetTickerDetails(context.Background(), params) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | // do something with the result 33 | log.Print(res) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/forex/snapshots-ticker/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Snapshot Ticker 2 | // https://massive.com/docs/forex/get_v2_snapshot_locale_global_markets_forex_tickers__ticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetTickerSnapshotParams{ 21 | Ticker: "C:EURUSD", 22 | Locale: models.Global, 23 | MarketType: models.Forex, 24 | } 25 | 26 | // make request 27 | res, err := c.GetTickerSnapshot(context.Background(), params) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | // do something with the result 33 | log.Print(res) 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/stocks/snapshots-all/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Snapshot All Tickers 2 | // https://massive.com/docs/stocks/get_v2_snapshot_locale_us_markets_stocks_tickers 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetAllTickersSnapshotParams{ 22 | Locale: models.US, 23 | MarketType: models.Stocks, 24 | }.WithTickers("AAPL,MSFT") 25 | 26 | // make request 27 | res, err := c.GetAllTickersSnapshot(context.Background(), params) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | // do something with the result 33 | log.Print(res) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/stocks/snapshots-ticker/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Snapshot Ticker 2 | // https://massive.com/docs/stocks/get_v2_snapshot_locale_us_markets_stocks_tickers__stocksticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetTickerSnapshotParams{ 22 | Ticker: "AAPL", 23 | Locale: "us", 24 | MarketType: "stocks", 25 | } 26 | 27 | // make request 28 | res, err := c.GetTickerSnapshot(context.Background(), params) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // do something with the result 34 | log.Print(res) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /rest/example/crypto/snapshots-ticker/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Snapshot Ticker 2 | // https://massive.com/docs/crypto/get_v2_snapshot_locale_global_markets_crypto_tickers__ticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetTickerSnapshotParams{ 22 | Ticker: "X:BTCUSD", 23 | Locale: models.Global, 24 | MarketType: models.Crypto, 25 | } 26 | 27 | // make request 28 | res, err := c.GetTickerSnapshot(context.Background(), params) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // do something with the result 34 | log.Print(res) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /rest/example/stocks/conditions/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Conditions 2 | // https://massive.com/docs/stocks/get_v3_reference_conditions 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListConditionsParams{}. 22 | WithAssetClass(models.AssetStocks). 23 | WithDataType(models.DataTrade). 24 | WithLimit(1000) 25 | 26 | // make request 27 | iter := c.ListConditions(context.Background(), params) 28 | 29 | // do something with the result 30 | for iter.Next() { 31 | log.Print(iter.Item()) 32 | } 33 | if iter.Err() != nil { 34 | log.Fatal(iter.Err()) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /rest/example/indices/daily-open-close/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Daily Open/Close 2 | // https://massive.com/docs/indices/get_v1_open-close__indicesticker___date 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetDailyOpenCloseAggParams{ 22 | Ticker: "I:SPX", 23 | Date: models.Date(time.Date(2023, 4, 11, 0, 0, 0, 0, time.Local)), 24 | }.WithAdjusted(true) 25 | 26 | // make request 27 | res, err := c.GetDailyOpenCloseAgg(context.Background(), params) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | // do something with the result 33 | log.Print(res) 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/options/conditions/main.go: -------------------------------------------------------------------------------- 1 | // Options - Conditions 2 | // https://massive.com/docs/options/get_v3_reference_conditions 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListConditionsParams{}. 22 | WithAssetClass(models.AssetOptions). 23 | WithDataType(models.DataTrade). 24 | WithLimit(1000) 25 | 26 | // make request 27 | iter := c.ListConditions(context.Background(), params) 28 | 29 | // do something with the result 30 | for iter.Next() { 31 | log.Print(iter.Item()) 32 | } 33 | if iter.Err() != nil { 34 | log.Fatal(iter.Err()) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /rest/example/options/snapshots-option-contract/main.go: -------------------------------------------------------------------------------- 1 | // Options - Option Contract 2 | // https://massive.com/docs/options/get_v3_snapshot_options__underlyingasset___optioncontract 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetOptionContractSnapshotParams{ 22 | UnderlyingAsset: "AAPL", 23 | OptionContract: "O:AAPL230616C00150000", 24 | } 25 | 26 | // make request 27 | res, err := c.GetOptionContractSnapshot(context.Background(), params) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | // do something with the result 33 | log.Print(res) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/crypto/daily-open-close/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Daily Open/Close 2 | // https://massive.com/docs/crypto/get_v1_open-close_crypto__from___to___date 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetDailyOpenCloseAggParams{ 22 | Ticker: "X:BTCUSD", 23 | Date: models.Date(time.Date(2023, 4, 11, 0, 0, 0, 0, time.Local)), 24 | }.WithAdjusted(true) 25 | 26 | // make request 27 | res, err := c.GetDailyOpenCloseAgg(context.Background(), params) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | // do something with the result 33 | log.Print(res) 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/forex/technical-indicators-macd/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Moving Average Convergence/Divergence (MACD) 2 | // https://massive.com/docs/forex/get_v1_indicators_macd__fxticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.GetMACDParams{ 21 | Ticker: "C:EURUSD", 22 | }.WithShortWindow(12). 23 | WithLongWindow(26). 24 | WithSignalWindow(9). 25 | WithOrder(models.Desc) 26 | 27 | // make request 28 | res, err := c.GetMACD(context.Background(), params) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // do something with the result 34 | log.Print(res) 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/options/contract/main.go: -------------------------------------------------------------------------------- 1 | // Options - Options Contract 2 | // https://massive.com/docs/options/get_v3_reference_options_contracts__options_ticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.GetOptionsContractParams{ 23 | Ticker: "O:EVRI240119C00002500", 24 | }.WithAsOf(models.Date(time.Date(2022, 5, 16, 0, 0, 0, 0, time.Local))) 25 | 26 | // make request 27 | res, err := c.GetOptionsContract(context.Background(), params) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | // do something with the result 33 | log.Print(res) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/stocks/daily-open-close/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Daily Open/Close 2 | // https://massive.com/docs/stocks/get_v1_open-close__stocksticker___date 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.GetDailyOpenCloseAggParams{ 23 | Ticker: "AAPL", 24 | Date: models.Date(time.Date(2023, 3, 8, 0, 0, 0, 0, time.Local)), 25 | }.WithAdjusted(true) 26 | 27 | // make request 28 | res, err := c.GetDailyOpenCloseAgg(context.Background(), params) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // do something with the result 34 | log.Print(res) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /rest/example/crypto/technical-indicators-macd/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Moving Average Convergence/Divergence (MACD) 2 | // https://massive.com/docs/crypto/get_v1_indicators_macd__cryptoticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.GetMACDParams{ 21 | Ticker: "X:BTCUSD", 22 | }.WithShortWindow(12). 23 | WithLongWindow(26). 24 | WithSignalWindow(9). 25 | WithOrder(models.Desc) 26 | 27 | // make request 28 | res, err := c.GetMACD(context.Background(), params) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // do something with the result 34 | log.Print(res) 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/forex/tickers/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Tickers 2 | // https://massive.com/docs/forex/get_v3_reference_tickers 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.ListTickersParams{}. 21 | WithMarket(models.AssetFx). 22 | WithActive(true). 23 | WithSort(models.TickerSymbol). 24 | WithOrder(models.Asc). 25 | WithLimit(1000) 26 | 27 | // make request 28 | iter := c.ListTickers(context.Background(), params) 29 | 30 | // do something with the result 31 | for iter.Next() { 32 | log.Print(iter.Item()) 33 | } 34 | if iter.Err() != nil { 35 | log.Fatal(iter.Err()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rest/example/indices/snapshots/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Snapshot 2 | // https://massive.com/docs/indices/get_v3_snapshot_indices 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "strings" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // define tickers 21 | tickers := []string{"I:SPX", "I:DJI"} 22 | tickerAnyOf := strings.Join(tickers, ",") 23 | 24 | // set params 25 | params := &models.GetIndicesSnapshotParams{ 26 | TickerAnyOf: &tickerAnyOf, 27 | } 28 | 29 | // make request 30 | res, err := c.GetIndicesSnapshot(context.Background(), params) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | 35 | // do something with the result 36 | log.Print(res) 37 | } 38 | -------------------------------------------------------------------------------- /rest/example/indices/technical-indicators-macd/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Moving Average Convergence/Divergence (MACD) 2 | // https://massive.com/docs/indices/get_v1_indicators_macd__indicesticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.GetMACDParams{ 21 | Ticker: "I:SPX", 22 | }.WithShortWindow(12). 23 | WithLongWindow(26). 24 | WithSignalWindow(9). 25 | WithOrder(models.Desc) 26 | 27 | // make request 28 | res, err := c.GetMACD(context.Background(), params) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // do something with the result 34 | log.Print(res) 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/stocks/snapshots-gainers-losers/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Snapshot Gainers/Losers 2 | // https://massive.com/docs/stocks/get_v2_snapshot_locale_us_markets_stocks__direction 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := &models.GetGainersLosersSnapshotParams{ 22 | Locale: "us", 23 | MarketType: "stocks", 24 | Direction: "gainers", // or "losers" 25 | } 26 | 27 | // make request 28 | res, err := c.GetGainersLosersSnapshot(context.Background(), params) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // do something with the result 34 | log.Print(res) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /rest/example/crypto/tickers/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Tickers 2 | // https://massive.com/docs/crypto/get_v3_reference_tickers 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.ListTickersParams{}. 21 | WithMarket(models.AssetCrypto). 22 | WithActive(true). 23 | WithSort(models.TickerSymbol). 24 | WithOrder(models.Asc). 25 | WithLimit(1000) 26 | 27 | // make request 28 | iter := c.ListTickers(context.Background(), params) 29 | 30 | // do something with the result 31 | for iter.Next() { 32 | log.Print(iter.Item()) 33 | } 34 | if iter.Err() != nil { 35 | log.Fatal(iter.Err()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rest/example/options/contracts/main.go: -------------------------------------------------------------------------------- 1 | // Options - Options Contracts 2 | // https://massive.com/docs/options/get_v3_reference_options_contracts 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListOptionsContractsParams{}. 22 | WithUnderlyingTicker(models.EQ, "HCP"). 23 | WithContractType("call"). 24 | WithLimit(1000) 25 | 26 | // make request 27 | iter := c.ListOptionsContracts(context.Background(), params) 28 | 29 | // do something with the result 30 | for iter.Next() { 31 | log.Print(iter.Item()) 32 | } 33 | if iter.Err() != nil { 34 | log.Fatal(iter.Err()) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /rest/example/forex/snapshots-gainers-losers/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Snapshot Gainers/Losers 2 | // https://massive.com/docs/forex/get_v2_snapshot_locale_global_markets_forex__direction 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetGainersLosersSnapshotParams{ 21 | Locale: models.Global, 22 | MarketType: models.Forex, 23 | Direction: models.Gainers, // or models.Losers 24 | } 25 | 26 | // make request 27 | res, err := c.GetGainersLosersSnapshot(context.Background(), params) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | // do something with the result 33 | log.Print(res) 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/indices/tickers/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Tickers 2 | // https://massive.com/docs/indices/get_v3_reference_tickers 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := models.ListTickersParams{}. 21 | WithMarket(models.AssetIndices). 22 | WithActive(true). 23 | WithSort(models.TickerSymbol). 24 | WithOrder(models.Asc). 25 | WithLimit(1000) 26 | 27 | // make request 28 | iter := c.ListTickers(context.Background(), params) 29 | 30 | // do something with the result 31 | for iter.Next() { 32 | log.Print(iter.Item()) 33 | } 34 | if iter.Err() != nil { 35 | log.Fatal(iter.Err()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rest/example/options/daily-open-close/main.go: -------------------------------------------------------------------------------- 1 | // Options - Daily Open/Close 2 | // https://massive.com/docs/options/get_v1_open-close__optionsticker___date 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.GetDailyOpenCloseAggParams{ 23 | Ticker: "O:SPY251219C00650000", 24 | Date: models.Date(time.Date(2023, 2, 22, 0, 0, 0, 0, time.Local)), 25 | }.WithAdjusted(true) 26 | 27 | // make request 28 | res, err := c.GetDailyOpenCloseAgg(context.Background(), params) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // do something with the result 34 | log.Print(res) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /rest/example/stocks/dividends/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Dividends v3 2 | // https://massive.com/docs/stocks/get_v3_reference_dividends 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListDividendsParams{}. 22 | WithTicker(models.EQ, "MSFT"). 23 | WithDividendType(models.DividendCD). 24 | WithLimit(1000). 25 | WithOrder(models.Desc) 26 | 27 | // make request 28 | iter := c.ListDividends(context.Background(), params) 29 | 30 | // do something with the result 31 | for iter.Next() { 32 | log.Print(iter.Item()) 33 | } 34 | if iter.Err() != nil { 35 | log.Fatal(iter.Err()) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /rest/example/crypto/snapshots-gainers-losers/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Snapshot Gainers/Losers 2 | // https://massive.com/docs/crypto/get_v2_snapshot_locale_global_markets_crypto__direction 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | // init client 17 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 18 | 19 | // set params 20 | params := &models.GetGainersLosersSnapshotParams{ 21 | Locale: models.Global, 22 | MarketType: models.Crypto, 23 | Direction: models.Gainers, // or models.Losers 24 | } 25 | 26 | // make request 27 | res, err := c.GetGainersLosersSnapshot(context.Background(), params) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | // do something with the result 33 | log.Print(res) 34 | } 35 | -------------------------------------------------------------------------------- /rest/example/forex/grouped-daily-bars/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Grouped Daily (Bars) 2 | // https://massive.com/docs/forex/get_v2_aggs_grouped_locale_global_market_fx__date 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetGroupedDailyAggsParams{ 22 | Locale: models.Global, 23 | MarketType: "fx", 24 | Date: models.Date(time.Date(2023, 3, 8, 0, 0, 0, 0, time.Local)), 25 | }.WithAdjusted(true) 26 | 27 | // make request 28 | res, err := c.GetGroupedDailyAggs(context.Background(), params) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // do something with the result 34 | log.Print(res) 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/stocks/technical-indicators-macd/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Moving Average Convergence/Divergence (MACD) 2 | // https://massive.com/docs/stocks/get_v1_indicators_macd__stockticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/indicators.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetMACDParams{ 22 | Ticker: "AAPL", 23 | }.WithShortWindow(12). 24 | WithLongWindow(26). 25 | WithSignalWindow(9). 26 | WithOrder(models.Desc). 27 | WithLimit(1000) 28 | 29 | // make request 30 | res, err := c.GetMACD(context.Background(), params) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | 35 | // do something with the result 36 | log.Print(res) 37 | 38 | } 39 | -------------------------------------------------------------------------------- /rest/example/crypto/grouped-daily-bars/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Grouped Daily (Bars) 2 | // https://massive.com/docs/crypto/get_v2_aggs_grouped_locale_global_market_crypto__date 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.GetGroupedDailyAggsParams{ 22 | Locale: models.Global, 23 | MarketType: models.Crypto, 24 | Date: models.Date(time.Date(2023, 3, 8, 0, 0, 0, 0, time.Local)), 25 | }.WithAdjusted(true) 26 | 27 | // make request 28 | res, err := c.GetGroupedDailyAggs(context.Background(), params) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // do something with the result 34 | log.Print(res) 35 | } 36 | -------------------------------------------------------------------------------- /rest/example/stocks/grouped-daily-bars/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Grouped Daily (Bars) 2 | // https://massive.com/docs/stocks/get_v2_aggs_grouped_locale_us_market_stocks__date 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.GetGroupedDailyAggsParams{ 23 | Locale: models.US, 24 | MarketType: models.Stocks, 25 | Date: models.Date(time.Date(2023, 3, 8, 0, 0, 0, 0, time.Local)), 26 | }.WithAdjusted(true) 27 | 28 | // make request 29 | res, err := c.GetGroupedDailyAggs(context.Background(), params) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // do something with the result 35 | log.Print(res) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /.massive/rest.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "io" 7 | "log" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | ) 12 | 13 | func main() { 14 | if len(os.Args) < 2 { 15 | log.Fatal("expected a URI for fetching the REST spec") 16 | } 17 | uri := os.Args[1] 18 | 19 | var body []byte 20 | if isURL(uri) { 21 | resp, err := http.Get(uri) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | defer resp.Body.Close() 26 | 27 | body, err = io.ReadAll(resp.Body) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | } else { 32 | log.Fatal("expected arg to be a valid URI") 33 | } 34 | 35 | var out bytes.Buffer 36 | err := json.Indent(&out, body, "", " ") 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | err = os.WriteFile(".massive/rest.json", out.Bytes(), 0644) 41 | if err != nil { 42 | log.Fatal(err) 43 | } 44 | } 45 | 46 | func isURL(uri string) bool { 47 | u, err := url.Parse(uri) 48 | return err == nil && u.Scheme != "" && u.Host != "" 49 | } 50 | -------------------------------------------------------------------------------- /rest/example/forex/quotes/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Quotes (BBO) 2 | // https://massive.com/docs/forex/get_v3_quotes__fxticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/quotes.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListQuotesParams{ 22 | Ticker: "C:EUR-USD", 23 | }.WithTimestamp(models.EQ, models.Nanos(time.Date(2023, 4, 13, 0, 0, 0, 0, time.UTC))). 24 | WithSort(models.Timestamp). 25 | WithOrder(models.Asc). 26 | WithLimit(50000) 27 | 28 | // make request 29 | iter := c.ListQuotes(context.Background(), params) 30 | 31 | // do something with the result 32 | for iter.Next() { 33 | log.Print(iter.Item()) 34 | } 35 | if iter.Err() != nil { 36 | log.Fatal(iter.Err()) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /rest/example/options/tickers/main.go: -------------------------------------------------------------------------------- 1 | // Options - Tickers 2 | // https://massive.com/docs/options/get_v3_reference_tickers 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListTickersParams{}. 22 | WithType("CS"). 23 | WithMarket(models.AssetStocks). 24 | WithExchange("XNAS"). 25 | WithActive(true). 26 | WithSort(models.TickerSymbol). 27 | WithOrder(models.Asc). 28 | WithLimit(1000) 29 | 30 | // make request 31 | iter := c.ListTickers(context.Background(), params) 32 | 33 | // do something with the result 34 | for iter.Next() { 35 | log.Print(iter.Item()) 36 | } 37 | if iter.Err() != nil { 38 | log.Fatal(iter.Err()) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /rest/example/stocks/tickers/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Tickers 2 | // https://massive.com/docs/stocks/get_v3_reference_tickers 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListTickersParams{}. 22 | WithType("CS"). 23 | WithMarket(models.AssetStocks). 24 | WithExchange("XNAS"). 25 | WithActive(true). 26 | WithSort(models.TickerSymbol). 27 | WithOrder(models.Asc). 28 | WithLimit(1000) 29 | 30 | // make request 31 | iter := c.ListTickers(context.Background(), params) 32 | 33 | // do something with the result 34 | for iter.Next() { 35 | log.Print(iter.Item()) 36 | } 37 | if iter.Err() != nil { 38 | log.Fatal(iter.Err()) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /rest/models/quotes_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/massive-com/client-go/v2/rest/models" 8 | ) 9 | 10 | func TestListQuotesParams(t *testing.T) { 11 | timestamp := models.Nanos(time.Date(2023, 3, 23, 0, 0, 0, 0, time.UTC)) 12 | order := models.Asc 13 | limit := 100 14 | sort := models.Timestamp 15 | 16 | expect := models.ListQuotesParams{ 17 | TimestampEQ: ×tamp, 18 | TimestampLT: ×tamp, 19 | TimestampLTE: ×tamp, 20 | TimestampGT: ×tamp, 21 | TimestampGTE: ×tamp, 22 | Order: &order, 23 | Limit: &limit, 24 | Sort: &sort, 25 | } 26 | actual := models.ListQuotesParams{}. 27 | WithTimestamp(models.EQ, timestamp). 28 | WithTimestamp(models.LT, timestamp). 29 | WithTimestamp(models.LTE, timestamp). 30 | WithTimestamp(models.GT, timestamp). 31 | WithTimestamp(models.GTE, timestamp). 32 | WithOrder(order). 33 | WithLimit(limit). 34 | WithSort(sort) 35 | 36 | checkParams(t, expect, *actual) 37 | } 38 | -------------------------------------------------------------------------------- /rest/models/trades_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/massive-com/client-go/v2/rest/models" 8 | ) 9 | 10 | func TestListTradesParams(t *testing.T) { 11 | timestamp := models.Nanos(time.Date(2023, 3, 23, 0, 0, 0, 0, time.UTC)) 12 | order := models.Asc 13 | limit := 100 14 | sort := models.Timestamp 15 | 16 | expect := models.ListTradesParams{ 17 | TimestampEQ: ×tamp, 18 | TimestampLT: ×tamp, 19 | TimestampLTE: ×tamp, 20 | TimestampGT: ×tamp, 21 | TimestampGTE: ×tamp, 22 | Order: &order, 23 | Limit: &limit, 24 | Sort: &sort, 25 | } 26 | actual := models.ListTradesParams{}. 27 | WithTimestamp(models.EQ, timestamp). 28 | WithTimestamp(models.LT, timestamp). 29 | WithTimestamp(models.LTE, timestamp). 30 | WithTimestamp(models.GT, timestamp). 31 | WithTimestamp(models.GTE, timestamp). 32 | WithOrder(order). 33 | WithLimit(limit). 34 | WithSort(sort) 35 | 36 | checkParams(t, expect, *actual) 37 | } 38 | -------------------------------------------------------------------------------- /rest/example/stocks/quotes/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Quotes (NBBO) 2 | // https://massive.com/docs/stocks/get_v3_quotes__stockticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/quotes.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.ListQuotesParams{ 23 | Ticker: "AAPL", 24 | }.WithTimestamp(models.EQ, models.Nanos(time.Date(2021, 7, 22, 0, 0, 0, 0, time.UTC))). 25 | WithSort(models.Timestamp). 26 | WithOrder(models.Asc). 27 | WithLimit(50000) 28 | 29 | // make request 30 | iter := c.ListQuotes(context.Background(), params) 31 | 32 | // do something with the result 33 | for iter.Next() { 34 | log.Print(iter.Item()) 35 | } 36 | if iter.Err() != nil { 37 | log.Fatal(iter.Err()) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /rest/example/stocks/ticker-news/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Ticker News 2 | // https://massive.com/docs/stocks/get_v2_reference_news 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.ListTickerNewsParams{}. 23 | WithTicker(models.LTE, "AAPL"). 24 | WithPublishedUTC(models.LT, models.Millis(time.Date(2023, 3, 9, 0, 0, 0, 0, time.UTC))). 25 | WithSort(models.PublishedUTC). 26 | WithOrder(models.Asc). 27 | WithLimit(1000) 28 | 29 | // make request 30 | iter := c.ListTickerNews(context.Background(), params) 31 | 32 | // do something with the result 33 | for iter.Next() { 34 | log.Print(iter.Item()) 35 | } 36 | if iter.Err() != nil { 37 | log.Fatal(iter.Err()) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /rest/models/snapshot_deprecated.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // Deprecated: Please use UniversalSnapshot types instead of AssetSnapshot types. 4 | type ListAssetSnapshotsParams ListUniversalSnapshotsParams 5 | 6 | func (p ListAssetSnapshotsParams) WithTickerAnyOf(q string) *ListAssetSnapshotsParams { 7 | p.TickerAnyOf = &q 8 | return &p 9 | } 10 | 11 | func (p ListAssetSnapshotsParams) WithTicker(q string) *ListAssetSnapshotsParams { 12 | p.Ticker = &q 13 | return &p 14 | } 15 | 16 | func (p ListAssetSnapshotsParams) WithType(q string) *ListAssetSnapshotsParams { 17 | p.Type = &q 18 | return &p 19 | } 20 | 21 | func (p ListAssetSnapshotsParams) WithTickersByComparison(c Comparator, q string) *ListAssetSnapshotsParams { 22 | switch c { 23 | case LT: 24 | p.TickerLT = &q 25 | case LTE: 26 | p.TickerLTE = &q 27 | case GT: 28 | p.TickerGT = &q 29 | case GTE: 30 | p.TickerGTE = &q 31 | } 32 | return &p 33 | } 34 | 35 | // Deprecated: Please use UniversalSnapshot types instead of AssetSnapshot types. 36 | type ListAssetSnapshotsResponse ListUniversalSnapshotsResponse 37 | -------------------------------------------------------------------------------- /rest/example/options/ticker-news/main.go: -------------------------------------------------------------------------------- 1 | // Options - Ticker News 2 | // https://massive.com/docs/options/get_v3_reference_tickers__ticker 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.ListTickerNewsParams{}. 23 | WithTicker(models.LTE, "AAPL"). 24 | WithPublishedUTC(models.LT, models.Millis(time.Date(2023, 3, 9, 0, 0, 0, 0, time.UTC))). 25 | WithSort(models.PublishedUTC). 26 | WithOrder(models.Asc). 27 | WithLimit(1000) 28 | 29 | // make request 30 | iter := c.ListTickerNews(context.Background(), params) 31 | 32 | // do something with the result 33 | for iter.Next() { 34 | log.Print(iter.Item()) 35 | } 36 | if iter.Err() != nil { 37 | log.Fatal(iter.Err()) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /rest/example/stocks/stock-splits/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Stock Splits v3 2 | // https://massive.com/docs/stocks/get_v3_reference_splits 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/reference.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.ListSplitsParams{}. 23 | WithTicker(models.EQ, "AAPL"). 24 | WithExecutionDate(models.EQ, models.Date(time.Date(2020, 8, 31, 0, 0, 0, 0, time.UTC))). 25 | WithReverseSplit(false). 26 | WithSort(models.TickerSymbol). 27 | WithOrder(models.Asc). 28 | WithLimit(1000) 29 | 30 | // make request 31 | iter := c.ListSplits(context.Background(), params) 32 | 33 | // do something with the result 34 | for iter.Next() { 35 | log.Print(iter.Item()) 36 | } 37 | if iter.Err() != nil { 38 | log.Fatal(iter.Err()) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /rest/example/options/snapshots-options-chain/main.go: -------------------------------------------------------------------------------- 1 | // Options - Options Chain 2 | // https://massive.com/docs/options/get_v3_snapshot_options__underlyingasset 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/snapshot.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | 11 | massive "github.com/massive-com/client-go/v2/rest" 12 | "github.com/massive-com/client-go/v2/rest/models" 13 | ) 14 | 15 | func main() { 16 | 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListOptionsChainParams{ 22 | UnderlyingAsset: "SPY", 23 | StrikePriceGTE: new(float64), 24 | StrikePriceLTE: new(float64), 25 | Limit: new(int), 26 | }.WithStrikePrice("gte", 500.00).WithStrikePrice("lte", 600.00).WithLimit(250) 27 | 28 | // make the request 29 | iter := c.ListOptionsChainSnapshot(context.Background(), params) 30 | 31 | // do something with the result 32 | for iter.Next() { 33 | log.Print(iter.Item()) 34 | } 35 | if iter.Err() != nil { 36 | log.Fatal(iter.Err()) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/massive-com/client-go/v2 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/cenkalti/backoff/v4 v4.3.0 7 | github.com/go-playground/form/v4 v4.2.1 8 | github.com/go-playground/validator/v10 v10.23.0 9 | github.com/go-resty/resty/v2 v2.13.1 10 | github.com/gorilla/websocket v1.5.3 11 | github.com/jarcoal/httpmock v1.4.0 12 | github.com/sirupsen/logrus v1.9.3 13 | github.com/stretchr/testify v1.11.1 14 | golang.org/x/exp v0.0.0-20220414153411-bcd21879b8fd 15 | gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 16 | ) 17 | 18 | require ( 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 21 | github.com/go-playground/locales v0.14.1 // indirect 22 | github.com/go-playground/universal-translator v0.18.1 // indirect 23 | github.com/leodido/go-urn v1.4.0 // indirect 24 | github.com/pmezard/go-difflib v1.0.0 // indirect 25 | golang.org/x/crypto v0.23.0 // indirect 26 | golang.org/x/net v0.25.0 // indirect 27 | golang.org/x/sys v0.20.0 // indirect 28 | golang.org/x/text v0.15.0 // indirect 29 | gopkg.in/yaml.v3 v3.0.1 // indirect 30 | ) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Polygon.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /rest/models/markets.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // GetMarketHolidaysResponse is the response returned by the GetMarketHolidays method. 4 | type GetMarketHolidaysResponse []MarketHoliday 5 | 6 | // GetMarketStatusResponse is the response returned by the GetMarketStatus method. 7 | type GetMarketStatusResponse struct { 8 | AfterHours bool `json:"afterHours"` 9 | Currencies map[string]string `json:"currencies,omitempty"` 10 | EarlyHours bool `json:"earlyHours"` 11 | Exchanges map[string]string `json:"exchanges,omitempty"` 12 | IndicesGroups map[string]string `json:"indicesGroups,omitempty"` 13 | Market string `json:"market,omitempty"` 14 | ServerTime Time `json:"serverTime,omitempty"` 15 | } 16 | 17 | // MarketHoliday represents a market holiday for a specific exchange. 18 | type MarketHoliday struct { 19 | Exchange string `json:"exchange,omitempty"` 20 | Name string `json:"name,omitempty"` 21 | Date Date `json:"date,omitempty"` 22 | Status string `json:"status,omitempty"` 23 | Open Time `json:"open,omitempty"` 24 | Close Time `json:"close,omitempty"` 25 | } 26 | -------------------------------------------------------------------------------- /rest/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | massive "github.com/massive-com/client-go/v2/rest" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | func main() { 13 | getAllTickersSnapshot() 14 | listTrades() 15 | } 16 | 17 | func getAllTickersSnapshot() { 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | params := models.GetAllTickersSnapshotParams{ 21 | Locale: models.US, 22 | MarketType: models.Stocks, 23 | }.WithTickers("AAPL,MSFT") 24 | 25 | res, err := c.GetAllTickersSnapshot(context.Background(), params) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | log.Print(res) // do something with the result 30 | } 31 | 32 | func listTrades() { 33 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 34 | 35 | params2 := models.ListTradesParams{ 36 | Ticker: "CORN", 37 | }.WithDay(2021, 7, 22).WithLimit(50000).WithOrder(models.Asc) 38 | 39 | iter := c.ListTrades(context.Background(), params2) 40 | for iter.Next() { 41 | log.Print(iter.Item()) // do something with the result 42 | } 43 | if iter.Err() != nil { 44 | log.Fatal(iter.Err()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rest/example/forex/aggregates-bars/main.go: -------------------------------------------------------------------------------- 1 | // Forex - Aggregates (Bars) 2 | // https://massive.com/docs/forex/get_v2_aggs_ticker__forexticker__range__multiplier___timespan___from___to 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListAggsParams{ 22 | Ticker: "C:EURUSD", 23 | Multiplier: 1, 24 | Timespan: "day", 25 | From: models.Millis(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)), 26 | To: models.Millis(time.Date(2023, 3, 9, 0, 0, 0, 0, time.UTC)), 27 | }.WithOrder(models.Desc).WithLimit(50000).WithAdjusted(true) 28 | 29 | // make request 30 | iter := c.ListAggs(context.Background(), params) 31 | 32 | // do something with the result 33 | for iter.Next() { 34 | log.Print(iter.Item()) 35 | } 36 | if iter.Err() != nil { 37 | log.Fatal(iter.Err()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rest/example/crypto/aggregates-bars/main.go: -------------------------------------------------------------------------------- 1 | // Crypto - Aggregates (Bars) 2 | // https://massive.com/docs/crypto/get_v2_aggs_ticker__cryptoticker__range__multiplier___timespan___from___to 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListAggsParams{ 22 | Ticker: "X:BTCUSD", 23 | Multiplier: 1, 24 | Timespan: "day", 25 | From: models.Millis(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)), 26 | To: models.Millis(time.Date(2023, 3, 9, 0, 0, 0, 0, time.UTC)), 27 | }.WithOrder(models.Desc).WithLimit(50000).WithAdjusted(true) 28 | 29 | // make request 30 | iter := c.ListAggs(context.Background(), params) 31 | 32 | // do something with the result 33 | for iter.Next() { 34 | log.Print(iter.Item()) 35 | } 36 | if iter.Err() != nil { 37 | log.Fatal(iter.Err()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rest/example/indices/aggregates-bars/main.go: -------------------------------------------------------------------------------- 1 | // Indices - Aggregates (Bars) 2 | // https://massive.com/docs/indices/get_v2_aggs_ticker__indicesticker__range__multiplier___timespan___from___to 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | // init client 18 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 19 | 20 | // set params 21 | params := models.ListAggsParams{ 22 | Ticker: "I:SPX", 23 | Multiplier: 1, 24 | Timespan: "day", 25 | From: models.Millis(time.Date(2023, 4, 3, 0, 0, 0, 0, time.UTC)), 26 | To: models.Millis(time.Date(2023, 4, 12, 0, 0, 0, 0, time.UTC)), 27 | }.WithOrder(models.Desc).WithLimit(50000).WithAdjusted(true) 28 | 29 | // make request 30 | iter := c.ListAggs(context.Background(), params) 31 | 32 | // do something with the result 33 | for iter.Next() { 34 | log.Print(iter.Item()) 35 | } 36 | if iter.Err() != nil { 37 | log.Fatal(iter.Err()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /rest/example/stocks/aggregates-bars/main.go: -------------------------------------------------------------------------------- 1 | // Stocks - Aggregates (Bars) 2 | // https://massive.com/docs/stocks/get_v2_aggs_ticker__stocksticker__range__multiplier___timespan___from___to 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.ListAggsParams{ 23 | Ticker: "AAPL", 24 | Multiplier: 1, 25 | Timespan: "day", 26 | From: models.Millis(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)), 27 | To: models.Millis(time.Date(2023, 3, 9, 0, 0, 0, 0, time.UTC)), 28 | }.WithOrder(models.Desc).WithLimit(50000).WithAdjusted(true) 29 | 30 | // make request 31 | iter := c.ListAggs(context.Background(), params) 32 | 33 | // do something with the result 34 | for iter.Next() { 35 | log.Print(iter.Item()) 36 | } 37 | if iter.Err() != nil { 38 | log.Fatal(iter.Err()) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /rest/example/options/aggregates-bars/main.go: -------------------------------------------------------------------------------- 1 | // Options - Aggregates (Bars) 2 | // https://massive.com/docs/options/get_v2_aggs_ticker__optionsticker__range__multiplier___timespan___from___to 3 | // https://github.com/massive-com/client-go/v2/blob/master/rest/aggs.go 4 | package main 5 | 6 | import ( 7 | "context" 8 | "log" 9 | "os" 10 | "time" 11 | 12 | massive "github.com/massive-com/client-go/v2/rest" 13 | "github.com/massive-com/client-go/v2/rest/models" 14 | ) 15 | 16 | func main() { 17 | 18 | // init client 19 | c := massive.New(os.Getenv("MASSIVE_API_KEY")) 20 | 21 | // set params 22 | params := models.ListAggsParams{ 23 | Ticker: "O:SPY251219C00650000", 24 | Multiplier: 1, 25 | Timespan: "day", 26 | From: models.Millis(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)), 27 | To: models.Millis(time.Date(2023, 3, 9, 0, 0, 0, 0, time.UTC)), 28 | }.WithOrder(models.Desc).WithLimit(50000).WithAdjusted(true) 29 | 30 | // make request 31 | iter := c.ListAggs(context.Background(), params) 32 | 33 | // do something with the result 34 | for iter.Next() { 35 | log.Print(iter.Item()) 36 | } 37 | if iter.Err() != nil { 38 | log.Fatal(iter.Err()) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /rest/example/launchpad/README.md: -------------------------------------------------------------------------------- 1 | # Launchpad 2 | 3 | Users of the Launchpad product will need to pass in certain headers in order to make API requests. 4 | 5 | ```golang 6 | params := &models.GetGroupedDailyAggsParams{ 7 | Locale: models.US, 8 | MarketType: models.Stocks, 9 | Date: models.Date(time.Date(2021, 7, 22, 0, 0, 0, 0, time.Local)), 10 | } 11 | 12 | res, err := c.GetGroupedDailyAggs(context.Background(), params, 13 | models.RequiredEdgeHeaders("EDGE_USER_ID", "EDGE_USER_IP_ADDRESS"), 14 | ) 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | log.Print(res) // do something with the result 19 | ``` 20 | 21 | Launchpad users can also provide the optional User Agent value describing their Edge User's origination request. 22 | 23 | ```golang 24 | params := &models.GetGroupedDailyAggsParams{ 25 | Locale: models.US, 26 | MarketType: models.Stocks, 27 | Date: models.Date(time.Date(2021, 7, 22, 0, 0, 0, 0, time.Local)), 28 | } 29 | 30 | res, err := c.GetGroupedDailyAggs(context.Background(), params, 31 | models.RequiredEdgeHeaders("EDGE_USER_ID", "EDGE_USER_IP_ADDRESS"), 32 | models.EdgeUserAgent("EDGE_USER_AGENT"), 33 | ) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | log.Print(res) // do something with the result 38 | ``` -------------------------------------------------------------------------------- /websocket/example/fmv/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | 7 | massivews "github.com/massive-com/client-go/v2/websocket" 8 | "github.com/massive-com/client-go/v2/websocket/models" 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | func main() { 13 | log := logrus.New() 14 | log.SetLevel(logrus.DebugLevel) 15 | log.SetFormatter(&logrus.JSONFormatter{}) 16 | c, err := massivews.New(massivews.Config{ 17 | APIKey: os.Getenv("MASSIVE_API_KEY"), 18 | Feed: massivews.BusinessFeed, 19 | Market: massivews.Stocks, 20 | Log: log, 21 | }) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | defer c.Close() 26 | 27 | // FMV 28 | _ = c.Subscribe(massivews.BusinessFairMarketValue, "*") 29 | 30 | if err := c.Connect(); err != nil { 31 | log.Error(err) 32 | return 33 | } 34 | 35 | sigint := make(chan os.Signal, 1) 36 | signal.Notify(sigint, os.Interrupt) 37 | 38 | for { 39 | select { 40 | case <-sigint: 41 | return 42 | case <-c.Error(): 43 | return 44 | case out, more := <-c.Output(): 45 | if !more { 46 | return 47 | } 48 | switch out.(type) { 49 | case models.FairMarketValue: 50 | log.WithFields(logrus.Fields{"fmv": out}).Info() 51 | 52 | default: 53 | log.WithFields(logrus.Fields{"unknown": out}).Info() 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /rest/models/exchanges.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // GetExchangesParams is the set of parameters for the GetExchanges method. 4 | type GetExchangesParams struct { 5 | // Filter by asset class. 6 | AssetClass *AssetClass `query:"asset_class,omitempty"` 7 | 8 | // Filter by locale. 9 | Locale *MarketLocale `query:"locale,omitempty"` 10 | } 11 | 12 | func (p GetExchangesParams) WithAssetClass(q AssetClass) *GetExchangesParams { 13 | p.AssetClass = &q 14 | return &p 15 | } 16 | 17 | func (p GetExchangesParams) WithLocale(q MarketLocale) *GetExchangesParams { 18 | p.Locale = &q 19 | return &p 20 | } 21 | 22 | // GetExchangesResponse is the response returned by the GetExchanges method. 23 | type GetExchangesResponse struct { 24 | BaseResponse 25 | Results []Exchange `json:"results,omitempty"` 26 | } 27 | 28 | // Exchange contains detailed information on a specified stock Exchange. 29 | type Exchange struct { 30 | Acronym string `json:"acronym,omitempty"` 31 | AssetClass string `json:"asset_class,omitempty"` 32 | ID int64 `json:"id,omitempty"` 33 | Locale string `json:"locale,omitempty"` 34 | MIC string `json:"mic,omitempty"` 35 | Name string `json:"name,omitempty"` 36 | OperatingMIC string `json:"operating_mic,omitempty"` 37 | ParticipantID string `json:"participant_id,omitempty"` 38 | Type string `json:"type,omitempty"` 39 | URL string `json:"url,omitempty"` 40 | } 41 | -------------------------------------------------------------------------------- /rest/models/splits_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/massive-com/client-go/v2/rest/models" 8 | ) 9 | 10 | func TestListSplitsParams(t *testing.T) { 11 | ticker := "A" 12 | date := models.Date(time.Date(2023, 3, 23, 0, 0, 0, 0, time.Local)) 13 | reverseSplit := true 14 | sort := models.TickerSymbol 15 | order := models.Asc 16 | limit := 100 17 | expect := models.ListSplitsParams{ 18 | TickerEQ: &ticker, 19 | TickerLT: &ticker, 20 | TickerLTE: &ticker, 21 | TickerGT: &ticker, 22 | TickerGTE: &ticker, 23 | ExecutionDateEQ: &date, 24 | ExecutionDateLT: &date, 25 | ExecutionDateLTE: &date, 26 | ExecutionDateGT: &date, 27 | ExecutionDateGTE: &date, 28 | ReverseSplit: &reverseSplit, 29 | Sort: &sort, 30 | Order: &order, 31 | Limit: &limit, 32 | } 33 | actual := models.ListSplitsParams{}. 34 | WithTicker(models.EQ, ticker). 35 | WithTicker(models.LT, ticker). 36 | WithTicker(models.LTE, ticker). 37 | WithTicker(models.GT, ticker). 38 | WithTicker(models.GTE, ticker). 39 | WithExecutionDate(models.EQ, date). 40 | WithExecutionDate(models.LT, date). 41 | WithExecutionDate(models.LTE, date). 42 | WithExecutionDate(models.GT, date). 43 | WithExecutionDate(models.GTE, date). 44 | WithReverseSplit(reverseSplit). 45 | WithSort(sort). 46 | WithOrder(order). 47 | WithLimit(limit) 48 | 49 | checkParams(t, expect, *actual) 50 | } 51 | -------------------------------------------------------------------------------- /rest/models/summaries.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "strings" 4 | 5 | type GetSummaryParams struct { 6 | // The ticker list to get summaries for 7 | TickerAnyOf *string `query:"ticker.any_of"` 8 | } 9 | 10 | func (p GetSummaryParams) WithTickerAnyOf(tickers ...string) *GetSummaryParams { 11 | q := strings.Join(tickers, ",") 12 | p.TickerAnyOf = &q 13 | return &p 14 | } 15 | 16 | type GetSummaryResponse struct { 17 | BaseResponse 18 | Results []SummaryResult `json:"results,omitempty"` 19 | } 20 | 21 | type SummaryResult struct { 22 | Price float64 `json:"price,omitempty"` 23 | Name string `json:"name,omitempty"` 24 | Ticker string `json:"ticker,omitempty"` 25 | Branding Branding `json:"branding,omitempty"` 26 | MarketStatus string `json:"market_status,omitempty"` 27 | Type string `json:"type,omitempty"` 28 | Session Session `json:"session,omitempty"` 29 | Options Options `json:"options,omitempty"` 30 | Message string `json:"message,omitempty"` 31 | Error string `json:"error,omitempty"` 32 | } 33 | 34 | //easyjson:json 35 | type Options struct { 36 | ContractType string `json:"contract_type,omitempty"` 37 | ExerciseStyle string `json:"exercise_style,omitempty"` 38 | ExpirationDate Date `json:"expiration_date,omitempty"` 39 | SharesPerContract float64 `json:"shares_per_contract,omitempty"` 40 | StrikePrice float64 `json:"strike_price,omitempty"` 41 | UnderlyingTicker string `json:"underlying_ticker,omitempty"` 42 | } 43 | -------------------------------------------------------------------------------- /rest/massive.go: -------------------------------------------------------------------------------- 1 | // Package massive defines a REST client for the Massive API. 2 | package massive 3 | 4 | import ( 5 | "net/http" 6 | 7 | "github.com/massive-com/client-go/v2/rest/client" 8 | ) 9 | 10 | // Client defines a client to the Massive REST API. 11 | type Client struct { 12 | client.Client 13 | AggsClient 14 | QuotesClient 15 | ReferenceClient 16 | TradesClient 17 | SnapshotClient 18 | IndicatorsClient 19 | SummariesClient 20 | FuturesClient 21 | VX VXClient 22 | } 23 | 24 | // New creates a client for the Massive REST API. 25 | func New(apiKey string) *Client { 26 | return newClient(apiKey, nil) 27 | } 28 | 29 | // NewWithClient creates a client for the Massive REST API using a custom HTTP client. 30 | func NewWithClient(apiKey string, hc *http.Client) *Client { 31 | return newClient(apiKey, hc) 32 | } 33 | 34 | func newClient(apiKey string, hc *http.Client) *Client { 35 | var c client.Client 36 | if hc == nil { 37 | c = client.New(apiKey) 38 | } else { 39 | c = client.NewWithClient(apiKey, hc) 40 | } 41 | 42 | return &Client{ 43 | Client: c, 44 | IndicatorsClient: IndicatorsClient{Client: c}, 45 | SummariesClient: SummariesClient{Client: c}, 46 | AggsClient: AggsClient{Client: c}, 47 | QuotesClient: QuotesClient{Client: c}, 48 | ReferenceClient: ReferenceClient{Client: c}, 49 | TradesClient: TradesClient{Client: c}, 50 | SnapshotClient: SnapshotClient{Client: c}, 51 | FuturesClient: FuturesClient{Client: c}, 52 | VX: VXClient{Client: c}, 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /rest/models/response.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "fmt" 4 | 5 | // BaseResponse has all possible attributes that any response can use. It's intended to be embedded in a domain specific 6 | // response struct. 7 | type BaseResponse struct { 8 | PaginationHooks 9 | 10 | // The status of this request's response. 11 | Status string `json:"status,omitempty"` 12 | 13 | // A request id assigned by the server. 14 | RequestID string `json:"request_id,omitempty"` 15 | 16 | // The total number of results for this request. 17 | Count int `json:"count,omitempty"` 18 | 19 | // A response message for successful requests. 20 | Message string `json:"message,omitempty"` 21 | 22 | // An error message for unsuccessful requests. 23 | ErrorMessage string `json:"error,omitempty"` 24 | } 25 | 26 | // PaginationHooks are links to next and/or previous pages. Embed this struct into an API response if the endpoint 27 | // supports pagination. 28 | type PaginationHooks struct { 29 | // If present, this value can be used to fetch the next page of data. 30 | NextURL string `json:"next_url,omitempty"` 31 | } 32 | 33 | func (p PaginationHooks) NextPage() string { 34 | return p.NextURL 35 | } 36 | 37 | // ErrorResponse represents an API response with an error status code. 38 | type ErrorResponse struct { 39 | BaseResponse 40 | 41 | // An HTTP status code for unsuccessful requests. 42 | StatusCode int 43 | } 44 | 45 | // Error returns the details of an error response. 46 | func (e *ErrorResponse) Error() string { 47 | return fmt.Sprintf("bad status with code '%d': message '%s': request ID '%s': internal status: '%s'", e.StatusCode, e.ErrorMessage, e.RequestID, e.Status) 48 | } 49 | -------------------------------------------------------------------------------- /websocket/example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | 7 | massivews "github.com/massive-com/client-go/v2/websocket" 8 | "github.com/massive-com/client-go/v2/websocket/models" 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | func main() { 13 | log := logrus.New() 14 | log.SetLevel(logrus.DebugLevel) 15 | log.SetFormatter(&logrus.JSONFormatter{}) 16 | c, err := massivews.New(massivews.Config{ 17 | APIKey: os.Getenv("MASSIVE_API_KEY"), 18 | Feed: massivews.RealTime, 19 | Market: massivews.Stocks, 20 | Log: log, 21 | }) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | defer c.Close() 26 | 27 | // aggregates 28 | //_ = c.Subscribe(massivews.StocksMinAggs, "*") 29 | //_ = c.Subscribe(massivews.StocksSecAggs, "*") 30 | 31 | // trades 32 | //_ = c.Subscribe(massivews.StocksTrades, "*") 33 | _ = c.Subscribe(massivews.StocksTrades, "SPY") 34 | 35 | // quotes 36 | //_ = c.Subscribe(massivews.StocksQuotes, "*") 37 | _ = c.Subscribe(massivews.StocksQuotes, "SPY") 38 | 39 | if err := c.Connect(); err != nil { 40 | log.Error(err) 41 | return 42 | } 43 | 44 | sigint := make(chan os.Signal, 1) 45 | signal.Notify(sigint, os.Interrupt) 46 | 47 | for { 48 | select { 49 | case <-sigint: 50 | return 51 | case <-c.Error(): 52 | return 53 | case out, more := <-c.Output(): 54 | if !more { 55 | return 56 | } 57 | switch out.(type) { 58 | case models.EquityAgg: 59 | log.WithFields(logrus.Fields{"aggregate": out}).Info() 60 | case models.EquityTrade: 61 | log.WithFields(logrus.Fields{"trade": out}).Info() 62 | case models.EquityQuote: 63 | log.WithFields(logrus.Fields{"quote": out}).Info() 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yml: -------------------------------------------------------------------------------- 1 | name: code-coverage 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | branches: 7 | - master 8 | pull_request: 9 | permissions: 10 | contents: write 11 | jobs: 12 | test: 13 | name: coverage 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/setup-go@v4 17 | with: 18 | go-version: ${{ matrix.go-version }} 19 | - uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository. 22 | - name: go-test 23 | run: | 24 | go test -v ./... -covermode=count -coverprofile=coverage.out.temp 25 | grep -Ev '(/example|_deprecated.go)' coverage.out.temp > coverage.out 26 | rm coverage.out.temp 27 | go tool cover -func=coverage.out -o=coverage.out 28 | - name: go-coverage-badge # Pass the `coverage.out` output to this action 29 | uses: tj-actions/coverage-badge-go@v2 30 | with: 31 | filename: coverage.out 32 | - name: verify-changed 33 | uses: tj-actions/verify-changed-files@v17 34 | id: verify-changed-files 35 | with: 36 | files: README.md 37 | - name: commit-changes 38 | if: steps.verify-changed-files.outputs.files_changed == 'true' 39 | run: | 40 | git config --local user.email "action@github.com" 41 | git config --local user.name "GitHub Action" 42 | git add README.md 43 | git commit -m "chore: Updated coverage badge." 44 | git fetch origin && git rebase origin/master 45 | - name: push-changes 46 | if: steps.verify-changed-files.outputs.files_changed == 'true' 47 | uses: ad-m/github-push-action@master 48 | with: 49 | github_token: ${{ github.token }} 50 | branch: ${{ github.head_ref }} 51 | -------------------------------------------------------------------------------- /websocket/subscription.go: -------------------------------------------------------------------------------- 1 | package massivews 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | 7 | "github.com/massive-com/client-go/v2/websocket/models" 8 | "golang.org/x/exp/maps" 9 | "golang.org/x/exp/slices" 10 | ) 11 | 12 | // subscriptions stores topic subscriptions for resubscribing after disconnect. 13 | type subscriptions map[Topic]set 14 | type set map[string]struct{} 15 | 16 | // add inserts a set of tickers to the given topic. 17 | func (subs subscriptions) add(topic Topic, tickers ...string) { 18 | _, prefixExists := subs[topic] 19 | if !prefixExists || slices.Contains(tickers, "*") { 20 | subs[topic] = make(set) 21 | } 22 | for _, t := range tickers { 23 | subs[topic][t] = struct{}{} 24 | } 25 | } 26 | 27 | // get retrieves a list of subscription messages based on what has been cached. 28 | func (subs subscriptions) get() []json.RawMessage { 29 | var msgs []json.RawMessage 30 | for topic, tickers := range subs { 31 | msg, err := getSub(models.Subscribe, topic, maps.Keys(tickers)...) 32 | if err != nil { 33 | continue // skip malformed messages 34 | } 35 | msgs = append(msgs, msg) 36 | } 37 | return msgs 38 | } 39 | 40 | // delete removes a set of tickers from the given topic. 41 | func (subs subscriptions) delete(topic Topic, tickers ...string) { 42 | for _, t := range tickers { 43 | delete(subs[topic], t) 44 | } 45 | if len(subs[topic]) == 0 { 46 | delete(subs, topic) 47 | } 48 | } 49 | 50 | // getSub builds a subscription message for a given topic. 51 | func getSub(action models.Action, topic Topic, tickers ...string) (json.RawMessage, error) { 52 | if len(tickers) == 0 { 53 | tickers = []string{"*"} 54 | } 55 | 56 | var params []string 57 | for _, ticker := range tickers { 58 | params = append(params, topic.prefix()+"."+ticker) 59 | } 60 | 61 | msg, err := json.Marshal(&models.ControlMessage{ 62 | Action: action, 63 | Params: strings.Join(params, ","), 64 | }) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | return msg, nil 70 | } 71 | -------------------------------------------------------------------------------- /rest/models/financials_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/massive-com/client-go/v2/rest/models" 8 | ) 9 | 10 | func TestListStockFinancialsParams(t *testing.T) { 11 | ticker := "A" 12 | date := models.Date(time.Date(2023, 3, 23, 0, 0, 0, 0, time.Local)) 13 | timeframe := models.TFAnnual 14 | cik := "0001650729" 15 | name := "Apple" 16 | sic := "3570" 17 | sources := false 18 | order := models.Asc 19 | limit := 100 20 | sort := models.TickerSymbol 21 | 22 | expect := models.ListStockFinancialsParams{ 23 | Ticker: &ticker, 24 | CIK: &cik, 25 | CompanyNameFull: &name, 26 | CompanyNameSearch: &name, 27 | SIC: &sic, 28 | FilingDateEQ: &date, 29 | FilingDateLT: &date, 30 | FilingDateLTE: &date, 31 | FilingDateGT: &date, 32 | FilingDateGTE: &date, 33 | PeriodOfReportDateEQ: &date, 34 | PeriodOfReportDateLT: &date, 35 | PeriodOfReportDateLTE: &date, 36 | PeriodOfReportDateGT: &date, 37 | PeriodOfReportDateGTE: &date, 38 | Timeframe: &timeframe, 39 | IncludeSources: &sources, 40 | Order: &order, 41 | Limit: &limit, 42 | Sort: &sort, 43 | } 44 | actual := models.ListStockFinancialsParams{}. 45 | WithTicker(ticker). 46 | WithCIK(cik). 47 | WithCompanyName(models.Full, name). 48 | WithCompanyName(models.Search, name). 49 | WithSIC(sic). 50 | WithFilingDate(models.EQ, date). 51 | WithFilingDate(models.LT, date). 52 | WithFilingDate(models.LTE, date). 53 | WithFilingDate(models.GT, date). 54 | WithFilingDate(models.GTE, date). 55 | WithPeriodOfReportDate(models.EQ, date). 56 | WithPeriodOfReportDate(models.LT, date). 57 | WithPeriodOfReportDate(models.LTE, date). 58 | WithPeriodOfReportDate(models.GT, date). 59 | WithPeriodOfReportDate(models.GTE, date). 60 | WithTimeframe(timeframe). 61 | WithIncludeSources(sources). 62 | WithOrder(order). 63 | WithLimit(limit). 64 | WithSort(sort) 65 | 66 | checkParams(t, expect, *actual) 67 | } 68 | -------------------------------------------------------------------------------- /rest/models/aggs_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/massive-com/client-go/v2/rest/models" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestListAggsParams(t *testing.T) { 12 | adjusted := true 13 | order := models.Asc 14 | limit := 50 15 | 16 | expect := models.ListAggsParams{ 17 | Adjusted: &adjusted, 18 | Order: &order, 19 | Limit: &limit, 20 | } 21 | actual := models.ListAggsParams{}.WithAdjusted(adjusted).WithOrder(order).WithLimit(limit) 22 | checkParams(t, expect, *actual) 23 | } 24 | 25 | func TestGetAggsParamsMethods(t *testing.T) { 26 | adjusted := true 27 | order := models.Asc 28 | limit := 50 29 | 30 | expect := models.GetAggsParams{ 31 | Adjusted: &adjusted, 32 | Order: &order, 33 | Limit: &limit, 34 | } 35 | actual := models.GetAggsParams{}.WithAdjusted(adjusted).WithOrder(order).WithLimit(limit) 36 | checkParams(t, expect, *actual) 37 | } 38 | 39 | func TestGetGroupedDailyAggsParamsMethods(t *testing.T) { 40 | adjusted := true 41 | includeOTC := true 42 | 43 | expect := models.GetGroupedDailyAggsParams{ 44 | Adjusted: &adjusted, 45 | IncludeOTC: &includeOTC, 46 | } 47 | actual := models.GetGroupedDailyAggsParams{}.WithAdjusted(adjusted).WithIncludeOTC(includeOTC) 48 | checkParams(t, expect, *actual) 49 | } 50 | 51 | func TestGetDailyOpenCloseAggParamsMethods(t *testing.T) { 52 | adjusted := true 53 | 54 | expect := models.GetDailyOpenCloseAggParams{ 55 | Adjusted: &adjusted, 56 | } 57 | actual := models.GetDailyOpenCloseAggParams{}.WithAdjusted(adjusted) 58 | checkParams(t, expect, *actual) 59 | } 60 | 61 | func TestGetPreviousCloseAggParamsMethods(t *testing.T) { 62 | adjusted := true 63 | 64 | expect := models.GetPreviousCloseAggParams{ 65 | Adjusted: &adjusted, 66 | } 67 | actual := models.GetPreviousCloseAggParams{}.WithAdjusted(adjusted) 68 | checkParams(t, expect, *actual) 69 | } 70 | 71 | func checkParams(t *testing.T, expect, actual interface{}) { 72 | for _, field := range reflect.VisibleFields(reflect.TypeOf(actual)) { 73 | assert.NotNil(t, field) 74 | } 75 | assert.Equal(t, expect, actual) 76 | } 77 | -------------------------------------------------------------------------------- /rest/models/types_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | "time" 7 | 8 | "github.com/massive-com/client-go/v2/rest/models" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | type Response struct { 13 | Time models.Time `json:"time"` 14 | } 15 | 16 | func TestUnmarshalTime(t *testing.T) { 17 | expect1, _ := time.Parse("2006-01-02T15:04:05Z", "2022-05-10T22:30:37Z") 18 | expect2, _ := time.Parse("2006-01-02T15:04:05.000Z", "2022-05-10T22:30:37.546Z") 19 | expect3, _ := time.Parse("2006-01-02T15:04:05-07:00", "2022-05-10T22:30:37-08:00") 20 | expect4, _ := time.Parse("2006-01-02T15:04:05.000-0700", "2022-05-10T22:30:37.546-0800") 21 | 22 | tests := map[string]struct { 23 | input []byte // input 24 | expect Response // expected output 25 | err error // expected error 26 | }{ 27 | "2006-01-02T15:04:05Z": { 28 | input: []byte(`{ 29 | "time": "2022-05-10T22:30:37Z" 30 | }`), 31 | expect: Response{ 32 | Time: models.Time(expect1), 33 | }, 34 | }, 35 | "2006-01-02T15:04:05.000Z": { 36 | input: []byte(`{ 37 | "time": "2022-05-10T22:30:37.546Z" 38 | }`), 39 | expect: Response{ 40 | Time: models.Time(expect2), 41 | }, 42 | }, 43 | "2006-01-02T15:04:05-07:00": { 44 | input: []byte(`{ 45 | "time": "2022-05-10T22:30:37-08:00" 46 | }`), 47 | expect: Response{ 48 | Time: models.Time(expect3), 49 | }, 50 | }, 51 | "2006-01-02T15:04:05.000-0700": { 52 | input: []byte(`{ 53 | "time": "2022-05-10T22:30:37.546-0800" 54 | }`), 55 | expect: Response{ 56 | Time: models.Time(expect4), 57 | }, 58 | }, 59 | "unexpected format": { 60 | input: []byte(`{ 61 | "time": "2022-10T22:3" 62 | }`), 63 | err: &time.ParseError{Layout: "2006-01-02T15:04:05Z", Value: "2022-10T22:3", LayoutElem: "-", ValueElem: "T22:3", Message: ""}, 64 | }, 65 | } 66 | 67 | for desc, tc := range tests { 68 | t.Run(desc, func(t *testing.T) { 69 | var res Response 70 | err := json.Unmarshal(tc.input, &res) 71 | assert.Equal(t, tc.err, err) 72 | 73 | expect := time.Time(tc.expect.Time).String() 74 | actual := time.Time(res.Time).String() 75 | if expect != actual { 76 | t.Errorf("%v: expected { %v } got { %v }", desc, expect, actual) 77 | } 78 | }) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /rest/iter/iter.go: -------------------------------------------------------------------------------- 1 | package iter 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/massive-com/client-go/v2/rest/encoder" 7 | ) 8 | 9 | // ListResponse defines an interface that list API responses must implement. 10 | type ListResponse interface { 11 | // NextPage returns a URL for retrieving the next page of list results. 12 | NextPage() string 13 | } 14 | 15 | // Query defines a closure that domain specific iterators must implement. The implementation should 16 | // include a call to the API and should return the API response with a separate slice of the results. 17 | type Query[T any] func(string) (ListResponse, []T, error) 18 | 19 | // Iter defines an iterator type that list methods should return. The contained type should typically 20 | // be a model that's returned in the results of a list method response. 21 | type Iter[T any] struct { 22 | ctx context.Context 23 | query Query[T] 24 | 25 | page ListResponse 26 | item T 27 | results []T 28 | 29 | err error 30 | } 31 | 32 | // NewIter returns a new initialized iterator. This method automatically makes the first query to populate 33 | // the results. List methods should use this helper method when building domain specific iterators. 34 | func NewIter[T any](ctx context.Context, path string, params any, query Query[T]) *Iter[T] { 35 | it := Iter[T]{ 36 | ctx: ctx, 37 | query: query, 38 | } 39 | 40 | uri, err := encoder.New().EncodeParams(path, params) 41 | if err != nil { 42 | it.err = err 43 | return &it 44 | } 45 | 46 | it.page, it.results, it.err = it.query(uri) 47 | return &it 48 | } 49 | 50 | // Next moves the iterator to the next result. 51 | func (it *Iter[T]) Next() bool { 52 | if it.err != nil { 53 | return false 54 | } 55 | 56 | if len(it.results) == 0 && it.page.NextPage() != "" { 57 | it.page, it.results, it.err = it.query(it.page.NextPage()) 58 | } 59 | 60 | if it.err != nil || len(it.results) == 0 { 61 | return false 62 | } 63 | 64 | it.err = it.ctx.Err() 65 | if it.err != nil { 66 | return false 67 | } 68 | 69 | it.item = it.results[0] 70 | it.results = it.results[1:] 71 | return true 72 | } 73 | 74 | // Item returns the result that the iterator is currently pointing to. 75 | func (it *Iter[T]) Item() T { 76 | return it.item 77 | } 78 | 79 | // Err returns any errors that occur during iteration. 80 | func (it *Iter[T]) Err() error { 81 | return it.err 82 | } 83 | -------------------------------------------------------------------------------- /websocket/example/launchpad/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | 7 | massivews "github.com/massive-com/client-go/v2/websocket" 8 | "github.com/massive-com/client-go/v2/websocket/models" 9 | "github.com/sirupsen/logrus" 10 | ) 11 | 12 | func main() { 13 | log := logrus.New() 14 | log.SetLevel(logrus.DebugLevel) 15 | log.SetFormatter(&logrus.JSONFormatter{}) 16 | c, err := massivews.New(massivews.Config{ 17 | APIKey: os.Getenv("MASSIVE_API_KEY"), 18 | Feed: massivews.LaunchpadFeed, 19 | Market: massivews.Stocks, // Change Market to match examples below (Stocks, Options, Forex, or Crypto) 20 | Log: log, 21 | }) 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | defer c.Close() 26 | 27 | // Stocks - make sure you change "Market: massivews.Stocks" above in the massivews.Config 28 | err = c.Subscribe(massivews.StocksLaunchpadMinAggs, "*") 29 | //err = c.Subscribe(massivews.StocksLaunchpadValue, "*") 30 | 31 | // Options - make sure you change "Market: massivews.Options" above in the massivews.Config 32 | //err = c.Subscribe(massivews.OptionsLaunchpadMinAggs, "O:A230616C00070000") 33 | //err = c.Subscribe(massivews.OptionsLaunchpadValue, "O:A230616C00070000") 34 | 35 | // Forex - make sure you change "Market: massivews.Forex" above in the massivews.Config 36 | //err = c.Subscribe(massivews.ForexLaunchpadMinAggs, "*") 37 | //err = c.Subscribe(massivews.ForexLaunchpadValue, "*") 38 | 39 | // Crypto - make sure you change "Market: massivews.Crypto" above in the massivews.Config 40 | //err = c.Subscribe(massivews.CryptoLaunchpadMinAggs, "*") 41 | //err = c.Subscribe(massivews.CryptoLaunchpadValue, "*") 42 | 43 | if err != nil { 44 | log.Error(err) 45 | return 46 | } 47 | 48 | if err := c.Connect(); err != nil { 49 | log.Error(err) 50 | return 51 | } 52 | 53 | sigint := make(chan os.Signal, 1) 54 | signal.Notify(sigint, os.Interrupt) 55 | 56 | for { 57 | select { 58 | case <-sigint: 59 | return 60 | case <-c.Error(): 61 | return 62 | case out, more := <-c.Output(): 63 | if !more { 64 | return 65 | } 66 | 67 | switch out.(type) { 68 | case models.CurrencyAgg: 69 | log.WithFields(logrus.Fields{"currency aggregate": out}).Info() 70 | case models.EquityAgg: 71 | log.WithFields(logrus.Fields{"equity aggregate": out}).Info() 72 | case models.LaunchpadValue: 73 | log.WithFields(logrus.Fields{"value": out}).Info() 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /rest/trades.go: -------------------------------------------------------------------------------- 1 | package massive 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/massive-com/client-go/v2/rest/client" 8 | "github.com/massive-com/client-go/v2/rest/iter" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | const ( 13 | ListTradesPath = "/v3/trades/{ticker}" 14 | GetLastTradePath = "/v2/last/trade/{ticker}" 15 | GetLastCryptoTradePath = "/v1/last/crypto/{from}/{to}" 16 | ) 17 | 18 | // TradesClient defines a REST client for the Massive trades API. 19 | type TradesClient struct { 20 | client.Client 21 | } 22 | 23 | // ListTrades retrieves trades for a specified ticker. For more details see 24 | // https://massive.com/docs/stocks/get_v3_trades__stockticker. 25 | // 26 | // This method returns an iterator that should be used to access the results via this pattern: 27 | // 28 | // iter := c.ListTrades(context.TODO(), params, opts...) 29 | // for iter.Next() { 30 | // log.Print(iter.Item()) // do something with the current value 31 | // } 32 | // if iter.Err() != nil { 33 | // return iter.Err() 34 | // } 35 | func (c *TradesClient) ListTrades(ctx context.Context, params *models.ListTradesParams, options ...models.RequestOption) *iter.Iter[models.Trade] { 36 | return iter.NewIter(ctx, ListTradesPath, params, func(uri string) (iter.ListResponse, []models.Trade, error) { 37 | res := &models.ListTradesResponse{} 38 | err := c.CallURL(ctx, http.MethodGet, uri, res, options...) 39 | return res, res.Results, err 40 | }) 41 | } 42 | 43 | // GetLastTrade retrieves the last trade for a specified ticker. For more details see 44 | // https://massive.com/docs/stocks/get_v2_last_trade__stocksticker. 45 | func (c *TradesClient) GetLastTrade(ctx context.Context, params *models.GetLastTradeParams, options ...models.RequestOption) (*models.GetLastTradeResponse, error) { 46 | res := &models.GetLastTradeResponse{} 47 | err := c.Call(ctx, http.MethodGet, GetLastTradePath, params, res, options...) 48 | return res, err 49 | } 50 | 51 | // GetLastCryptoTrade retrieves the last trade for a crypto pair. For more details see 52 | // https://massive.com/docs/crypto/get_v1_last_crypto__from___to. 53 | func (c *TradesClient) GetLastCryptoTrade(ctx context.Context, params *models.GetLastCryptoTradeParams, options ...models.RequestOption) (*models.GetLastCryptoTradeResponse, error) { 54 | res := &models.GetLastCryptoTradeResponse{} 55 | err := c.Call(ctx, http.MethodGet, GetLastCryptoTradePath, params, res, options...) 56 | return res, err 57 | } 58 | -------------------------------------------------------------------------------- /rest/models/economy.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // ListTreasuryYieldsParams is the set of parameters for the ListTreasuryYields method. 4 | type ListTreasuryYieldsParams struct { 5 | // Calendar date of the yield observation (YYYY-MM-DD). 6 | DateEQ *string `query:"date"` 7 | DateLT *string `query:"date.lt"` 8 | DateLTE *string `query:"date.lte"` 9 | DateGT *string `query:"date.gt"` 10 | DateGTE *string `query:"date.gte"` 11 | 12 | // Sort field used for ordering. Default is date. 13 | Sort *Sort `query:"sort"` 14 | 15 | // Order results based on the sort field. Default is asc. 16 | Order *Order `query:"order"` 17 | 18 | // Limit the number of results returned, default is 100 and max is 50000. 19 | Limit *int `query:"limit"` 20 | } 21 | 22 | func (p ListTreasuryYieldsParams) WithDate(c Comparator, q string) *ListTreasuryYieldsParams { 23 | switch c { 24 | case EQ: 25 | p.DateEQ = &q 26 | case LT: 27 | p.DateLT = &q 28 | case LTE: 29 | p.DateLTE = &q 30 | case GT: 31 | p.DateGT = &q 32 | case GTE: 33 | p.DateGTE = &q 34 | } 35 | return &p 36 | } 37 | 38 | func (p ListTreasuryYieldsParams) WithSort(q Sort) *ListTreasuryYieldsParams { 39 | p.Sort = &q 40 | return &p 41 | } 42 | 43 | func (p ListTreasuryYieldsParams) WithOrder(q Order) *ListTreasuryYieldsParams { 44 | p.Order = &q 45 | return &p 46 | } 47 | 48 | func (p ListTreasuryYieldsParams) WithLimit(q int) *ListTreasuryYieldsParams { 49 | p.Limit = &q 50 | return &p 51 | } 52 | 53 | // ListTreasuryYieldsResponse is the response returned by the ListTreasuryYields method. 54 | type ListTreasuryYieldsResponse struct { 55 | BaseResponse 56 | 57 | // An array of treasury yields that match your query. 58 | Results []TreasuryYield `json:"results,omitempty"` 59 | } 60 | 61 | // TreasuryYield contains treasury yield data for a specific date. 62 | type TreasuryYield struct { 63 | Date string `json:"date,omitempty"` 64 | Yield1Month *float64 `json:"yield_1_month,omitempty"` 65 | Yield3Month *float64 `json:"yield_3_month,omitempty"` 66 | Yield6Month *float64 `json:"yield_6_month,omitempty"` 67 | Yield1Year *float64 `json:"yield_1_year,omitempty"` 68 | Yield2Year *float64 `json:"yield_2_year,omitempty"` 69 | Yield3Year *float64 `json:"yield_3_year,omitempty"` 70 | Yield5Year *float64 `json:"yield_5_year,omitempty"` 71 | Yield7Year *float64 `json:"yield_7_year,omitempty"` 72 | Yield10Year *float64 `json:"yield_10_year,omitempty"` 73 | Yield20Year *float64 `json:"yield_20_year,omitempty"` 74 | Yield30Year *float64 `json:"yield_30_year,omitempty"` 75 | } 76 | -------------------------------------------------------------------------------- /rest/models/contracts_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/massive-com/client-go/v2/rest/models" 8 | ) 9 | 10 | func TestGetOptionsContractParams(t *testing.T) { 11 | ticker := "A" 12 | date := models.Date(time.Date(2023, 3, 23, 0, 0, 0, 0, time.Local)) 13 | expect := models.GetOptionsContractParams{ 14 | Ticker: ticker, 15 | AsOf: &date, 16 | } 17 | actual := models.GetOptionsContractParams{ 18 | Ticker: ticker, 19 | }.WithAsOf(date) 20 | checkParams(t, expect, *actual) 21 | } 22 | 23 | func TestListOptionsContractsParams(t *testing.T) { 24 | date := models.Date(time.Date(2023, 3, 23, 0, 0, 0, 0, time.Local)) 25 | contractType := "call" 26 | expired := true 27 | strike := 100.0 28 | sort := models.TickerSymbol 29 | order := models.Asc 30 | limit := 100 31 | ticker := "A" 32 | expect := models.ListOptionsContractsParams{ 33 | ContractType: &contractType, 34 | UnderlyingTickerEQ: &ticker, 35 | UnderlyingTickerLT: &ticker, 36 | UnderlyingTickerLTE: &ticker, 37 | UnderlyingTickerGT: &ticker, 38 | UnderlyingTickerGTE: &ticker, 39 | ExpirationDateEQ: &date, 40 | ExpirationDateLT: &date, 41 | ExpirationDateLTE: &date, 42 | ExpirationDateGT: &date, 43 | ExpirationDateGTE: &date, 44 | StrikePriceEQ: &strike, 45 | StrikePriceLT: &strike, 46 | StrikePriceLTE: &strike, 47 | StrikePriceGT: &strike, 48 | StrikePriceGTE: &strike, 49 | AsOf: &date, 50 | Expired: &expired, 51 | Sort: &sort, 52 | Order: &order, 53 | Limit: &limit, 54 | } 55 | actual := models.ListOptionsContractsParams{}. 56 | WithContractType(contractType). 57 | WithUnderlyingTicker(models.EQ, ticker). 58 | WithUnderlyingTicker(models.LT, ticker). 59 | WithUnderlyingTicker(models.LTE, ticker). 60 | WithUnderlyingTicker(models.GT, ticker). 61 | WithUnderlyingTicker(models.GTE, ticker). 62 | WithExpirationDate(models.EQ, date). 63 | WithExpirationDate(models.LT, date). 64 | WithExpirationDate(models.LTE, date). 65 | WithExpirationDate(models.GT, date). 66 | WithExpirationDate(models.GTE, date). 67 | WithStrikePrice(models.EQ, strike). 68 | WithStrikePrice(models.LT, strike). 69 | WithStrikePrice(models.LTE, strike). 70 | WithStrikePrice(models.GT, strike). 71 | WithStrikePrice(models.GTE, strike). 72 | WithAsOf(date). 73 | WithExpired(expired). 74 | WithSort(sort). 75 | WithOrder(order). 76 | WithLimit(limit) 77 | 78 | checkParams(t, expect, *actual) 79 | } 80 | -------------------------------------------------------------------------------- /rest/models/request.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | ) 7 | 8 | // RequestOptions are used to configure client calls. 9 | type RequestOptions struct { 10 | // APIKey to pass with the request 11 | APIKey *string 12 | 13 | // Headers to apply to the request 14 | Headers http.Header 15 | 16 | // Query params to apply to the request 17 | QueryParams url.Values 18 | 19 | // Trace enables request tracing 20 | Trace bool 21 | } 22 | 23 | // RequestOption changes the configuration of RequestOptions. 24 | type RequestOption func(o *RequestOptions) 25 | 26 | // APIKey sets an APIKey as an option. 27 | func APIKey(id string) RequestOption { 28 | return func(o *RequestOptions) { 29 | o.APIKey = &id 30 | } 31 | } 32 | 33 | // Header sets a header as an option. 34 | func Header(key, value string) RequestOption { 35 | return func(o *RequestOptions) { 36 | if o.Headers == nil { 37 | o.Headers = make(http.Header) 38 | } 39 | 40 | o.Headers.Add(key, value) 41 | } 42 | } 43 | 44 | // QueryParam sets a query param as an option. 45 | func QueryParam(key, value string) RequestOption { 46 | return func(o *RequestOptions) { 47 | if o.QueryParams == nil { 48 | o.QueryParams = make(url.Values) 49 | } 50 | 51 | o.QueryParams.Add(key, value) 52 | } 53 | } 54 | 55 | // Headers required to use the Launchpad product. 56 | const ( 57 | // HeaderEdgeID is a required Launchpad header. It identifies the Edge User requesting data. 58 | HeaderEdgeID = "X-Massive-Edge-ID" 59 | // HeaderEdgeIPAddress is a required Launchpad header. It denotes the originating IP Address of the Edge User requesting data. 60 | HeaderEdgeIPAddress = "X-Massive-Edge-IP-Address" 61 | // HeaderEdgeUserAgent is an optional Launchpad header. It denotes the originating UserAgent of the Edge User requesting data. 62 | HeaderEdgeUserAgent = "X-Massive-Edge-User-Agent" 63 | ) 64 | 65 | // RequiredEdgeHeaders sets the required headers for the Launchpad product. 66 | func RequiredEdgeHeaders(edgeID, edgeIPAddress string) RequestOption { 67 | return func(o *RequestOptions) { 68 | if o.Headers == nil { 69 | o.Headers = make(http.Header) 70 | } 71 | 72 | o.Headers.Add(HeaderEdgeID, edgeID) 73 | o.Headers.Add(HeaderEdgeIPAddress, edgeIPAddress) 74 | } 75 | } 76 | 77 | // EdgeUserAgent sets the Launchpad optional header denoting the Edge User's UserAgent. 78 | func EdgeUserAgent(userAgent string) RequestOption { 79 | return Header(HeaderEdgeUserAgent, userAgent) 80 | } 81 | 82 | // WithTrace enables or disables request tracing. 83 | func WithTrace(trace bool) RequestOption { 84 | return func(o *RequestOptions) { 85 | o.Trace = trace 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /rest/models/dividends_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/massive-com/client-go/v2/rest/models" 8 | ) 9 | 10 | func TestListDividendsParams(t *testing.T) { 11 | ticker := "A" 12 | date := models.Date(time.Date(2023, 3, 23, 0, 0, 0, 0, time.Local)) 13 | cash := 1.25 14 | frequency := models.Annually 15 | dividendType := models.DividendCD 16 | order := models.Asc 17 | limit := 100 18 | sort := models.TickerSymbol 19 | expect := models.ListDividendsParams{ 20 | TickerEQ: &ticker, 21 | TickerLT: &ticker, 22 | TickerLTE: &ticker, 23 | TickerGT: &ticker, 24 | TickerGTE: &ticker, 25 | ExDividendDateEQ: &date, 26 | ExDividendDateLT: &date, 27 | ExDividendDateLTE: &date, 28 | ExDividendDateGT: &date, 29 | ExDividendDateGTE: &date, 30 | DeclarationDateEQ: &date, 31 | DeclarationDateLT: &date, 32 | DeclarationDateLTE: &date, 33 | DeclarationDateGT: &date, 34 | DeclarationDateGTE: &date, 35 | PayDateEQ: &date, 36 | PayDateLT: &date, 37 | PayDateLTE: &date, 38 | PayDateGT: &date, 39 | PayDateGTE: &date, 40 | CashAmountEQ: &cash, 41 | CashAmountLT: &cash, 42 | CashAmountLTE: &cash, 43 | CashAmountGT: &cash, 44 | CashAmountGTE: &cash, 45 | Frequency: &frequency, 46 | DividendType: ÷ndType, 47 | Order: &order, 48 | Limit: &limit, 49 | Sort: &sort, 50 | } 51 | actual := models.ListDividendsParams{}. 52 | WithTicker(models.EQ, ticker). 53 | WithTicker(models.LT, ticker). 54 | WithTicker(models.LTE, ticker). 55 | WithTicker(models.GT, ticker). 56 | WithTicker(models.GTE, ticker). 57 | WithExDividendDate(models.EQ, date). 58 | WithExDividendDate(models.LT, date). 59 | WithExDividendDate(models.LTE, date). 60 | WithExDividendDate(models.GT, date). 61 | WithExDividendDate(models.GTE, date). 62 | WithDeclarationDate(models.EQ, date). 63 | WithDeclarationDate(models.LT, date). 64 | WithDeclarationDate(models.LTE, date). 65 | WithDeclarationDate(models.GT, date). 66 | WithDeclarationDate(models.GTE, date). 67 | WithPayDate(models.EQ, date). 68 | WithPayDate(models.LT, date). 69 | WithPayDate(models.LTE, date). 70 | WithPayDate(models.GT, date). 71 | WithPayDate(models.GTE, date). 72 | WithCashAmount(models.EQ, cash). 73 | WithCashAmount(models.LT, cash). 74 | WithCashAmount(models.LTE, cash). 75 | WithCashAmount(models.GT, cash). 76 | WithCashAmount(models.GTE, cash). 77 | WithFrequency(models.Annually). 78 | WithDividendType(models.DividendCD). 79 | WithOrder(order). 80 | WithLimit(limit). 81 | WithSort(sort) 82 | 83 | checkParams(t, expect, *actual) 84 | } 85 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := help 2 | TARGET_MAX_CHAR_NUM := 20 3 | VERSION := "" 4 | 5 | GREEN := $(shell tput -Txterm setaf 2) 6 | YELLOW := $(shell tput -Txterm setaf 3) 7 | WHITE := $(shell tput -Txterm setaf 7) 8 | RESET := $(shell tput -Txterm sgr0) 9 | 10 | .PHONY: help fmt lint test rest-example ws-example test-coverage display-coverage release 11 | 12 | ## Show help 13 | help: 14 | @awk '/^[a-zA-Z\-_0-9]+:/ { \ 15 | helpMessage = match(lastLine, /^## (.*)/); \ 16 | if (helpMessage) { \ 17 | helpCommand = substr($$1, 0, index($$1, ":")-1); \ 18 | helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \ 19 | printf " ${YELLOW}%-$(TARGET_MAX_CHAR_NUM)s${RESET} ${GREEN}%s${RESET}\n", helpCommand, helpMessage; \ 20 | } \ 21 | } \ 22 | { lastLine = $$0 }' $(MAKEFILE_LIST) 23 | 24 | ## Format 25 | fmt: 26 | @echo Formatting 27 | @go fmt ./... 28 | 29 | ## Lint with golangci-lint 30 | lint: 31 | @echo Linting 32 | @golangci-lint run --no-config --issues-exit-code=0 --timeout=5m 33 | 34 | ## Run the tests 35 | test: 36 | @echo Running tests 37 | @go test -race -v ./... 38 | 39 | ## Update the REST API spec 40 | rest-spec: 41 | @echo Updating the REST API spec 42 | @go run .massive/rest.go https://api.massive.io/openapi 43 | 44 | ## Update the WebSocket API spec 45 | ws-spec: 46 | @echo Updating the WebSocket API spec 47 | @curl https://api.massive.io/specs/websocket.json > .massive/websocket.json 48 | 49 | ## Run the REST example 50 | rest-example: 51 | @echo Running the REST example 52 | @go run rest/example/main.go 53 | 54 | ## Run the REST example 55 | launchpad-example: 56 | @echo Running the Launchpad example 57 | @go run rest/example/launchpad/main.go 58 | 59 | ## Run the WebSocket example 60 | ws-example: 61 | @echo Running the WebSocket example 62 | @go run websocket/example/main.go 63 | 64 | ## Run the tests with coverage 65 | test-coverage: 66 | @echo Running tests with coverage 67 | @go test ./... -short -coverprofile=cover.out -covermode=atomic -coverpkg=./... 68 | 69 | ## Display test coverage 70 | display-coverage: 71 | @echo Displaying test coverage 72 | @go tool cover -html=cover.out 73 | 74 | ## Publish a new release (usage: make release VERSION={VERSION_TAG}) 75 | release: fmt lint test 76 | @echo Tagging release with version '${VERSION}' 77 | @[[ "${VERSION}" == v* ]] || { echo "Must pass a version tag starting with 'v' (e.g. 'make release VERSION=v0.1.0')" ; exit 1; } 78 | @sed -i.bak '/const clientVersion/s/.*/const clientVersion = "${VERSION}"/' rest/client/client.go && rm rest/client/client.go.bak 79 | @git reset && git add -p rest/client/client.go 80 | @git checkout -b stage-${VERSION} 81 | @git commit -m "update client version tag to '${VERSION}'" 82 | @echo Creating and merging a PR 83 | @gh pr create --fill && gh pr merge --admin --squash --delete-branch 84 | @echo Publishing a release 85 | @gh release create ${VERSION} 86 | -------------------------------------------------------------------------------- /rest/models/splits.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // ListSplitsParams is the set of parameters for the ListSplits method. 4 | type ListSplitsParams struct { 5 | // Return the stock splits that contain this ticker. 6 | TickerEQ *string `query:"ticker"` 7 | TickerLT *string `query:"ticker.lt"` 8 | TickerLTE *string `query:"ticker.lte"` 9 | TickerGT *string `query:"ticker.gt"` 10 | TickerGTE *string `query:"ticker.gte"` 11 | 12 | // Query by execution date with the format YYYY-MM-DD. 13 | ExecutionDateEQ *Date `query:"execution_date"` 14 | ExecutionDateLT *Date `query:"execution_date.lt"` 15 | ExecutionDateLTE *Date `query:"execution_date.lte"` 16 | ExecutionDateGT *Date `query:"execution_date.gt"` 17 | ExecutionDateGTE *Date `query:"execution_date.gte"` 18 | 19 | // Query for reverse stock splits. A split ratio where split_from is greater than split_to represents a reverse 20 | // split. By default this filter is not used. 21 | ReverseSplit *bool `query:"reverse_split"` 22 | 23 | // Order results based on the sort field. 24 | Order *Order `query:"order"` 25 | 26 | // Limit the number of results returned, default is 10 and max is 1000. 27 | Limit *int `query:"limit"` 28 | 29 | // Sort field used for ordering. 30 | Sort *Sort `query:"sort"` 31 | } 32 | 33 | func (p ListSplitsParams) WithTicker(c Comparator, q string) *ListSplitsParams { 34 | switch c { 35 | case EQ: 36 | p.TickerEQ = &q 37 | case LT: 38 | p.TickerLT = &q 39 | case LTE: 40 | p.TickerLTE = &q 41 | case GT: 42 | p.TickerGT = &q 43 | case GTE: 44 | p.TickerGTE = &q 45 | } 46 | return &p 47 | } 48 | 49 | func (p ListSplitsParams) WithExecutionDate(c Comparator, q Date) *ListSplitsParams { 50 | switch c { 51 | case EQ: 52 | p.ExecutionDateEQ = &q 53 | case LT: 54 | p.ExecutionDateLT = &q 55 | case LTE: 56 | p.ExecutionDateLTE = &q 57 | case GT: 58 | p.ExecutionDateGT = &q 59 | case GTE: 60 | p.ExecutionDateGTE = &q 61 | } 62 | return &p 63 | } 64 | 65 | func (p ListSplitsParams) WithReverseSplit(q bool) *ListSplitsParams { 66 | p.ReverseSplit = &q 67 | return &p 68 | } 69 | 70 | func (p ListSplitsParams) WithOrder(q Order) *ListSplitsParams { 71 | p.Order = &q 72 | return &p 73 | } 74 | 75 | func (p ListSplitsParams) WithLimit(q int) *ListSplitsParams { 76 | p.Limit = &q 77 | return &p 78 | } 79 | 80 | func (p ListSplitsParams) WithSort(q Sort) *ListSplitsParams { 81 | p.Sort = &q 82 | return &p 83 | } 84 | 85 | // ListSplitsResponse is the response returned by the ListSplits method. 86 | type ListSplitsResponse struct { 87 | BaseResponse 88 | Results []Split `json:"results,omitempty"` 89 | } 90 | 91 | // Split contains detailed information on a specified stock split. 92 | type Split struct { 93 | ExecutionDate Date `json:"execution_date,omitempty"` 94 | SplitFrom float64 `json:"split_from,omitempty"` 95 | SplitTo float64 `json:"split_to,omitempty"` 96 | Ticker string `json:"ticker,omitempty"` 97 | } 98 | -------------------------------------------------------------------------------- /rest/vx.go: -------------------------------------------------------------------------------- 1 | package massive 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/massive-com/client-go/v2/rest/client" 8 | "github.com/massive-com/client-go/v2/rest/iter" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | const ( 13 | ListFinancialsPath = "/vX/reference/financials" 14 | GetTickerEventsPath = "/vX/reference/tickers/{id}/events" 15 | ListIPOsPath = "/vX/reference/ipos" 16 | ) 17 | 18 | // VXClient defines a REST client for the Massive VX (experimental) API. 19 | type VXClient struct { 20 | client.Client 21 | } 22 | 23 | // ListStockFinancials retrieves historical financial data for a stock ticker. The financials data is extracted from XBRL from company SEC filings 24 | // using the methodology outlined here: http://xbrl.squarespace.com/understanding-sec-xbrl-financi/. 25 | // 26 | // Note: this method utilizes an experimental API and could experience breaking changes or deprecation. 27 | // 28 | // This method returns an iterator that should be used to access the results via this pattern: 29 | // 30 | // iter := c.ListStockFinancials(context.TODO(), params, opts...) 31 | // for iter.Next() { 32 | // log.Print(iter.Item()) // do something with the current value 33 | // } 34 | // if iter.Err() != nil { 35 | // return iter.Err() 36 | // } 37 | func (c *VXClient) ListStockFinancials(ctx context.Context, params *models.ListStockFinancialsParams, options ...models.RequestOption) *iter.Iter[models.StockFinancial] { 38 | return iter.NewIter(ctx, ListFinancialsPath, params, func(uri string) (iter.ListResponse, []models.StockFinancial, error) { 39 | res := &models.ListStockFinancialsResponse{} 40 | err := c.CallURL(ctx, http.MethodGet, uri, res, options...) 41 | return res, res.Results, err 42 | }) 43 | } 44 | 45 | // GetTickerEvents retrieves a timeline of events for the entity associated with the given ticker, CUSIP, or Composite FIGI. 46 | // // For more details see https://massive.com/docs/stocks/get_vx_reference_tickers__id__events. 47 | func (c *VXClient) GetTickerEvents(ctx context.Context, params *models.GetTickerEventsParams, options ...models.RequestOption) (*models.GetTickerEventsResponse, error) { 48 | res := &models.GetTickerEventsResponse{} 49 | err := c.Call(ctx, http.MethodGet, GetTickerEventsPath, params, res, options...) 50 | return res, err 51 | } 52 | 53 | // ListIPOs retrieves detailed information about Initial Public Offerings (IPOs), including both upcoming and historical events. 54 | // Note: this method utilizes an experimental API and could experience breaking changes or deprecation. 55 | func (c *VXClient) ListIPOs(ctx context.Context, params *models.ListIPOsParams, options ...models.RequestOption) *iter.Iter[models.IPOResult] { 56 | return iter.NewIter(ctx, ListIPOsPath, params, func(uri string) (iter.ListResponse, []models.IPOResult, error) { 57 | res := &models.ListIPOsResponse{} 58 | err := c.CallURL(ctx, http.MethodGet, uri, res, options...) 59 | return res, res.Results, err 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /rest/models/snapshot_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/massive-com/client-go/v2/rest/models" 8 | ) 9 | 10 | func TestGetAllTickersSnapshotParams(t *testing.T) { 11 | tickers := "AAPL,GOOL,TSLA" 12 | otc := false 13 | expect := models.GetAllTickersSnapshotParams{ 14 | Tickers: &tickers, 15 | IncludeOTC: &otc, 16 | } 17 | actual := models.GetAllTickersSnapshotParams{}. 18 | WithTickers(tickers). 19 | WithIncludeOTC(otc) 20 | 21 | checkParams(t, expect, *actual) 22 | } 23 | 24 | func TestGetIndicesSnapshotParams(t *testing.T) { 25 | tickers := "AAPL,GOOL,TSLA" 26 | expect := models.GetIndicesSnapshotParams{ 27 | TickerAnyOf: &tickers, 28 | } 29 | actual := models.GetIndicesSnapshotParams{}.WithTickerAnyOf(tickers) 30 | checkParams(t, expect, *actual) 31 | } 32 | 33 | func TestListUniversalSnapshotsParams(t *testing.T) { 34 | ticker := "A" 35 | tickers := "AAPL,GOOL,TSLA" 36 | snapshot := "stocks" 37 | expect := models.ListUniversalSnapshotsParams{ 38 | TickerAnyOf: &tickers, 39 | Ticker: &ticker, 40 | TickerLT: &ticker, 41 | TickerLTE: &ticker, 42 | TickerGT: &ticker, 43 | TickerGTE: &ticker, 44 | Type: &snapshot, 45 | } 46 | actual := models.ListUniversalSnapshotsParams{}. 47 | WithTickerAnyOf(tickers). 48 | WithTicker(ticker). 49 | WithTickersByComparison(models.LT, ticker). 50 | WithTickersByComparison(models.LTE, ticker). 51 | WithTickersByComparison(models.GT, ticker). 52 | WithTickersByComparison(models.GTE, ticker). 53 | WithType(snapshot) 54 | 55 | checkParams(t, expect, *actual) 56 | } 57 | 58 | func TestListOptionsChainParams(t *testing.T) { 59 | date := models.Date(time.Date(2023, 3, 23, 0, 0, 0, 0, time.Local)) 60 | contractType := models.ContractCall 61 | strikePrice := 1.23 62 | limit := 100 63 | sort := models.TickerSymbol 64 | order := models.Asc 65 | expect := models.ListOptionsChainParams{ 66 | StrikePrice: &strikePrice, 67 | StrikePriceLT: &strikePrice, 68 | StrikePriceLTE: &strikePrice, 69 | StrikePriceGT: &strikePrice, 70 | StrikePriceGTE: &strikePrice, 71 | ContractType: &contractType, 72 | ExpirationDateEQ: &date, 73 | ExpirationDateLT: &date, 74 | ExpirationDateLTE: &date, 75 | ExpirationDateGT: &date, 76 | ExpirationDateGTE: &date, 77 | Limit: &limit, 78 | Sort: &sort, 79 | Order: &order, 80 | } 81 | actual := models.ListOptionsChainParams{}. 82 | WithStrikePrice(models.EQ, strikePrice). 83 | WithStrikePrice(models.LT, strikePrice). 84 | WithStrikePrice(models.LTE, strikePrice). 85 | WithStrikePrice(models.GT, strikePrice). 86 | WithStrikePrice(models.GTE, strikePrice). 87 | WithContractType(contractType). 88 | WithExpirationDate(models.EQ, date). 89 | WithExpirationDate(models.LT, date). 90 | WithExpirationDate(models.LTE, date). 91 | WithExpirationDate(models.GT, date). 92 | WithExpirationDate(models.GTE, date). 93 | WithLimit(limit). 94 | WithSort(sort). 95 | WithOrder(order) 96 | 97 | checkParams(t, expect, *actual) 98 | } 99 | -------------------------------------------------------------------------------- /rest/quotes.go: -------------------------------------------------------------------------------- 1 | package massive 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/massive-com/client-go/v2/rest/client" 8 | "github.com/massive-com/client-go/v2/rest/iter" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | const ( 13 | ListQuotesPath = "/v3/quotes/{ticker}" 14 | GetLastQuotePath = "/v2/last/nbbo/{ticker}" 15 | GetLastForexQuotePath = "/v1/last_quote/currencies/{from}/{to}" 16 | GetRealTimeCurrencyConversionPath = "/v1/conversion/{from}/{to}" 17 | ) 18 | 19 | // QuotesClient defines a REST client for the Massive quotes API. 20 | type QuotesClient struct { 21 | client.Client 22 | } 23 | 24 | // ListQuotes retrieves quotes for a specified ticker. For more details see 25 | // https://massive.com/docs/stocks/get_v3_quotes__stockticker. 26 | // 27 | // This method returns an iterator that should be used to access the results via this pattern: 28 | // 29 | // iter := c.ListQuotes(context.TODO(), params, opts...) 30 | // for iter.Next() { 31 | // log.Print(iter.Item()) // do something with the current value 32 | // } 33 | // if iter.Err() != nil { 34 | // return iter.Err() 35 | // } 36 | func (c *QuotesClient) ListQuotes(ctx context.Context, params *models.ListQuotesParams, options ...models.RequestOption) *iter.Iter[models.Quote] { 37 | return iter.NewIter(ctx, ListQuotesPath, params, func(uri string) (iter.ListResponse, []models.Quote, error) { 38 | res := &models.ListQuotesResponse{} 39 | err := c.CallURL(ctx, http.MethodGet, uri, res, options...) 40 | return res, res.Results, err 41 | }) 42 | } 43 | 44 | // GetLastQuote retrieves the last quote (NBBO) for a specified ticker. For more details see 45 | // https://massive.com/docs/stocks/get_v2_last_nbbo__stocksticker. 46 | func (c *QuotesClient) GetLastQuote(ctx context.Context, params *models.GetLastQuoteParams, options ...models.RequestOption) (*models.GetLastQuoteResponse, error) { 47 | res := &models.GetLastQuoteResponse{} 48 | err := c.Call(ctx, http.MethodGet, GetLastQuotePath, params, res, options...) 49 | return res, err 50 | } 51 | 52 | // GetLastForexQuote retrieves the last quote (BBO) for a forex currency pair. For more details see 53 | // https://massive.com/docs/forex/get_v1_last_quote_currencies__from___to. 54 | func (c *QuotesClient) GetLastForexQuote(ctx context.Context, params *models.GetLastForexQuoteParams, options ...models.RequestOption) (*models.GetLastForexQuoteResponse, error) { 55 | res := &models.GetLastForexQuoteResponse{} 56 | err := c.Call(ctx, http.MethodGet, GetLastForexQuotePath, params, res, options...) 57 | return res, err 58 | } 59 | 60 | // GetRealTimeCurrencyConversion retrieves retrieves currency conversion using the latest market conversion rates. Note 61 | // that you can convert in both directions. For more details see 62 | // https://massive.com/docs/forex/get_v1_conversion__from___to. 63 | func (c *QuotesClient) GetRealTimeCurrencyConversion(ctx context.Context, params *models.GetRealTimeCurrencyConversionParams, options ...models.RequestOption) (*models.GetRealTimeCurrencyConversionResponse, error) { 64 | res := &models.GetRealTimeCurrencyConversionResponse{} 65 | err := c.Call(ctx, http.MethodGet, GetRealTimeCurrencyConversionPath, params, res, options...) 66 | return res, err 67 | } 68 | -------------------------------------------------------------------------------- /rest/indicators.go: -------------------------------------------------------------------------------- 1 | package massive 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/massive-com/client-go/v2/rest/client" 8 | "github.com/massive-com/client-go/v2/rest/models" 9 | ) 10 | 11 | const ( 12 | GetSMAPath = "/v1/indicators/sma/{ticker}" 13 | GetEMAPath = "/v1/indicators/ema/{ticker}" 14 | GetMACDPath = "/v1/indicators/macd/{ticker}" 15 | GetRSIPath = "/v1/indicators/rsi/{ticker}" 16 | ) 17 | 18 | // IndicatorsClient defines a REST client for the Massive Technical Indicators API. 19 | type IndicatorsClient struct { 20 | client.Client 21 | } 22 | 23 | // GetSMA retrieves simple moving average data over the given time range with the specified parameters. 24 | // For example, if timespan = 'day' and window = '10', a 10-period simple moving average 25 | // will be calculated using day aggregates for each period. 26 | // For more details see https://massive.com/docs/stocks/get_v1_indicators_sma__stockticker. 27 | func (ic *IndicatorsClient) GetSMA(ctx context.Context, params *models.GetSMAParams, opts ...models.RequestOption) (*models.GetSMAResponse, error) { 28 | res := &models.GetSMAResponse{} 29 | err := ic.Call(ctx, http.MethodGet, GetSMAPath, params, res, opts...) 30 | return res, err 31 | } 32 | 33 | // GetEMA retrieves exponential moving average data over the given time range with the specified parameters. 34 | // For example, if timespan = 'day' and window = '10', a 10-period exponential moving average 35 | // will be calculated using day aggregates for each period. 36 | // For more details see https://massive.com/docs/stocks/get_v1_indicators_ema__stockticker. 37 | func (ic *IndicatorsClient) GetEMA(ctx context.Context, params *models.GetEMAParams, opts ...models.RequestOption) (*models.GetEMAResponse, error) { 38 | res := &models.GetEMAResponse{} 39 | err := ic.Call(ctx, http.MethodGet, GetEMAPath, params, res, opts...) 40 | return res, err 41 | } 42 | 43 | // GetMACD retrieves moving average convergence divergence data over the given time range with the specified parameters. 44 | // For example, if timespan = 'day', short_window = '12', long_window = '26' and signal_window = '9', 45 | // the MACD will be calculated by taking the difference between a 26-period EMA and a 12-period EMA. The signal line values 46 | // will be calculated by taking the 9-day ema of the difference, and the histogram values will be calculated by taking 47 | // the difference between the MACD values and the signal line. 48 | // For more details see https://massive.com/docs/stocks/get_v1_indicators_macd__stockticker. 49 | func (ic *IndicatorsClient) GetMACD(ctx context.Context, params *models.GetMACDParams, opts ...models.RequestOption) (*models.GetMACDResponse, error) { 50 | res := &models.GetMACDResponse{} 51 | err := ic.Call(ctx, http.MethodGet, GetMACDPath, params, res, opts...) 52 | return res, err 53 | } 54 | 55 | // GetRSI retrieves relative strength index data over the given time range with the specified parameters. 56 | // For example, if timespan = 'day' and window = '10', a 10-period relative strength index 57 | // will be calculated using day aggregates for each period. 58 | // For more details see https://massive.com/docs/stocks/get_v1_indicators_rsi__stockticker. 59 | func (ic *IndicatorsClient) GetRSI(ctx context.Context, params *models.GetRSIParams, opts ...models.RequestOption) (*models.GetRSIResponse, error) { 60 | res := &models.GetRSIResponse{} 61 | err := ic.Call(ctx, http.MethodGet, GetRSIPath, params, res, opts...) 62 | return res, err 63 | } 64 | -------------------------------------------------------------------------------- /rest/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/go-resty/resty/v2" 10 | "github.com/massive-com/client-go/v2/rest/encoder" 11 | "github.com/massive-com/client-go/v2/rest/models" 12 | ) 13 | 14 | const clientVersion = "v1.16.0" 15 | 16 | const ( 17 | APIURL = "https://api.massive.com" 18 | DefaultRetryCount = 3 19 | ) 20 | 21 | // Client defines an HTTP client for the Massive REST API. 22 | type Client struct { 23 | HTTP *resty.Client 24 | encoder *encoder.Encoder 25 | } 26 | 27 | // New returns a new client with the specified API key and default settings. 28 | func New(apiKey string) Client { 29 | return newClient(apiKey, nil) 30 | } 31 | 32 | // NewWithClient returns a new client with the specified API key and a custom HTTP client. 33 | func NewWithClient(apiKey string, hc *http.Client) Client { 34 | return newClient(apiKey, hc) 35 | } 36 | 37 | func newClient(apiKey string, hc *http.Client) Client { 38 | var c *resty.Client 39 | if hc == nil { 40 | c = resty.New() 41 | } else { 42 | c = resty.NewWithClient(hc) 43 | } 44 | 45 | c.SetBaseURL(APIURL) 46 | c.SetAuthToken(apiKey) 47 | c.SetRetryCount(DefaultRetryCount) 48 | c.SetTimeout(10 * time.Second) 49 | c.SetHeader("User-Agent", fmt.Sprintf("Massive.com GoClient/%v", clientVersion)) 50 | c.SetHeader("Accept-Encoding", "gzip") 51 | 52 | return Client{ 53 | HTTP: c, 54 | encoder: encoder.New(), 55 | } 56 | } 57 | 58 | // Call makes an API call based on the request params and options. The response is automatically unmarshaled. 59 | func (c *Client) Call(ctx context.Context, method, path string, params, response any, opts ...models.RequestOption) error { 60 | uri, err := c.encoder.EncodeParams(path, params) 61 | if err != nil { 62 | return err 63 | } 64 | return c.CallURL(ctx, method, uri, response, opts...) 65 | } 66 | 67 | // CallURL makes an API call based on a request URI and options. The response is automatically unmarshaled. 68 | func (c *Client) CallURL(ctx context.Context, method, uri string, response any, opts ...models.RequestOption) error { 69 | options := mergeOptions(opts...) 70 | 71 | req := c.HTTP.R().SetContext(ctx) 72 | if options.APIKey != nil { 73 | req.SetAuthToken(*options.APIKey) 74 | } 75 | req.SetQueryParamsFromValues(options.QueryParams) 76 | req.SetHeaderMultiValues(options.Headers) 77 | req.SetResult(response).SetError(&models.ErrorResponse{}) 78 | 79 | res, err := req.Execute(method, uri) 80 | if err != nil { 81 | return fmt.Errorf("failed to execute request: %w", err) 82 | } else if res.IsError() { 83 | errRes := res.Error().(*models.ErrorResponse) 84 | errRes.StatusCode = res.StatusCode() 85 | if errRes.RequestID == "" { 86 | errRes.RequestID = res.Header().Get("X-Request-ID") 87 | } 88 | return errRes 89 | } 90 | 91 | if options.Trace { 92 | fmt.Printf("Request URL: %s\n", uri) 93 | sanitizedHeaders := req.Header 94 | for k := range sanitizedHeaders { 95 | if k == "Authorization" { 96 | sanitizedHeaders[k] = []string{"REDACTED"} 97 | } 98 | } 99 | fmt.Printf("Request Headers: %s\n", sanitizedHeaders) 100 | fmt.Printf("Response Headers: %+v\n", res.Header()) 101 | } 102 | 103 | return nil 104 | } 105 | 106 | func mergeOptions(opts ...models.RequestOption) *models.RequestOptions { 107 | options := &models.RequestOptions{} 108 | for _, o := range opts { 109 | o(options) 110 | } 111 | 112 | return options 113 | } 114 | -------------------------------------------------------------------------------- /rest/models/conditions.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // ListConditionsParams is the set of parameters for the ListConditions method. 4 | type ListConditionsParams struct { 5 | // Filter for conditions within a given asset class. 6 | AssetClass *AssetClass `query:"asset_class,omitempty"` 7 | 8 | // Filter by data type. 9 | DataType *DataType `query:"data_type,omitempty"` 10 | 11 | // Filter for conditions with a given ID. 12 | ID *int64 `query:"id,omitempty"` 13 | 14 | // Filter by SIP. If the condition contains a mapping for that SIP, the condition will be returned. 15 | SIP *SIP `query:"sip,omitempty"` 16 | 17 | // Order results based on the sort field. 18 | Order *Order `query:"order"` 19 | 20 | // Limit the number of results returned, default is 10 and max is 1000. 21 | Limit *int `query:"limit"` 22 | 23 | // Sort field used for ordering. 24 | Sort *Sort `query:"sort"` 25 | } 26 | 27 | func (p ListConditionsParams) WithAssetClass(q AssetClass) *ListConditionsParams { 28 | p.AssetClass = &q 29 | return &p 30 | } 31 | 32 | func (p ListConditionsParams) WithDataType(q DataType) *ListConditionsParams { 33 | p.DataType = &q 34 | return &p 35 | } 36 | 37 | func (p ListConditionsParams) WithID(q int64) *ListConditionsParams { 38 | p.ID = &q 39 | return &p 40 | } 41 | 42 | func (p ListConditionsParams) WithSIP(q SIP) *ListConditionsParams { 43 | p.SIP = &q 44 | return &p 45 | } 46 | 47 | func (p ListConditionsParams) WithOrder(q Order) *ListConditionsParams { 48 | p.Order = &q 49 | return &p 50 | } 51 | 52 | func (p ListConditionsParams) WithLimit(q int) *ListConditionsParams { 53 | p.Limit = &q 54 | return &p 55 | } 56 | 57 | func (p ListConditionsParams) WithSort(q Sort) *ListConditionsParams { 58 | p.Sort = &q 59 | return &p 60 | } 61 | 62 | // ListConditionsResponse is the response returned by the ListConditions method. 63 | type ListConditionsResponse struct { 64 | BaseResponse 65 | Results []Condition `json:"results,omitempty"` 66 | } 67 | 68 | // Condition contains detailed information on a specified condition. 69 | type Condition struct { 70 | Abbreviation string `json:"abbreviation,omitempty"` 71 | AssetClass string `json:"asset_class,omitempty"` 72 | DataTypes []string `json:"data_types,omitempty"` 73 | Description string `json:"description,omitempty"` 74 | Exchange int64 `json:"exchange,omitempty"` 75 | ID int64 `json:"id,omitempty"` 76 | Legacy bool `json:"legacy"` 77 | Name string `json:"name,omitempty"` 78 | SIPMapping SIPMapping `json:"sip_mapping,omitempty"` 79 | Type string `json:"type,omitempty"` 80 | UpdateRules UpdateRules `json:"update_rules,omitempty"` 81 | } 82 | 83 | // SIPMapping maps a condition to symbols for each SIP. 84 | type SIPMapping struct { 85 | CTA string `json:"CTA,omitempty"` 86 | OPRA string `json:"OPRA,omitempty"` 87 | UTP string `json:"UTP,omitempty"` 88 | } 89 | 90 | // UpdateRules is a list of aggregation rules. 91 | type UpdateRules struct { 92 | Consolidated struct { 93 | UpdatesHighLow bool `json:"updates_high_low,omitempty"` 94 | UpdatesOpenClose bool `json:"updates_open_close,omitempty"` 95 | UpdatesVolume bool `json:"updates_volume,omitempty"` 96 | } `json:"consolidated,omitempty"` 97 | MarketCenter struct { 98 | UpdatesHighLow bool `json:"updates_high_low,omitempty"` 99 | UpdatesOpenClose bool `json:"updates_open_close,omitempty"` 100 | UpdatesVolume bool `json:"updates_volume,omitempty"` 101 | } `json:"market_center,omitempty"` 102 | } 103 | -------------------------------------------------------------------------------- /rest/encoder/encoder.go: -------------------------------------------------------------------------------- 1 | package encoder 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "strings" 7 | "time" 8 | 9 | "github.com/go-playground/form/v4" 10 | "github.com/go-playground/validator/v10" 11 | "github.com/massive-com/client-go/v2/rest/models" 12 | ) 13 | 14 | // Encoder defines a path and query param encoder that plays nicely with the Massive REST API. 15 | type Encoder struct { 16 | validate *validator.Validate 17 | pathEncoder *form.Encoder 18 | queryEncoder *form.Encoder 19 | } 20 | 21 | // New returns a new path and query param encoder. 22 | func New() *Encoder { 23 | return &Encoder{ 24 | validate: validator.New(), 25 | pathEncoder: newEncoder("path"), 26 | queryEncoder: newEncoder("query"), 27 | } 28 | } 29 | 30 | // EncodeParams encodes path and query params and returns a valid request URI. 31 | func (e *Encoder) EncodeParams(path string, params any) (string, error) { 32 | if err := e.validateParams(params); err != nil { 33 | return "", err 34 | } 35 | 36 | uri, err := e.encodePath(path, params) 37 | if err != nil { 38 | return "", err 39 | } 40 | 41 | query, err := e.encodeQuery(params) 42 | if err != nil { 43 | return "", err 44 | } else if query != "" { 45 | uri += "?" + query 46 | } 47 | 48 | return uri, nil 49 | } 50 | 51 | func (e *Encoder) validateParams(params any) error { 52 | if err := e.validate.Struct(params); err != nil { 53 | return fmt.Errorf("invalid request params: %w", err) 54 | } 55 | return nil 56 | } 57 | 58 | func (e *Encoder) encodePath(uri string, params any) (string, error) { 59 | val, err := e.pathEncoder.Encode(¶ms) 60 | if err != nil { 61 | return "", fmt.Errorf("error encoding path params: %w", err) 62 | } 63 | 64 | pathParams := map[string]string{} 65 | for k, v := range val { 66 | pathParams[k] = v[0] // only accept the first one for a given key 67 | } 68 | 69 | for k, v := range pathParams { 70 | uri = strings.ReplaceAll(uri, fmt.Sprintf("{%s}", k), url.PathEscape(v)) 71 | } 72 | 73 | return uri, nil 74 | } 75 | 76 | func (e *Encoder) encodeQuery(params any) (string, error) { 77 | query, err := e.queryEncoder.Encode(¶ms) 78 | if err != nil { 79 | return "", fmt.Errorf("error encoding query params: %w", err) 80 | } 81 | return query.Encode(), nil 82 | } 83 | 84 | func newEncoder(tag string) *form.Encoder { 85 | e := form.NewEncoder() 86 | e.SetMode(form.ModeExplicit) 87 | e.SetTagName(tag) 88 | 89 | e.RegisterCustomTypeFunc(func(x any) ([]string, error) { 90 | return []string{fmt.Sprint(time.Time(x.(models.Time)).Format("2006-01-02T15:04:05.000Z"))}, nil 91 | }, models.Time{}) 92 | e.RegisterCustomTypeFunc(func(x any) ([]string, error) { 93 | return []string{fmt.Sprint(time.Time(x.(models.Date)).Format("2006-01-02"))}, nil 94 | }, models.Date{}) 95 | e.RegisterCustomTypeFunc(func(x any) ([]string, error) { 96 | return []string{fmt.Sprint(time.Time(x.(models.Millis)).UnixMilli())}, nil 97 | }, models.Millis{}) 98 | e.RegisterCustomTypeFunc(func(x any) ([]string, error) { 99 | if isDay(time.Time(x.(models.Nanos))) { 100 | // endpoints that have nanosecond timestamp query parameters are expected to 101 | // also work with date strings if a user wants all data from a specific day 102 | return []string{fmt.Sprint(time.Time(x.(models.Nanos)).Format("2006-01-02"))}, nil 103 | } 104 | return []string{fmt.Sprint(time.Time(x.(models.Nanos)).UnixNano())}, nil 105 | }, models.Nanos{}) 106 | 107 | return e 108 | } 109 | 110 | func isDay(t time.Time) bool { 111 | if t.Hour() != 0 || t.Minute() != 0 || t.Second() != 0 || t.Nanosecond() != 0 { 112 | return false 113 | } 114 | return true 115 | } 116 | -------------------------------------------------------------------------------- /rest/models/tickers_test.go: -------------------------------------------------------------------------------- 1 | package models_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/massive-com/client-go/v2/rest/models" 8 | ) 9 | 10 | func TestListTickersParams(t *testing.T) { 11 | ticker := "A" 12 | assetType := "CS" 13 | assetMarket := models.AssetStocks 14 | cik := 1650729 15 | name := "Apple" 16 | date := models.Date(time.Date(2023, 3, 23, 0, 0, 0, 0, time.Local)) 17 | active := true 18 | sort := models.TickerSymbol 19 | order := models.Asc 20 | limit := 100 21 | 22 | expect := models.ListTickersParams{ 23 | TickerEQ: &ticker, 24 | TickerLT: &ticker, 25 | TickerLTE: &ticker, 26 | TickerGT: &ticker, 27 | TickerGTE: &ticker, 28 | Type: &assetType, 29 | Market: &assetMarket, 30 | CIK: &cik, 31 | Search: &name, 32 | Date: &date, 33 | Active: &active, 34 | Sort: &sort, 35 | Order: &order, 36 | Limit: &limit, 37 | } 38 | actual := models.ListTickersParams{}. 39 | WithTicker(models.EQ, ticker). 40 | WithTicker(models.LT, ticker). 41 | WithTicker(models.LTE, ticker). 42 | WithTicker(models.GT, ticker). 43 | WithTicker(models.GTE, ticker). 44 | WithType(assetType). 45 | WithMarket(assetMarket). 46 | WithCIK(cik). 47 | WithSearch(name). 48 | WithDate(date). 49 | WithActive(active). 50 | WithSort(sort). 51 | WithOrder(order). 52 | WithLimit(limit) 53 | 54 | checkParams(t, expect, *actual) 55 | } 56 | 57 | func TestGetTickerDetailsParams(t *testing.T) { 58 | date := models.Date(time.Date(2023, 3, 23, 0, 0, 0, 0, time.Local)) 59 | 60 | expect := models.GetTickerDetailsParams{ 61 | Date: &date, 62 | } 63 | actual := models.GetTickerDetailsParams{}. 64 | WithDate(date) 65 | 66 | checkParams(t, expect, *actual) 67 | } 68 | 69 | func TestListTickerNewsParams(t *testing.T) { 70 | ticker := "A" 71 | date := models.Millis(time.Date(2022, 7, 25, 0, 0, 0, 0, time.UTC)) 72 | sort := models.TickerSymbol 73 | order := models.Asc 74 | limit := 100 75 | 76 | expect := models.ListTickerNewsParams{ 77 | TickerEQ: &ticker, 78 | TickerLT: &ticker, 79 | TickerLTE: &ticker, 80 | TickerGT: &ticker, 81 | TickerGTE: &ticker, 82 | PublishedUtcEQ: &date, 83 | PublishedUtcLT: &date, 84 | PublishedUtcLTE: &date, 85 | PublishedUtcGT: &date, 86 | PublishedUtcGTE: &date, 87 | Sort: &sort, 88 | Order: &order, 89 | Limit: &limit, 90 | } 91 | actual := models.ListTickerNewsParams{}. 92 | WithTicker(models.EQ, ticker). 93 | WithTicker(models.LT, ticker). 94 | WithTicker(models.LTE, ticker). 95 | WithTicker(models.GT, ticker). 96 | WithTicker(models.GTE, ticker). 97 | WithPublishedUTC(models.EQ, date). 98 | WithPublishedUTC(models.LT, date). 99 | WithPublishedUTC(models.LTE, date). 100 | WithPublishedUTC(models.GT, date). 101 | WithPublishedUTC(models.GTE, date). 102 | WithSort(sort). 103 | WithOrder(order). 104 | WithLimit(limit) 105 | 106 | checkParams(t, expect, *actual) 107 | } 108 | 109 | func TestGetTickerTypesParams(t *testing.T) { 110 | assetClass := models.AssetStocks 111 | locale := models.US 112 | 113 | expect := models.GetTickerTypesParams{ 114 | AssetClass: &assetClass, 115 | Locale: &locale, 116 | } 117 | actual := models.GetTickerTypesParams{}. 118 | WithAssetClass(assetClass). 119 | WithLocale(locale) 120 | checkParams(t, expect, *actual) 121 | } 122 | 123 | func TestGetTickerEventsParams(t *testing.T) { 124 | types := "ticker_change" 125 | expect := models.GetTickerEventsParams{ 126 | Types: &types, 127 | } 128 | actual := models.GetTickerEventsParams{}. 129 | WithTypes(types) 130 | 131 | checkParams(t, expect, *actual) 132 | } 133 | -------------------------------------------------------------------------------- /rest/summaries_test.go: -------------------------------------------------------------------------------- 1 | package massive_test 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "testing" 7 | 8 | "github.com/jarcoal/httpmock" 9 | massive "github.com/massive-com/client-go/v2/rest" 10 | "github.com/massive-com/client-go/v2/rest/models" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestGetSummary(t *testing.T) { 15 | c := massive.New("API_KEY") 16 | 17 | httpmock.ActivateNonDefault(c.HTTP.GetClient()) 18 | defer httpmock.DeactivateAndReset() 19 | expectedSummaryResponse := `{ 20 | "request_id": "abc123", 21 | "results": [ 22 | { 23 | "branding": { 24 | "icon_url": "https://api.massive.com/icon.png", 25 | "logo_url": "https://api.massive.com/logo.svg" 26 | }, 27 | "market_status": "closed", 28 | "name": "Norwegian Cruise Lines", 29 | "price": 22.3, 30 | "session": { 31 | "change": -1.05, 32 | "change_percent": -4.67, 33 | "close": 21.4, 34 | "early_trading_change": -0.39, 35 | "early_trading_change_percent": -0.07, 36 | "high": 22.49, 37 | "late_trading_change": 1.2, 38 | "late_trading_change_percent": 3.92, 39 | "low": 21.35, 40 | "open": 22.49, 41 | "previous_close": 22.45, 42 | "volume": 37 43 | }, 44 | "ticker": "NCLH", 45 | "type": "stocks" 46 | }, 47 | { 48 | "market_status": "closed", 49 | "name": "NCLH $5 Call", 50 | "options": { 51 | "contract_type": "call", 52 | "exercise_style": "american", 53 | "expiration_date": "2022-10-14", 54 | "shares_per_contract": 100, 55 | "strike_price": 5, 56 | "underlying_ticker": "NCLH" 57 | }, 58 | "price": 6.6, 59 | "session": { 60 | "change": -0.05, 61 | "change_percent": -1.07, 62 | "close": 6.65, 63 | "early_trading_change": -0.01, 64 | "early_trading_change_percent": -0.03, 65 | "high": 7.01, 66 | "late_trading_change": -0.4, 67 | "late_trading_change_percent": -0.02, 68 | "low": 5.42, 69 | "open": 6.7, 70 | "previous_close": 6.71, 71 | "volume": 67 72 | }, 73 | "ticker": "O:NCLH221014C00005000", 74 | "type": "options" 75 | }, 76 | { 77 | "market_status": "open", 78 | "name": "Euro - United States Dollar", 79 | "price": 0.97989, 80 | "session": { 81 | "change": -0.0001, 82 | "change_percent": -0.67, 83 | "close": 0.97989, 84 | "high": 0.98999, 85 | "low": 0.96689, 86 | "open": 0.97889, 87 | "previous_close": 0.98001 88 | }, 89 | "ticker": "C:EURUSD", 90 | "type": "fx" 91 | }, 92 | { 93 | "branding": { 94 | "icon_url": "https://api.massive.com/icon.png", 95 | "logo_url": "https://api.massive.com/logo.svg" 96 | }, 97 | "market_status": "open", 98 | "name": "Bitcoin - United States Dollar", 99 | "price": 32154.68, 100 | "session": { 101 | "change": -201.23, 102 | "change_percent": -0.77, 103 | "close": 32154.68, 104 | "high": 33124.28, 105 | "low": 28182.88, 106 | "open": 31129.32, 107 | "previous_close": 33362.18 108 | }, 109 | "ticker": "X:BTCUSD", 110 | "type": "crypto" 111 | }, 112 | { 113 | "error": "NOT_FOUND", 114 | "message": "Ticker not found.", 115 | "ticker": "APx" 116 | } 117 | ], 118 | "status": "OK" 119 | }` 120 | expectedGetSummaryUrl := "https://api.massive.com/v1/summaries?ticker.any_of=NCLH%2CO%3ANCLH221014C00005000%2CC%3AEURUSD%2CX%3ABTCUSD%2CAPx" 121 | registerResponder(expectedGetSummaryUrl, expectedSummaryResponse) 122 | tickerAnyOf := []string{"NCLH", "O:NCLH221014C00005000", "C:EURUSD", "X:BTCUSD", "APx"} 123 | 124 | res, err := c.GetSummaries(context.Background(), models.GetSummaryParams{}.WithTickerAnyOf(tickerAnyOf...)) 125 | assert.Nil(t, err) 126 | 127 | var expect models.GetSummaryResponse 128 | err = json.Unmarshal([]byte(expectedSummaryResponse), &expect) 129 | assert.Nil(t, err) 130 | assert.Equal(t, &expect, res) 131 | } 132 | -------------------------------------------------------------------------------- /rest/encoder/encoder_test.go: -------------------------------------------------------------------------------- 1 | package encoder_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/massive-com/client-go/v2/rest/encoder" 8 | "github.com/massive-com/client-go/v2/rest/models" 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestEncodeParams(t *testing.T) { 13 | testPath := "/v1/{num}/{str}" 14 | 15 | type Params struct { 16 | Num float64 `validate:"required" path:"num"` 17 | Str string `validate:"required" path:"str"` 18 | 19 | NumQ *float64 `query:"num"` 20 | StrQ *string `query:"str"` 21 | } 22 | 23 | num := 1.6302 24 | str := "testing" 25 | params := Params{ 26 | Num: 1.6302, 27 | Str: str, 28 | NumQ: &num, 29 | StrQ: &str, 30 | } 31 | 32 | expected := "/v1/1.6302/testing?num=1.6302&str=testing" 33 | actual, err := encoder.New().EncodeParams(testPath, params) 34 | assert.Nil(t, err) 35 | assert.Equal(t, expected, actual) 36 | } 37 | 38 | func TestEncodeTime(t *testing.T) { 39 | testPath := "/v1/{time}" 40 | 41 | type Params struct { 42 | Time models.Time `validate:"required" path:"time"` 43 | TimeQ *models.Time `query:"time"` 44 | } 45 | 46 | ptime := models.Time(time.Date(2021, 7, 22, 0, 0, 0, 0, time.UTC)) 47 | params := Params{ 48 | Time: ptime, 49 | TimeQ: &ptime, 50 | } 51 | 52 | expected := "/v1/2021-07-22T00:00:00.000Z?time=2021-07-22T00%3A00%3A00.000Z" 53 | actual, err := encoder.New().EncodeParams(testPath, params) 54 | assert.Nil(t, err) 55 | assert.Equal(t, expected, actual) 56 | } 57 | 58 | func TestEncodeDate(t *testing.T) { 59 | testPath := "/v1/{date}" 60 | 61 | type Params struct { 62 | Date models.Date `validate:"required" path:"date"` 63 | DateQ *models.Date `query:"date"` 64 | } 65 | 66 | pdate := models.Date(time.Date(2021, 7, 22, 0, 0, 0, 0, time.UTC)) 67 | params := Params{ 68 | Date: pdate, 69 | DateQ: &pdate, 70 | } 71 | 72 | expected := "/v1/2021-07-22?date=2021-07-22" 73 | actual, err := encoder.New().EncodeParams(testPath, params) 74 | assert.Nil(t, err) 75 | assert.Equal(t, expected, actual) 76 | } 77 | 78 | func TestEncodeMillis(t *testing.T) { 79 | testPath := "/v1/{millis}" 80 | 81 | type Params struct { 82 | Millis models.Millis `validate:"required" path:"millis"` 83 | MillisQ *models.Millis `query:"millis"` 84 | } 85 | 86 | pmillis := models.Millis(time.Date(2021, 7, 22, 0, 0, 0, 0, time.UTC)) 87 | params := Params{ 88 | Millis: pmillis, 89 | MillisQ: &pmillis, 90 | } 91 | 92 | expected := "/v1/1626912000000?millis=1626912000000" 93 | actual, err := encoder.New().EncodeParams(testPath, params) 94 | assert.Nil(t, err) 95 | assert.Equal(t, expected, actual) 96 | } 97 | 98 | func TestEncodeNanos(t *testing.T) { 99 | testPath := "/v1/{nanos}" 100 | 101 | type Params struct { 102 | Nanos models.Nanos `validate:"required" path:"nanos"` 103 | NanosQ *models.Nanos `query:"nanos"` 104 | } 105 | 106 | pnanos := models.Nanos(time.Date(2021, 7, 22, 10, 0, 0, 0, time.UTC)) 107 | params := Params{ 108 | Nanos: pnanos, 109 | NanosQ: &pnanos, 110 | } 111 | 112 | expected := "/v1/1626948000000000000?nanos=1626948000000000000" 113 | actual, err := encoder.New().EncodeParams(testPath, params) 114 | assert.Nil(t, err) 115 | assert.Equal(t, expected, actual) 116 | } 117 | 118 | func TestEncodeDayNanos(t *testing.T) { 119 | testPath := "/v1/{nanos}" 120 | 121 | type Params struct { 122 | Nanos models.Nanos `validate:"required" path:"nanos"` 123 | NanosQ *models.Nanos `query:"nanos"` 124 | } 125 | 126 | pnanos := models.Nanos(time.Date(2021, 7, 22, 0, 0, 0, 0, time.UTC)) 127 | params := Params{ 128 | Nanos: pnanos, 129 | NanosQ: &pnanos, 130 | } 131 | 132 | expected := "/v1/2021-07-22?nanos=2021-07-22" 133 | actual, err := encoder.New().EncodeParams(testPath, params) 134 | assert.Nil(t, err) 135 | assert.Equal(t, expected, actual) 136 | } 137 | 138 | func TestValidateError(t *testing.T) { 139 | _, err := encoder.New().EncodeParams("/v1/test", nil) 140 | assert.NotNil(t, err) 141 | } 142 | -------------------------------------------------------------------------------- /websocket/subscription_test.go: -------------------------------------------------------------------------------- 1 | package massivews 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | "github.com/massive-com/client-go/v2/websocket/models" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestSupportsTopic(t *testing.T) { 12 | assert.Equal(t, true, Stocks.supports(StocksMinAggs)) 13 | assert.Equal(t, false, Stocks.supports(stocksMax)) 14 | assert.Equal(t, true, Options.supports(OptionsSecAggs)) 15 | assert.Equal(t, false, Options.supports(StocksMinAggs)) 16 | assert.Equal(t, true, Forex.supports(ForexQuotes)) 17 | assert.Equal(t, false, Forex.supports(OptionsQuotes)) 18 | assert.Equal(t, true, Crypto.supports(CryptoL2Book)) 19 | assert.Equal(t, false, Crypto.supports(cryptoMin)) 20 | assert.Equal(t, true, Market("testMarket").supports(StocksImbalances)) 21 | } 22 | 23 | func TestGet(t *testing.T) { 24 | c, err := New(Config{ 25 | APIKey: "test", 26 | Feed: RealTime, 27 | Market: Stocks, 28 | }) 29 | assert.Nil(t, err) 30 | 31 | // subscribe to a topic 32 | minAggs := models.ControlMessage{Action: models.Subscribe, Params: "AM.AAPL"} 33 | aBytes, _ := json.Marshal(minAggs) 34 | err = c.Subscribe(StocksMinAggs, "AAPL") 35 | assert.Nil(t, err) 36 | msgs := c.subs.get() 37 | assert.Equal(t, []json.RawMessage{aBytes}, msgs) 38 | 39 | // unsubscribe from * 40 | err = c.Unsubscribe(StocksMinAggs, "NFLX", "*") 41 | assert.Nil(t, err) 42 | msgs = c.subs.get() 43 | assert.Equal(t, []json.RawMessage(nil), msgs) 44 | 45 | // subscribe to another topic 46 | err = c.Subscribe(StocksTrades, "SNAP") 47 | assert.Nil(t, err) 48 | trades := models.ControlMessage{Action: models.Subscribe, Params: "T.SNAP"} 49 | tBytes, _ := json.Marshal(trades) 50 | msgs = c.subs.get() 51 | assert.Equal(t, []json.RawMessage{tBytes}, msgs) 52 | 53 | // unsubscribe from * 54 | err = c.Unsubscribe(StocksTrades) 55 | assert.Nil(t, err) 56 | msgs = c.subs.get() 57 | assert.Equal(t, []json.RawMessage(nil), msgs) 58 | } 59 | 60 | func TestGetSub(t *testing.T) { 61 | submsg, err := getSub(models.Subscribe, StocksMinAggs, "AAPL", "GME", "HOOD") 62 | assert.Nil(t, err) 63 | sub, err := json.Marshal(models.ControlMessage{Action: models.Subscribe, Params: "AM.AAPL,AM.GME,AM.HOOD"}) 64 | assert.Nil(t, err) 65 | assert.Equal(t, json.RawMessage(sub), submsg) 66 | 67 | unsubmsg, err := getSub(models.Unsubscribe, StocksSecAggs) 68 | assert.Nil(t, err) 69 | unsub, err := json.Marshal(models.ControlMessage{Action: models.Unsubscribe, Params: "A.*"}) 70 | assert.Nil(t, err) 71 | assert.Equal(t, json.RawMessage(unsub), unsubmsg) 72 | } 73 | 74 | func TestSubscriptions(t *testing.T) { 75 | c, err := New(Config{ 76 | APIKey: "test", 77 | Feed: RealTime, 78 | Market: Stocks, 79 | }) 80 | assert.Nil(t, err) 81 | 82 | err = c.Subscribe(StocksMinAggs, "AAPL", "TSLA") 83 | assert.Nil(t, err) 84 | _, aapl := c.subs[StocksMinAggs]["AAPL"] 85 | assert.True(t, aapl) 86 | _, tsla := c.subs[StocksMinAggs]["TSLA"] 87 | assert.True(t, tsla) 88 | 89 | err = c.Unsubscribe(StocksMinAggs, "AAPL", "NFLX") 90 | assert.Nil(t, err) 91 | _, aapl = c.subs[StocksMinAggs]["AAPL"] 92 | assert.False(t, aapl) 93 | 94 | err = c.Subscribe(StocksMinAggs) 95 | assert.Nil(t, err) 96 | _, all := c.subs[StocksMinAggs]["*"] 97 | assert.True(t, all) 98 | _, tsla = c.subs[StocksMinAggs]["TSLA"] 99 | assert.False(t, tsla) 100 | 101 | err = c.Unsubscribe(StocksMinAggs, "*") 102 | assert.Nil(t, err) 103 | _, all = c.subs[StocksMinAggs]["*"] 104 | assert.False(t, all) 105 | _, trade := c.subs[StocksTrades] 106 | assert.False(t, trade) 107 | 108 | err = c.Unsubscribe(StocksTrades, "RDFN") 109 | assert.Nil(t, err) 110 | _, trade = c.subs[StocksTrades] 111 | assert.False(t, trade) 112 | 113 | err = c.Subscribe(StocksTrades, "FB") 114 | assert.Nil(t, err) 115 | _, fb := c.subs[StocksTrades]["FB"] 116 | assert.True(t, fb) 117 | 118 | err = c.Unsubscribe(StocksTrades) 119 | assert.Nil(t, err) 120 | _, fb = c.subs[StocksTrades]["FB"] 121 | assert.False(t, fb) 122 | } 123 | -------------------------------------------------------------------------------- /rest/aggs.go: -------------------------------------------------------------------------------- 1 | package massive 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/massive-com/client-go/v2/rest/client" 8 | "github.com/massive-com/client-go/v2/rest/iter" 9 | "github.com/massive-com/client-go/v2/rest/models" 10 | ) 11 | 12 | const ( 13 | ListAggsPath = "/v2/aggs/ticker/{ticker}/range/{multiplier}/{timespan}/{from}/{to}" 14 | GetAggsPath = "/v2/aggs/ticker/{ticker}/range/{multiplier}/{timespan}/{from}/{to}" 15 | GetGroupedDailyAggsPath = "/v2/aggs/grouped/locale/{locale}/market/{marketType}/{date}" 16 | GetDailyOpenCloseAggPath = "/v1/open-close/{ticker}/{date}" 17 | GetPreviousCloseAggPath = "/v2/aggs/ticker/{ticker}/prev" 18 | ) 19 | 20 | // AggsClient defines a REST client for the Massive aggs API. 21 | type AggsClient struct { 22 | client.Client 23 | } 24 | 25 | // ListAggs retrieves aggregate bars for a specified ticker over a given date range in custom time window sizes. 26 | // For example, if timespan = 'minute' and multiplier = '5' then 5-minute bars will be returned. 27 | // For more details see https://massive.com/docs/stocks/get_v2_aggs_ticker__stocksticker__range__multiplier___timespan___from___to. 28 | // 29 | // This method returns an iterator that should be used to access the results via this pattern: 30 | // 31 | // iter := c.ListAggs(context.TODO(), params, opts...) 32 | // for iter.Next() { 33 | // log.Print(iter.Item()) // do something with the current value 34 | // } 35 | // if iter.Err() != nil { 36 | // return iter.Err() 37 | // } 38 | func (ac *AggsClient) ListAggs(ctx context.Context, params *models.ListAggsParams, options ...models.RequestOption) *iter.Iter[models.Agg] { 39 | return iter.NewIter(ctx, ListAggsPath, params, func(uri string) (iter.ListResponse, []models.Agg, error) { 40 | res := &models.ListAggsResponse{} 41 | err := ac.CallURL(ctx, http.MethodGet, uri, res, options...) 42 | return res, res.Results, err 43 | }) 44 | } 45 | 46 | // GetAggs retrieves aggregate bars for a specified ticker over a given date range in custom time window sizes. 47 | // For example, if timespan = 'minute' and multiplier = '5' then 5-minute bars will be returned. 48 | // For more details see https://massive.com/docs/stocks/get_v2_aggs_ticker__stocksticker__range__multiplier___timespan___from___to. 49 | // 50 | // Deprecated: This method does not return an iterator and forces users to handle pagination manually. Use 51 | // pkg.go.dev/github.com/massive-com/client-go/v2/rest#AggsClient.ListAggs instead if you want automatic pagination. 52 | func (ac *AggsClient) GetAggs(ctx context.Context, params *models.GetAggsParams, opts ...models.RequestOption) (*models.GetAggsResponse, error) { 53 | res := &models.GetAggsResponse{} 54 | err := ac.Call(ctx, http.MethodGet, GetAggsPath, params, res, opts...) 55 | return res, err 56 | } 57 | 58 | // GetGroupedDailyAggs retrieves the daily open, high, low, and close (OHLC) for the specified market type. 59 | // For more details see https://massive.com/docs/stocks/get_v2_aggs_grouped_locale_us_market_stocks__date. 60 | func (ac *AggsClient) GetGroupedDailyAggs(ctx context.Context, params *models.GetGroupedDailyAggsParams, opts ...models.RequestOption) (*models.GetGroupedDailyAggsResponse, error) { 61 | res := &models.GetGroupedDailyAggsResponse{} 62 | err := ac.Call(ctx, http.MethodGet, GetGroupedDailyAggsPath, params, res, opts...) 63 | return res, err 64 | } 65 | 66 | // GetDailyOpenCloseAgg retrieves the open, close and afterhours prices of a specific symbol on a certain date. 67 | // For more details see https://massive.com/docs/stocks/get_v1_open-close__stocksticker___date. 68 | func (ac *AggsClient) GetDailyOpenCloseAgg(ctx context.Context, params *models.GetDailyOpenCloseAggParams, opts ...models.RequestOption) (*models.GetDailyOpenCloseAggResponse, error) { 69 | res := &models.GetDailyOpenCloseAggResponse{} 70 | err := ac.Call(ctx, http.MethodGet, GetDailyOpenCloseAggPath, params, res, opts...) 71 | return res, err 72 | } 73 | 74 | // GetPreviousCloseAgg retrieves the previous day's open, high, low, and close (OHLC) for the specified ticker. 75 | // For more details see https://massive.com/docs/stocks/get_v2_aggs_ticker__stocksticker__prev. 76 | func (ac *AggsClient) GetPreviousCloseAgg(ctx context.Context, params *models.GetPreviousCloseAggParams, opts ...models.RequestOption) (*models.GetPreviousCloseAggResponse, error) { 77 | res := &models.GetPreviousCloseAggResponse{} 78 | err := ac.Call(ctx, http.MethodGet, GetPreviousCloseAggPath, params, res, opts...) 79 | return res, err 80 | } 81 | -------------------------------------------------------------------------------- /websocket/client_test.go: -------------------------------------------------------------------------------- 1 | package massivews 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "net/http/httptest" 7 | "strings" 8 | "testing" 9 | "time" 10 | 11 | "github.com/gorilla/websocket" 12 | "github.com/massive-com/client-go/v2/websocket/models" 13 | "github.com/sirupsen/logrus" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func connect(w http.ResponseWriter, r *http.Request) { 18 | upgrader := websocket.Upgrader{} 19 | c, err := upgrader.Upgrade(w, r, nil) 20 | if err != nil { 21 | return 22 | } 23 | defer c.Close() 24 | 25 | for { 26 | mt, msg, err := c.ReadMessage() 27 | if err != nil { 28 | return 29 | } 30 | 31 | var cm models.ControlMessage 32 | _ = json.Unmarshal(msg, &cm) 33 | if cm.Action == "auth" && cm.Params == "good" { 34 | res := []models.ControlMessage{{EventType: models.EventType{EventType: "status"}, Status: "auth_success"}} 35 | data, _ := json.Marshal(res) 36 | err = c.WriteMessage(mt, data) 37 | } else { 38 | res := []models.ControlMessage{{EventType: models.EventType{EventType: "status"}, Status: "auth_failed"}} 39 | data, _ := json.Marshal(res) 40 | err = c.WriteMessage(mt, data) 41 | } 42 | if err != nil { 43 | return 44 | } 45 | } 46 | } 47 | 48 | func TestNew(t *testing.T) { 49 | // successful creation 50 | c, err := New(Config{ 51 | APIKey: "test", 52 | Feed: PolyFeed, 53 | Market: Options, 54 | }) 55 | assert.NotNil(t, c) 56 | assert.Nil(t, err) 57 | assert.Equal(t, "wss://polyfeed.massive.com/options", c.url) 58 | assert.Equal(t, &nopLogger{}, c.log) 59 | 60 | // empty config 61 | c, err = New(Config{}) 62 | assert.Nil(t, c) 63 | assert.NotNil(t, err) 64 | } 65 | 66 | func TestConnectAuthSuccess(t *testing.T) { 67 | s := httptest.NewServer(http.HandlerFunc(connect)) 68 | defer s.Close() 69 | 70 | log := logrus.New() 71 | log.SetLevel(logrus.DebugLevel) 72 | u := "ws" + strings.TrimPrefix(s.URL, "http") 73 | var retries uint64 = 0 74 | c, err := New(Config{ 75 | APIKey: "good", 76 | Feed: Feed(u), 77 | Market: Market(""), 78 | Log: log, 79 | MaxRetries: &retries, 80 | }) 81 | assert.NotNil(t, c) 82 | assert.Nil(t, err) 83 | 84 | defer func() { 85 | time.Sleep(100 * time.Millisecond) 86 | c.Close() 87 | }() 88 | 89 | // closing before connecting shouldn't do anthing 90 | c.Close() 91 | 92 | // connect successfully 93 | err = c.Connect() 94 | assert.Nil(t, err) 95 | 96 | // connecting twice shouldn't do anything 97 | err = c.Connect() 98 | assert.Nil(t, err) 99 | 100 | c.Close() 101 | c.Output() 102 | } 103 | 104 | func TestConnectAuthFailure(t *testing.T) { 105 | s := httptest.NewServer(http.HandlerFunc(connect)) 106 | defer s.Close() 107 | 108 | log := logrus.New() 109 | log.SetLevel(logrus.DebugLevel) 110 | u := "ws" + strings.TrimPrefix(s.URL, "http") 111 | var retries uint64 = 0 112 | c, err := New(Config{ 113 | APIKey: "bad", 114 | Feed: Feed(u), 115 | Market: Market(""), 116 | Log: log, 117 | MaxRetries: &retries, 118 | }) 119 | assert.NotNil(t, c) 120 | assert.Nil(t, err) 121 | 122 | defer func() { 123 | time.Sleep(100 * time.Millisecond) 124 | c.Close() 125 | }() 126 | 127 | err = c.Connect() 128 | assert.Nil(t, err) 129 | 130 | err = <-c.Error() 131 | assert.NotNil(t, err) 132 | } 133 | 134 | func TestConnectRetryFailure(t *testing.T) { 135 | s := httptest.NewServer(http.HandlerFunc(connect)) 136 | defer s.Close() 137 | 138 | log := logrus.New() 139 | log.SetLevel(logrus.DebugLevel) 140 | u := "wss" + strings.TrimPrefix(s.URL, "http") // connecting to wss should fail 141 | var retries uint64 = 1 142 | c, err := New(Config{ 143 | APIKey: "bad", 144 | Feed: Feed(u), 145 | Market: Market(""), 146 | Log: log, 147 | MaxRetries: &retries, 148 | }) 149 | assert.NotNil(t, c) 150 | assert.Nil(t, err) 151 | err = c.Connect() 152 | assert.NotNil(t, err) 153 | c.Close() 154 | } 155 | 156 | func TestReconnectCallback(t *testing.T) { 157 | s := httptest.NewServer(http.HandlerFunc(connect)) 158 | defer s.Close() 159 | 160 | reconnectCallbackCount := 0 161 | log := logrus.New() 162 | log.SetLevel(logrus.DebugLevel) 163 | u := "ws" + strings.TrimPrefix(s.URL, "http") 164 | var retries uint64 = 0 165 | c, err := New(Config{ 166 | APIKey: "good", 167 | Feed: Feed(u), 168 | Market: Market(""), 169 | Log: log, 170 | MaxRetries: &retries, 171 | ReconnectCallback: func(err error) { 172 | assert.Nil(t, err) 173 | reconnectCallbackCount++ 174 | }, 175 | }) 176 | assert.NotNil(t, c) 177 | assert.Nil(t, err) 178 | err = c.Connect() 179 | assert.Nil(t, err) 180 | c.reconnect() 181 | c.Close() 182 | assert.Equal(t, 1, reconnectCallbackCount) 183 | } 184 | -------------------------------------------------------------------------------- /rest/trades_test.go: -------------------------------------------------------------------------------- 1 | package massive_test 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "testing" 7 | "time" 8 | 9 | "github.com/jarcoal/httpmock" 10 | massive "github.com/massive-com/client-go/v2/rest" 11 | "github.com/massive-com/client-go/v2/rest/models" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestListTrades(t *testing.T) { 16 | c := massive.New("API_KEY") 17 | 18 | httpmock.ActivateNonDefault(c.HTTP.GetClient()) 19 | defer httpmock.DeactivateAndReset() 20 | 21 | trade1 := `{ 22 | "conditions": [ 23 | 12, 24 | 41 25 | ], 26 | "exchange": 11, 27 | "id": "1", 28 | "participant_timestamp": 1517562000015577000, 29 | "price": 171.55, 30 | "sequence_number": 1063, 31 | "sip_timestamp": 1517562000016036600, 32 | "size": 100, 33 | "tape": 3 34 | }` 35 | 36 | trade2 := `{ 37 | "conditions": [ 38 | 12, 39 | 41 40 | ], 41 | "exchange": 11, 42 | "id": "2", 43 | "participant_timestamp": 1517562000015577600, 44 | "price": 171.55, 45 | "sequence_number": 1064, 46 | "sip_timestamp": 1517562000016038100, 47 | "size": 100, 48 | "tape": 3 49 | }` 50 | 51 | expectedResponse := `{ 52 | "status": "OK", 53 | "request_id": "a47d1beb8c11b6ae897ab76cdbbf35a3", 54 | "next_url": "https://api.massive.com/v3/trades/AAPL?cursor=YWN0aXZlPXRydWUmZGF0ZT0yMDIxLTA0LTI1JmxpbWl0PTEmb3JkZXI9YXNjJnBhZ2VfbWFya2VyPUElN0M5YWRjMjY0ZTgyM2E1ZjBiOGUyNDc5YmZiOGE1YmYwNDVkYzU0YjgwMDcyMWE2YmI1ZjBjMjQwMjU4MjFmNGZiJnNvcnQ9dGlja2Vy", 55 | "results": [ 56 | ` + indent(true, trade1, "\t\t") + `, 57 | ` + indent(true, trade2, "\t\t") + ` 58 | ] 59 | }` 60 | 61 | registerResponder("https://api.massive.com/v3/trades/AAPL?limit=2&order=asc&sort=timestamp×tamp.gte=1626948000000000000", expectedResponse) 62 | registerResponder("https://api.massive.com/v3/trades/AAPL?cursor=YWN0aXZlPXRydWUmZGF0ZT0yMDIxLTA0LTI1JmxpbWl0PTEmb3JkZXI9YXNjJnBhZ2VfbWFya2VyPUElN0M5YWRjMjY0ZTgyM2E1ZjBiOGUyNDc5YmZiOGE1YmYwNDVkYzU0YjgwMDcyMWE2YmI1ZjBjMjQwMjU4MjFmNGZiJnNvcnQ9dGlja2Vy&sort=timestamp", "{}") 63 | iter := c.ListTrades(context.Background(), models.ListTradesParams{Ticker: "AAPL"}. 64 | WithTimestamp(models.GTE, models.Nanos(time.Date(2021, 7, 22, 10, 0, 0, 0, time.UTC))). 65 | WithOrder(models.Asc).WithLimit(2), models.QueryParam("sort", string(models.Timestamp))) 66 | 67 | // iter creation 68 | assert.Nil(t, iter.Err()) 69 | assert.NotNil(t, iter.Item()) 70 | 71 | // first item 72 | assert.True(t, iter.Next()) 73 | assert.Nil(t, iter.Err()) 74 | var expect1 models.Trade 75 | err := json.Unmarshal([]byte(trade1), &expect1) 76 | assert.Nil(t, err) 77 | assert.Equal(t, expect1, iter.Item()) 78 | 79 | // second item 80 | assert.True(t, iter.Next()) 81 | assert.Nil(t, iter.Err()) 82 | var expect2 models.Trade 83 | err = json.Unmarshal([]byte(trade2), &expect2) 84 | assert.Nil(t, err) 85 | assert.Equal(t, expect2, iter.Item()) 86 | 87 | // end of list 88 | assert.False(t, iter.Next()) 89 | assert.Nil(t, iter.Err()) 90 | } 91 | 92 | func TestGetLastTrade(t *testing.T) { 93 | c := massive.New("API_KEY") 94 | 95 | httpmock.ActivateNonDefault(c.HTTP.GetClient()) 96 | defer httpmock.DeactivateAndReset() 97 | 98 | expectedResponse := `{ 99 | "status": "OK", 100 | "request_id": "f05562305bd26ced64b98ed68b3c5d96", 101 | "results": { 102 | "T": "AAPL", 103 | "f": 1617901342969796400, 104 | "q": 3135876, 105 | "t": 1617901342969834000, 106 | "y": 1617901342968000000, 107 | "c": [ 108 | 37 109 | ], 110 | "i": "118749", 111 | "p": 129.8473, 112 | "r": 202, 113 | "s": 25, 114 | "x": 4, 115 | "z": 3 116 | } 117 | }` 118 | 119 | registerResponder("https://api.massive.com/v2/last/trade/AAPL", expectedResponse) 120 | res, err := c.GetLastTrade(context.Background(), &models.GetLastTradeParams{ 121 | Ticker: "AAPL", 122 | }) 123 | assert.Nil(t, err) 124 | 125 | var expect models.GetLastTradeResponse 126 | err = json.Unmarshal([]byte(expectedResponse), &expect) 127 | assert.Nil(t, err) 128 | assert.Equal(t, &expect, res) 129 | } 130 | 131 | func TestGetLastCryptoTrade(t *testing.T) { 132 | c := massive.New("API_KEY") 133 | 134 | httpmock.ActivateNonDefault(c.HTTP.GetClient()) 135 | defer httpmock.DeactivateAndReset() 136 | 137 | expectedResponse := `{ 138 | "status": "success", 139 | "request_id": "d2d779df015fe2b7fbb8e58366610ef7", 140 | "symbol": "BTC-USD", 141 | "last": { 142 | "price": 16835.42, 143 | "size": 0.006909, 144 | "exchange": 4, 145 | "conditions": [ 146 | 1 147 | ], 148 | "timestamp": 1605560885027 149 | } 150 | }` 151 | 152 | registerResponder("https://api.massive.com/v1/last/crypto/BTC/USD", expectedResponse) 153 | res, err := c.GetLastCryptoTrade(context.Background(), &models.GetLastCryptoTradeParams{ 154 | From: "BTC", 155 | To: "USD", 156 | }) 157 | assert.Nil(t, err) 158 | 159 | var expect models.GetLastCryptoTradeResponse 160 | err = json.Unmarshal([]byte(expectedResponse), &expect) 161 | assert.Nil(t, err) 162 | assert.Equal(t, &expect, res) 163 | } 164 | --------------------------------------------------------------------------------