123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- package feed
- import (
- "fmt"
- "log/slog"
- "net/http"
- )
- type marketResponseJson struct {
- Chart struct {
- Result []struct {
- Meta struct {
- Currency string `json:"currency"`
- Symbol string `json:"symbol"`
- RegularMarketPrice float64 `json:"regularMarketPrice"`
- ChartPreviousClose float64 `json:"chartPreviousClose"`
- } `json:"meta"`
- Indicators struct {
- Quote []struct {
- Close []float64 `json:"close,omitempty"`
- } `json:"quote"`
- } `json:"indicators"`
- } `json:"result"`
- } `json:"chart"`
- }
- // TODO: allow changing chart time frame
- const marketChartDays = 21
- func FetchMarketsDataFromYahoo(marketRequests []MarketRequest) (Markets, error) {
- requests := make([]*http.Request, 0, len(marketRequests))
- for i := range marketRequests {
- request, _ := http.NewRequest("GET", fmt.Sprintf("https://query1.finance.yahoo.com/v8/finance/chart/%s?range=1mo&interval=1d", marketRequests[i].Symbol), nil)
- requests = append(requests, request)
- }
- job := newJob(decodeJsonFromRequestTask[marketResponseJson](defaultClient), requests)
- responses, errs, err := workerPoolDo(job)
- if err != nil {
- return nil, fmt.Errorf("%w: %v", ErrNoContent, err)
- }
- markets := make(Markets, 0, len(responses))
- var failed int
- for i := range responses {
- if errs[i] != nil {
- failed++
- slog.Error("Failed to fetch market data", "symbol", marketRequests[i].Symbol, "error", errs[i])
- continue
- }
- response := responses[i]
- if len(response.Chart.Result) == 0 {
- failed++
- slog.Error("Market response contains no data", "symbol", marketRequests[i].Symbol)
- continue
- }
- prices := response.Chart.Result[0].Indicators.Quote[0].Close
- if len(prices) > marketChartDays {
- prices = prices[len(prices)-marketChartDays:]
- }
- previous := response.Chart.Result[0].Meta.RegularMarketPrice
- if len(prices) >= 2 && prices[len(prices)-2] != 0 {
- previous = prices[len(prices)-2]
- }
- points := SvgPolylineCoordsFromYValues(100, 50, maybeCopySliceWithoutZeroValues(prices))
- currency, exists := currencyToSymbol[response.Chart.Result[0].Meta.Currency]
- if !exists {
- currency = response.Chart.Result[0].Meta.Currency
- }
- markets = append(markets, Market{
- MarketRequest: marketRequests[i],
- Price: response.Chart.Result[0].Meta.RegularMarketPrice,
- Currency: currency,
- PercentChange: percentChange(
- response.Chart.Result[0].Meta.RegularMarketPrice,
- previous,
- ),
- SvgChartPoints: points,
- })
- }
- if len(markets) == 0 {
- return nil, ErrNoContent
- }
- if failed > 0 {
- return markets, fmt.Errorf("%w: could not fetch data for %d market(s)", ErrPartialContent, failed)
- }
- return markets, nil
- }
|