implement rudimentary caching for yahoo finance to avoid http 429 rate limiting
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Steven Polley 2025-05-07 19:59:30 -06:00
parent 13291da691
commit 61074bfd80
3 changed files with 31 additions and 5 deletions

View File

@ -0,0 +1,15 @@
package staticjsonYahooFinance
import (
"time"
)
const cacheAgeSeconds = 900
// intialized in providerImpl.go's Configure
var chartCache map[string]chartCacheEntry
type chartCacheEntry struct {
Chart chartResponse
LastUpdated time.Time
}

View File

@ -3,10 +3,11 @@ package staticjsonYahooFinance
import ( import (
"fmt" "fmt"
"net/url" "net/url"
"time"
) )
// A chart response is what we get back from Yahoo Finance // A chartResponse response is what we get back from Yahoo Finance
type chart struct { type chartResponse struct {
Chart struct { Chart struct {
Result []struct { Result []struct {
Meta struct { Meta struct {
@ -69,8 +70,16 @@ type chart struct {
} `json:"chart"` } `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) { func (c client) getChart(symbol string) (int, error) {
chartResponse := &chart{} 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{}) err := c.get(fmt.Sprintf("/chart/%s", symbol), chartResponse, url.Values{})
if err != nil { if err != nil {
return 0, fmt.Errorf("http get request error: %v", err) return 0, fmt.Errorf("http get request error: %v", err)
@ -80,5 +89,7 @@ func (c client) getChart(symbol string) (int, error) {
return 0, fmt.Errorf("unexpected length of results - expected 1 but got %d", len(chartResponse.Chart.Result)) 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 return int(chartResponse.Chart.Result[0].Meta.RegularMarketPrice * 1000), nil
} }

View File

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"time"
) )
type security struct { type security struct {
@ -48,6 +47,8 @@ func (p *Provider) Configure() error {
return fmt.Errorf("failed to create new client: %v", err) return fmt.Errorf("failed to create new client: %v", err)
} }
chartCache = make(map[string]chartCacheEntry)
return nil return nil
} }
@ -63,7 +64,6 @@ func (p *Provider) GetBalances() ([]int, []string, error) {
return balances, ynabAccountIDs, fmt.Errorf("failed to get quote for security with symbol '%s': %v", p.data.Accounts[i].Securities[j].Symbol, err) return balances, ynabAccountIDs, fmt.Errorf("failed to get quote for security with symbol '%s': %v", p.data.Accounts[i].Securities[j].Symbol, err)
} }
balance += price * p.data.Accounts[i].Securities[j].Quantity balance += price * p.data.Accounts[i].Securities[j].Quantity
time.Sleep(time.Second) // Disgusting way to avoid HTTP 429 rate limiting
} }
balances = append(balances, balance) balances = append(balances, balance)
ynabAccountIDs = append(ynabAccountIDs, p.data.Accounts[i].YnabAccountID) ynabAccountIDs = append(ynabAccountIDs, p.data.Accounts[i].YnabAccountID)