Steven Polley 61074bfd80
All checks were successful
continuous-integration/drone/push Build is passing
implement rudimentary caching for yahoo finance to avoid http 429 rate limiting
2025-05-07 19:59:30 -06:00

96 lines
3.3 KiB
Go

package staticjsonYahooFinance
import (
"fmt"
"net/url"
"time"
)
// A chartResponse response is what we get back from Yahoo Finance
type chartResponse struct {
Chart struct {
Result []struct {
Meta struct {
Currency string `json:"currency"`
Symbol string `json:"symbol"`
ExchangeName string `json:"exchangeName"`
InstrumentType string `json:"instrumentType"`
FirstTradeDate int `json:"firstTradeDate"`
RegularMarketTime int `json:"regularMarketTime"`
Gmtoffset int `json:"gmtoffset"`
Timezone string `json:"timezone"`
ExchangeTimezoneName string `json:"exchangeTimezoneName"`
RegularMarketPrice float64 `json:"regularMarketPrice"`
ChartPreviousClose float64 `json:"chartPreviousClose"`
PreviousClose float64 `json:"previousClose"`
Scale int `json:"scale"`
PriceHint int `json:"priceHint"`
CurrentTradingPeriod struct {
Pre struct {
Timezone string `json:"timezone"`
Start int `json:"start"`
End int `json:"end"`
Gmtoffset int `json:"gmtoffset"`
} `json:"pre"`
Regular struct {
Timezone string `json:"timezone"`
Start int `json:"start"`
End int `json:"end"`
Gmtoffset int `json:"gmtoffset"`
} `json:"regular"`
Post struct {
Timezone string `json:"timezone"`
Start int `json:"start"`
End int `json:"end"`
Gmtoffset int `json:"gmtoffset"`
} `json:"post"`
} `json:"currentTradingPeriod"`
TradingPeriods [][]struct {
Timezone string `json:"timezone"`
Start int `json:"start"`
End int `json:"end"`
Gmtoffset int `json:"gmtoffset"`
} `json:"tradingPeriods"`
DataGranularity string `json:"dataGranularity"`
Range string `json:"range"`
ValidRanges []string `json:"validRanges"`
} `json:"meta"`
Timestamp []int `json:"timestamp"`
Indicators struct {
Quote []struct {
Volume []any `json:"volume"`
High []any `json:"high"`
Close []any `json:"close"`
Open []any `json:"open"`
Low []any `json:"low"`
} `json:"quote"`
} `json:"indicators"`
} `json:"result"`
Error any `json:"error"`
} `json:"chart"`
}
// getChart first checks if the symbol chartCacheEntry is valid,
// and if not then it downloads fresh chart data
func (c client) getChart(symbol string) (int, error) {
if cacheItem, ok := chartCache[symbol]; ok {
// if the cacheEntry is still valid, use it
if time.Now().Before(cacheItem.LastUpdated.Add(cacheAgeSeconds)) {
return int(cacheItem.Chart.Chart.Result[0].Meta.RegularMarketPrice * 1000), nil
}
}
chartResponse := &chartResponse{}
err := c.get(fmt.Sprintf("/chart/%s", symbol), chartResponse, url.Values{})
if err != nil {
return 0, fmt.Errorf("http get request error: %v", err)
}
if len(chartResponse.Chart.Result) != 1 {
return 0, fmt.Errorf("unexpected length of results - expected 1 but got %d", len(chartResponse.Chart.Result))
}
chartCache[symbol] = chartCacheEntry{Chart: *chartResponse, LastUpdated: time.Now()}
return int(chartResponse.Chart.Result[0].Meta.RegularMarketPrice * 1000), nil
}