Skip to content

Instantly share code, notes, and snippets.

@dim0xff
Created April 15, 2023 16:29
Show Gist options
  • Save dim0xff/7798ffa5d362215ab361bdd47f9f7391 to your computer and use it in GitHub Desktop.
Save dim0xff/7798ffa5d362215ab361bdd47f9f7391 to your computer and use it in GitHub Desktop.
yahoo-finance-proxy
/*
yahoo-finance-proxy - прокси получения котировок из Yahoo Finance для https://github.com/KonishchevDmitry/investments
Работает на 15 апр 2023
В config.yaml добавить
quotes:
custom_provider:
url: http://127.0.0.1:12345
Собрать и запустить
$ go mod init yahoo-finance-proxy
$ go build
$ ./yahoo-finance-proxy
*/
package main
import (
"encoding/json"
"flag"
"io"
"log"
"net/http"
"net/url"
)
const yahooUrl = "https://query1.finance.yahoo.com/v7/finance/quote"
func main() {
var l string
flag.StringVar(&l, "l", ":12345", "listen on (:12345)")
flag.Parse()
log.Printf("start quotes proxy on %s", l)
http.HandleFunc("/v1/quotes", QuotesHandler)
log.Fatal(http.ListenAndServe(l, nil))
}
type QuoteResult struct {
Symbol string `json:"symbol"`
Price string `json:"price"`
Currency string `json:"currency,omitempty"`
}
type QuoteResponse struct {
Quotes []*QuoteResult `json:"quotes"`
}
type YQuoteResponse struct {
QuoteResponse struct {
Error interface{} `json:"error"`
Result []struct {
Currency string `json:"currency"`
RegularMarketPrice struct {
Fmt string `json:"fmt"`
Raw float64 `json:"raw"`
} `json:"regularMarketPrice"`
Symbol string `json:"symbol"`
} `json:"result"`
} `json:"quoteResponse"`
}
func QuotesHandler(w http.ResponseWriter, req *http.Request) {
req.Body.Close()
s := req.URL.Query().Get("symbols")
if s == "" {
log.Printf("empty symbols: %s", req.URL.String())
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("empty symbols"))
return
}
u, err := url.Parse(yahooUrl)
if err != nil {
log.Printf("can't parse url '%s': %v", yahooUrl, err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
q := make(url.Values)
q.Set("symbols", s)
q.Set("formatted", "true")
q.Set("fields", "regularMarketPrice,marketCap")
u.RawQuery = q.Encode()
yReq, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
log.Printf("can't create request: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
yReq.Header.Set("Referer", "https://finance.yahoo.com/")
yReq.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.61 Safari/537.36")
client := http.Client{}
yRes, err := client.Do(yReq)
if err != nil {
log.Printf("can't do request: %v", err)
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(err.Error()))
return
}
yBody, err := io.ReadAll(yRes.Body)
if err != nil {
log.Printf("can't read response: %v", err)
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(err.Error()))
return
}
yRes.Body.Close()
var yQuotes YQuoteResponse
err = json.Unmarshal(yBody, &yQuotes)
if err != nil {
log.Printf("can't unmarshal response '%s': %v", string(yBody), err)
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(err.Error()))
return
}
if yQuotes.QuoteResponse.Error != nil {
log.Printf("can't yQuotes error: %v", yQuotes.QuoteResponse.Error)
w.WriteHeader(http.StatusBadGateway)
if e, ok := yQuotes.QuoteResponse.Error.(string); ok {
w.Write([]byte(e))
} else {
w.Write(yBody)
}
return
}
qRes := QuoteResponse{Quotes: []*QuoteResult{}}
for _, yq := range yQuotes.QuoteResponse.Result {
qRes.Quotes = append(qRes.Quotes, &QuoteResult{
Symbol: yq.Symbol,
Price: yq.RegularMarketPrice.Fmt,
Currency: yq.Currency,
})
}
rBody, err := json.Marshal(qRes)
if err != nil {
log.Printf("can't marshal qRes: %v", err)
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.WriteHeader(http.StatusOK)
w.Write(rBody)
log.Printf("OK, %d", len(yQuotes.QuoteResponse.Result))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment