func (c *client) GetSnapshot(symbol string) (*Snapshot, error) {
	u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/%s/snapshot", c.opts.BaseURL, symbol))
	if err != nil {
		return nil, err
	}
	setLatestQueryParams(u, c.opts.Feed, nil)
	resp, err := c.get(u)
	if err != nil {
		return nil, err
	}
	var snapshot Snapshot
	if err = unmarshal(resp, &snapshot); err != nil {
		return nil, err
	}
	return &snapshot, nil
}
GetSnapshot returns the snapshot for a given symbol
package marketdata
import (
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strings"
	"time"
)
type Client interface {
	GetTrades(symbol string, params GetTradesParams) ([]Trade, error)
	GetTradesAsync(symbol string, params GetTradesParams) <-chan TradeItem
	GetMultiTrades(symbols []string, params GetTradesParams) (map[string][]Trade, error)
	GetMultiTradesAsync(symbols []string, params GetTradesParams) <-chan MultiTradeItem
	GetQuotes(symbol string, params GetQuotesParams) ([]Quote, error)
	GetQuotesAsync(symbol string, params GetQuotesParams) <-chan QuoteItem
	GetMultiQuotes(symbols []string, params GetQuotesParams) (map[string][]Quote, error)
	GetMultiQuotesAsync(symbols []string, params GetQuotesParams) <-chan MultiQuoteItem
	GetBars(symbol string, params GetBarsParams) ([]Bar, error)
	GetBarsAsync(symbol string, params GetBarsParams) <-chan BarItem
	GetMultiBars(symbols []string, params GetBarsParams) (map[string][]Bar, error)
	GetMultiBarsAsync(symbols []string, params GetBarsParams) <-chan MultiBarItem
	GetLatestBar(symbol string) (*Bar, error)
	GetLatestBars(symbols []string) (map[string]Bar, error)
	GetLatestTrade(symbol string) (*Trade, error)
	GetLatestTrades(symbols []string) (map[string]Trade, error)
	GetLatestQuote(symbol string) (*Quote, error)
	GetLatestQuotes(symbols []string) (map[string]Quote, error)
	GetSnapshot(symbol string) (*Snapshot, error)
	GetSnapshots(symbols []string) (map[string]*Snapshot, error)
	GetCryptoTrades(symbol string, params GetCryptoTradesParams) ([]CryptoTrade, error)
	GetCryptoTradesAsync(symbol string, params GetCryptoTradesParams) <-chan CryptoTradeItem
	GetCryptoQuotes(symbol string, params GetCryptoQuotesParams) ([]CryptoQuote, error)
	GetCryptoQuotesAsync(symbol string, params GetCryptoQuotesParams) <-chan CryptoQuoteItem
	GetCryptoBars(symbol string, params GetCryptoBarsParams) ([]CryptoBar, error)
	GetCryptoBarsAsync(symbol string, params GetCryptoBarsParams) <-chan CryptoBarItem
	GetCryptoMultiBars(symbols []string, params GetCryptoBarsParams) (map[string][]CryptoBar, error)
	GetCryptoMultiBarsAsync(symbols []string, params GetCryptoBarsParams) <-chan CryptoMultiBarItem
	GetLatestCryptoTrade(symbol, exchange string) (*CryptoTrade, error)
	GetLatestCryptoQuote(symbol, exchange string) (*CryptoQuote, error)
	GetLatestCryptoXBBO(symbol string, exchanges []string) (*CryptoXBBO, error)
}
type ClientOpts struct {
	ApiKey     string
	ApiSecret  string
	OAuth      string
	BaseURL    string
	Timeout    time.Duration
	RetryLimit int
	RetryDelay time.Duration
	Feed       string
}
type client struct {
	opts ClientOpts
	do func(c *client, req *http.Request) (*http.Response, error)
}
func NewClient(opts ClientOpts) Client {
	if opts.ApiKey == "" {
		opts.ApiKey = os.Getenv("APCA_API_KEY_ID")
	}
	if opts.ApiSecret == "" {
		opts.ApiSecret = os.Getenv("APCA_API_SECRET_KEY")
	}
	if opts.OAuth == "" {
		opts.OAuth = os.Getenv("APCA_API_OAUTH")
	}
	if opts.BaseURL == "" {
		if s := os.Getenv("APCA_API_DATA_URL"); s != "" {
			opts.BaseURL = s
		} else {
			opts.BaseURL = "https://data.alpaca.markets"
		}
	}
	if opts.RetryLimit == 0 {
		opts.RetryLimit = 10
	}
	if opts.RetryDelay == 0 {
		opts.RetryDelay = time.Second
	}
	return &client{
		opts: opts,
		do: defaultDo,
	}
}
var DefaultClient = NewClient(ClientOpts{})
func defaultDo(c *client, req *http.Request) (*http.Response, error) {
	if c.opts.OAuth != "" {
		req.Header.Set("Authorization", "Bearer "+c.opts.OAuth)
	} else {
		req.Header.Set("APCA-API-KEY-ID", c.opts.ApiKey)
		req.Header.Set("APCA-API-SECRET-KEY", c.opts.ApiSecret)
	}
	client := &http.Client{
		Timeout: c.opts.Timeout,
	}
	var resp *http.Response
	var err error
	for i := 0; ; i++ {
		resp, err = client.Do(req)
		if err != nil {
			return nil, err
		}
		if resp.StatusCode != http.StatusTooManyRequests {
			break
		}
		if i >= c.opts.RetryLimit {
			break
		}
		time.Sleep(c.opts.RetryDelay)
	}
	if err = verify(resp); err != nil {
		return nil, err
	}
	return resp, nil
}
func setBaseQuery(q url.Values, start, end time.Time, feed, defaultFeed string) {
	if !start.IsZero() {
		q.Set("start", start.Format(time.RFC3339))
	}
	if !end.IsZero() {
		q.Set("end", end.Format(time.RFC3339))
	}
	if feed != "" {
		q.Set("feed", feed)
	} else {
		if defaultFeed != "" {
			q.Set("feed", feed)
		}
	}
}
func setCryptoBaseQuery(q url.Values, start, end time.Time, exchanges []string) {
	if !start.IsZero() {
		q.Set("start", start.Format(time.RFC3339))
	}
	if !end.IsZero() {
		q.Set("end", end.Format(time.RFC3339))
	}
	if len(exchanges) > 0 {
		q.Set("exchanges", strings.Join(exchanges, ","))
	}
}
const v2MaxLimit = 10000
func setQueryLimit(q url.Values, totalLimit int, pageLimit int, received int) {
	limit := 0  
	if pageLimit != 0 {
		limit = pageLimit
	}
	if totalLimit != 0 {
		remaining := totalLimit - received
		if remaining <= 0 {  
			return
		}
		if (limit == 0 || limit > remaining) && remaining <= v2MaxLimit {
			limit = remaining
		}
	}
	if limit != 0 {
		q.Set("limit", fmt.Sprintf("%d", limit))
	}
}
type GetTradesParams struct {
	Start time.Time
	End time.Time
	TotalLimit int
	PageLimit int
	Feed string
}
func (c *client) GetTrades(symbol string, params GetTradesParams) ([]Trade, error) {
	trades := make([]Trade, 0)
	for item := range c.GetTradesAsync(symbol, params) {
		if err := item.Error; err != nil {
			return nil, err
		}
		trades = append(trades, item.Trade)
	}
	return trades, nil
}
func (c *client) GetTradesAsync(symbol string, params GetTradesParams) <-chan TradeItem {
	ch := make(chan TradeItem)
	go func() {
		defer close(ch)
		u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/%s/trades", c.opts.BaseURL, symbol))
		if err != nil {
			ch <- TradeItem{Error: err}
			return
		}
		q := u.Query()
		setBaseQuery(q, params.Start, params.End, params.Feed, c.opts.Feed)
		received := 0
		for params.TotalLimit == 0 || received < params.TotalLimit {
			setQueryLimit(q, params.TotalLimit, params.PageLimit, received)
			u.RawQuery = q.Encode()
			resp, err := c.get(u)
			if err != nil {
				ch <- TradeItem{Error: err}
				return
			}
			var tradeResp tradeResponse
			if err = unmarshal(resp, &tradeResp); err != nil {
				ch <- TradeItem{Error: err}
				return
			}
			for _, trade := range tradeResp.Trades {
				ch <- TradeItem{Trade: trade}
			}
			if tradeResp.NextPageToken == nil {
				return
			}
			q.Set("page_token", *tradeResp.NextPageToken)
			received += len(tradeResp.Trades)
		}
	}()
	return ch
}
func (c *client) GetMultiTrades(
	symbols []string, params GetTradesParams,
) (map[string][]Trade, error) {
	trades := make(map[string][]Trade, len(symbols))
	for item := range c.GetMultiTradesAsync(symbols, params) {
		if err := item.Error; err != nil {
			return nil, err
		}
		trades[item.Symbol] = append(trades[item.Symbol], item.Trade)
	}
	return trades, nil
}
func (c *client) GetMultiTradesAsync(symbols []string, params GetTradesParams) <-chan MultiTradeItem {
	ch := make(chan MultiTradeItem)
	go func() {
		defer close(ch)
		u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/trades", c.opts.BaseURL))
		if err != nil {
			ch <- MultiTradeItem{Error: err}
			return
		}
		q := u.Query()
		q.Set("symbols", strings.Join(symbols, ","))
		setBaseQuery(q, params.Start, params.End, params.Feed, c.opts.Feed)
		received := 0
		for params.TotalLimit == 0 || received < params.TotalLimit {
			setQueryLimit(q, params.TotalLimit, params.PageLimit, received)
			u.RawQuery = q.Encode()
			resp, err := c.get(u)
			if err != nil {
				ch <- MultiTradeItem{Error: err}
				return
			}
			var tradeResp multiTradeResponse
			if err = unmarshal(resp, &tradeResp); err != nil {
				ch <- MultiTradeItem{Error: err}
				return
			}
			sortedSymbols := make([]string, 0, len(tradeResp.Trades))
			for symbol := range tradeResp.Trades {
				sortedSymbols = append(sortedSymbols, symbol)
			}
			sort.Strings(sortedSymbols)
			for _, symbol := range sortedSymbols {
				trades := tradeResp.Trades[symbol]
				for _, trade := range trades {
					ch <- MultiTradeItem{Symbol: symbol, Trade: trade}
				}
				received += len(trades)
			}
			if tradeResp.NextPageToken == nil {
				return
			}
			q.Set("page_token", *tradeResp.NextPageToken)
		}
	}()
	return ch
}
type GetQuotesParams struct {
	Start time.Time
	End time.Time
	TotalLimit int
	PageLimit int
	Feed string
}
func (c *client) GetQuotes(symbol string, params GetQuotesParams) ([]Quote, error) {
	quotes := make([]Quote, 0)
	for item := range c.GetQuotesAsync(symbol, params) {
		if err := item.Error; err != nil {
			return nil, err
		}
		quotes = append(quotes, item.Quote)
	}
	return quotes, nil
}
func (c *client) GetQuotesAsync(symbol string, params GetQuotesParams) <-chan QuoteItem {
	ch := make(chan QuoteItem)
	go func() {
		defer close(ch)
		u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/%s/quotes", c.opts.BaseURL, symbol))
		if err != nil {
			ch <- QuoteItem{Error: err}
			return
		}
		q := u.Query()
		setBaseQuery(q, params.Start, params.End, params.Feed, c.opts.Feed)
		received := 0
		for params.TotalLimit == 0 || received < params.TotalLimit {
			setQueryLimit(q, params.TotalLimit, params.PageLimit, received)
			u.RawQuery = q.Encode()
			resp, err := c.get(u)
			if err != nil {
				ch <- QuoteItem{Error: err}
				return
			}
			var quoteResp quoteResponse
			if err = unmarshal(resp, &quoteResp); err != nil {
				ch <- QuoteItem{Error: err}
				return
			}
			for _, quote := range quoteResp.Quotes {
				ch <- QuoteItem{Quote: quote}
			}
			if quoteResp.NextPageToken == nil {
				return
			}
			q.Set("page_token", *quoteResp.NextPageToken)
			received += len(quoteResp.Quotes)
		}
	}()
	return ch
}
func (c *client) GetMultiQuotes(
	symbols []string, params GetQuotesParams,
) (map[string][]Quote, error) {
	quotes := make(map[string][]Quote, len(symbols))
	for item := range c.GetMultiQuotesAsync(symbols, params) {
		if err := item.Error; err != nil {
			return nil, err
		}
		quotes[item.Symbol] = append(quotes[item.Symbol], item.Quote)
	}
	return quotes, nil
}
func (c *client) GetMultiQuotesAsync(symbols []string, params GetQuotesParams) <-chan MultiQuoteItem {
	ch := make(chan MultiQuoteItem)
	go func() {
		defer close(ch)
		u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/quotes", c.opts.BaseURL))
		if err != nil {
			ch <- MultiQuoteItem{Error: err}
			return
		}
		q := u.Query()
		q.Set("symbols", strings.Join(symbols, ","))
		setBaseQuery(q, params.Start, params.End, params.Feed, c.opts.Feed)
		received := 0
		for params.TotalLimit == 0 || received < params.TotalLimit {
			setQueryLimit(q, params.TotalLimit, params.PageLimit, received)
			u.RawQuery = q.Encode()
			resp, err := c.get(u)
			if err != nil {
				ch <- MultiQuoteItem{Error: err}
				return
			}
			var quoteResp multiQuoteResponse
			if err = unmarshal(resp, &quoteResp); err != nil {
				ch <- MultiQuoteItem{Error: err}
				return
			}
			sortedSymbols := make([]string, 0, len(quoteResp.Quotes))
			for symbol := range quoteResp.Quotes {
				sortedSymbols = append(sortedSymbols, symbol)
			}
			sort.Strings(sortedSymbols)
			for _, symbol := range sortedSymbols {
				quotes := quoteResp.Quotes[symbol]
				for _, quote := range quotes {
					ch <- MultiQuoteItem{Symbol: symbol, Quote: quote}
				}
				received += len(quotes)
			}
			if quoteResp.NextPageToken == nil {
				return
			}
			q.Set("page_token", *quoteResp.NextPageToken)
		}
	}()
	return ch
}
type GetBarsParams struct {
	TimeFrame TimeFrame
	Adjustment Adjustment
	Start time.Time
	End time.Time
	TotalLimit int
	PageLimit int
	Feed string
}
func setQueryBarParams(q url.Values, params GetBarsParams, feed string) {
	setBaseQuery(q, params.Start, params.End, params.Feed, feed)
	adjustment := Raw
	if params.Adjustment != "" {
		adjustment = params.Adjustment
	}
	q.Set("adjustment", string(adjustment))
	timeframe := OneDay
	if params.TimeFrame.N != 0 {
		timeframe = params.TimeFrame
	}
	q.Set("timeframe", timeframe.String())
}
func (c *client) GetBars(symbol string, params GetBarsParams) ([]Bar, error) {
	bars := make([]Bar, 0)
	for item := range c.GetBarsAsync(symbol, params) {
		if err := item.Error; err != nil {
			return nil, err
		}
		bars = append(bars, item.Bar)
	}
	return bars, nil
}
func (c *client) GetBarsAsync(symbol string, params GetBarsParams) <-chan BarItem {
	ch := make(chan BarItem)
	go func() {
		defer close(ch)
		u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/%s/bars", c.opts.BaseURL, symbol))
		if err != nil {
			ch <- BarItem{Error: err}
			return
		}
		q := u.Query()
		setQueryBarParams(q, params, c.opts.Feed)
		received := 0
		for params.TotalLimit == 0 || received < params.TotalLimit {
			setQueryLimit(q, params.TotalLimit, params.PageLimit, received)
			u.RawQuery = q.Encode()
			resp, err := c.get(u)
			if err != nil {
				ch <- BarItem{Error: err}
				return
			}
			var barResp barResponse
			if err = unmarshal(resp, &barResp); err != nil {
				ch <- BarItem{Error: err}
				return
			}
			for _, bar := range barResp.Bars {
				ch <- BarItem{Bar: bar}
			}
			if barResp.NextPageToken == nil {
				return
			}
			q.Set("page_token", *barResp.NextPageToken)
			received += len(barResp.Bars)
		}
	}()
	return ch
}
func (c *client) GetMultiBars(
	symbols []string, params GetBarsParams,
) (map[string][]Bar, error) {
	bars := make(map[string][]Bar, len(symbols))
	for item := range c.GetMultiBarsAsync(symbols, params) {
		if err := item.Error; err != nil {
			return nil, err
		}
		bars[item.Symbol] = append(bars[item.Symbol], item.Bar)
	}
	return bars, nil
}
func (c *client) GetMultiBarsAsync(symbols []string, params GetBarsParams) <-chan MultiBarItem {
	ch := make(chan MultiBarItem)
	go func() {
		defer close(ch)
		u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/bars", c.opts.BaseURL))
		if err != nil {
			ch <- MultiBarItem{Error: err}
			return
		}
		q := u.Query()
		q.Set("symbols", strings.Join(symbols, ","))
		setQueryBarParams(q, params, c.opts.Feed)
		received := 0
		for params.TotalLimit == 0 || received < params.TotalLimit {
			setQueryLimit(q, params.TotalLimit, params.PageLimit, received)
			u.RawQuery = q.Encode()
			resp, err := c.get(u)
			if err != nil {
				ch <- MultiBarItem{Error: err}
				return
			}
			var barResp multiBarResponse
			if err = unmarshal(resp, &barResp); err != nil {
				ch <- MultiBarItem{Error: err}
				return
			}
			sortedSymbols := make([]string, 0, len(barResp.Bars))
			for symbol := range barResp.Bars {
				sortedSymbols = append(sortedSymbols, symbol)
			}
			sort.Strings(sortedSymbols)
			for _, symbol := range sortedSymbols {
				bars := barResp.Bars[symbol]
				for _, bar := range bars {
					ch <- MultiBarItem{Symbol: symbol, Bar: bar}
				}
				received += len(bars)
			}
			if barResp.NextPageToken == nil {
				return
			}
			q.Set("page_token", *barResp.NextPageToken)
		}
	}()
	return ch
}
func setLatestQueryParams(u *url.URL, feed string, symbols []string) {
	q := u.Query()
	if len(symbols) > 0 {
		q.Set("symbols", strings.Join(symbols, ","))
	}
	if feed != "" {
		q.Set("feed", feed)
	}
	u.RawQuery = q.Encode()
}
func (c *client) GetLatestBar(symbol string) (*Bar, error) {
	u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/%s/bars/latest", c.opts.BaseURL, symbol))
	if err != nil {
		return nil, err
	}
	setLatestQueryParams(u, c.opts.Feed, nil)
	resp, err := c.get(u)
	if err != nil {
		return nil, err
	}
	var latestBarResp latestBarResponse
	if err = unmarshal(resp, &latestBarResp); err != nil {
		return nil, err
	}
	return &latestBarResp.Bar, nil
}
func (c *client) GetLatestBars(symbols []string) (map[string]Bar, error) {
	u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/bars/latest", c.opts.BaseURL))
	if err != nil {
		return nil, err
	}
	setLatestQueryParams(u, c.opts.Feed, symbols)
	resp, err := c.get(u)
	if err != nil {
		return nil, err
	}
	var latestBarsResp latestBarsResponse
	if err = unmarshal(resp, &latestBarsResp); err != nil {
		return nil, err
	}
	return latestBarsResp.Bars, nil
}
func (c *client) GetLatestTrade(symbol string) (*Trade, error) {
	u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/%s/trades/latest", c.opts.BaseURL, symbol))
	if err != nil {
		return nil, err
	}
	setLatestQueryParams(u, c.opts.Feed, nil)
	resp, err := c.get(u)
	if err != nil {
		return nil, err
	}
	var latestTradeResp latestTradeResponse
	if err = unmarshal(resp, &latestTradeResp); err != nil {
		return nil, err
	}
	return &latestTradeResp.Trade, nil
}
func (c *client) GetLatestTrades(symbols []string) (map[string]Trade, error) {
	u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/trades/latest", c.opts.BaseURL))
	if err != nil {
		return nil, err
	}
	setLatestQueryParams(u, c.opts.Feed, symbols)
	resp, err := c.get(u)
	if err != nil {
		return nil, err
	}
	var latestTradesResp latestTradesResponse
	if err = unmarshal(resp, &latestTradesResp); err != nil {
		return nil, err
	}
	return latestTradesResp.Trades, nil
}
func (c *client) GetLatestQuote(symbol string) (*Quote, error) {
	u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/%s/quotes/latest", c.opts.BaseURL, symbol))
	if err != nil {
		return nil, err
	}
	setLatestQueryParams(u, c.opts.Feed, nil)
	resp, err := c.get(u)
	if err != nil {
		return nil, err
	}
	var latestQuoteResp latestQuoteResponse
	if err = unmarshal(resp, &latestQuoteResp); err != nil {
		return nil, err
	}
	return &latestQuoteResp.Quote, nil
}
func (c *client) GetLatestQuotes(symbols []string) (map[string]Quote, error) {
	u, err := url.Parse(fmt.Sprintf("%s/v2/stocks/quotes/latest", c.opts.BaseURL))
	if err != nil {
		return nil, err
	}
	setLatestQueryParams(u, c.opts.Feed, symbols)
	resp, err := c.get(u)
	if err != nil {
		return nil, err
	}
	var latestQuotesResp latestQuotesResponse
	if err = unmarshal(resp, &latestQuotesResp); err != nil {
		return nil, err
	}
	return latestQuotesResp.Quotes, nil
}