-
Notifications
You must be signed in to change notification settings - Fork 42
/
financialQuotes.go
140 lines (117 loc) · 3.42 KB
/
financialQuotes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package cmd
import (
"context"
"encoding/csv"
"encoding/json"
"errors"
"net/http"
"strconv"
"strings"
"time"
"github.com/patrickmn/go-cache"
"golang.org/x/time/rate"
)
// Alpha Vantage allows 5 per minute, we do 4 per minute
var avLimiter *rate.Limiter = rate.NewLimiter(rate.Every(time.Minute/4), 1)
type avQuote struct {
Symbol string
Open float64
PreviousClose float64
Last float64
}
// Alpha Vantage allows 500 requests per day. Since we don't care about realtime
// values, we cache results for 24 hours.
var avqCache *cache.Cache = cache.New(time.Hour*24, time.Hour)
// https://www.alphavantage.co/documentation/#latestprice
func fundQuote(symbol string) (quote avQuote, err error) {
if avq, found := avqCache.Get(symbol); found {
return avq.(avQuote), nil
}
go func() {
avLimiter.Wait(context.Background())
resp, herr := http.Get("https://alphavantage.co/query?function=GLOBAL_QUOTE&symbol=" + symbol + "&datatype=csv&apikey=" + portfolioConfigData.AVToken)
if herr != nil {
return
}
defer resp.Body.Close()
cr := csv.NewReader(resp.Body)
recs, cerr := cr.ReadAll()
if cerr != nil {
return
}
// symbol,open,high,low,price,volume,latestDay,previousClose,change,changePercent
if len(recs) != 2 || len(recs[0]) != 10 {
return
}
quote.Symbol = recs[1][0]
quote.Open, _ = strconv.ParseFloat(recs[1][1], 64)
quote.Last, _ = strconv.ParseFloat(recs[1][4], 64)
quote.PreviousClose, _ = strconv.ParseFloat(recs[1][7], 64)
avqCache.Add(symbol, quote, cache.DefaultExpiration)
}()
return quote, errors.New("not cached")
}
type gdaxQuote struct {
Volume string `json:"volume"`
PreviousClose float64 `json:"open,string"`
Last float64 `json:"last,string"`
}
// https://docs.pro.coinbase.com/
func cryptoQuote(symbol string) (quote gdaxQuote, err error) {
resp, herr := http.Get("https://api.pro.coinbase.com/products/" + symbol + "/stats")
if herr != nil {
return quote, herr
}
defer resp.Body.Close()
dec := json.NewDecoder(resp.Body)
derr := dec.Decode("e)
if derr != nil {
return quote, derr
}
if quote.Volume == "" {
return quote, errors.New("Unable to find data for symbol " + symbol)
}
return quote, nil
}
// Alpha Vantage allows 500 requests per day. Since we don't care about realtime
// values, we cache results for 24 hours.
var avdCache *cache.Cache = cache.New(time.Hour*24, time.Hour)
// https://www.alphavantage.co/documentation/#weeklyadj
func fundAnnualDividends(symbol string) (amount float64, err error) {
if div, found := avdCache.Get(symbol); found {
return div.(float64), nil
}
go func() {
avLimiter.Wait(context.Background())
yearAgo := time.Now().AddDate(-1, 0, 0).Format(time.DateOnly)
resp, herr := http.Get("https://www.alphavantage.co/query?function=TIME_SERIES_WEEKLY_ADJUSTED&datatype=csv&symbol=" + symbol + "&apikey=" + portfolioConfigData.AVToken)
if herr != nil {
return
}
defer resp.Body.Close()
cr := csv.NewReader(resp.Body)
recs, cerr := cr.ReadAll()
if cerr != nil {
return
}
divIdx := -1
if len(recs) < 2 {
return
}
for i := range recs[0] {
if strings.Contains(recs[0][i], "dividend") {
divIdx = i
}
}
if divIdx < 0 {
return
}
for _, rec := range recs[1:] {
if div, derr := strconv.ParseFloat(rec[divIdx], 64); rec[0] > yearAgo && derr == nil {
amount += div
}
}
avdCache.Add(symbol, amount, cache.DefaultExpiration)
}()
return amount, nil
}