From 76a3cdd191145ef1628e3bd08c1bbcbf0bb13ea9 Mon Sep 17 00:00:00 2001 From: Felipe Marinho Date: Tue, 30 Aug 2022 02:21:55 +0000 Subject: [PATCH] new: feat: add b3 lambda --- README.md | 29 ++++++++ api/b3/prices/[ticker].go | 143 ++++++++++++++++++++++++++++++++++++++ go.mod | 4 ++ go.sum | 15 ++++ 4 files changed, 191 insertions(+) create mode 100644 README.md create mode 100644 api/b3/prices/[ticker].go create mode 100644 go.sum diff --git a/README.md b/README.md new file mode 100644 index 0000000..16e30b5 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# vercel-lambdas + +This is a collection of lambdas for personal use powered by [vercel](https://vercel.com/). + +## statusinvest + +The path `statusinvest` contains lambdas that fetches the status of a company (fundamentalist analysis) from [statusinvest.com](https://statusinvest.com/). + +## b3 + +The path `b3` contains lambdas that fetches stock prices data directly from from [b3.com](https://b3.com/). + +- `b3/prices/:symbol` [Test it!](https://vercel-lambdas-felipemarinho.vercel.app/api/b3/prices/IBOV) + - Returns the stock prices for a given symbol. + - Example Response: + ```json + { + "symbol": "IBOV", + "name": "IBOVESPA", + "market": "Indices", + "openingPrice": 112295.87, + "minPrice": 111689.15, + "maxPrice": 113221.54, + "averagePrice": 112702.99, + "currentPrice": 112323.12, + "priceVariation": 0.02, + "indexComponentIndicator": false + } + ``` diff --git a/api/b3/prices/[ticker].go b/api/b3/prices/[ticker].go new file mode 100644 index 0000000..3c8648d --- /dev/null +++ b/api/b3/prices/[ticker].go @@ -0,0 +1,143 @@ +package quotation + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + + log "github.com/sirupsen/logrus" +) + +type RawResponse struct { + BizSts struct { + Cd string `json:"cd"` + } `json:"BizSts"` + Msg struct { + DtTm string `json:"dtTm"` + } `json:"Msg"` + Trad []struct { + Scty struct { + SctyQtn struct { + OpngPric float64 `json:"opngPric"` + MinPric float64 `json:"minPric"` + MaxPric float64 `json:"maxPric"` + AvrgPric float64 `json:"avrgPric"` + CurPrc float64 `json:"curPrc"` + PrcFlcn float64 `json:"prcFlcn"` + } `json:"SctyQtn"` + Mkt struct { + Nm string `json:"nm"` + } `json:"mkt"` + Symb string `json:"symb"` + Desc string `json:"desc"` + IndxCmpnInd bool `json:"indxCmpnInd"` + } `json:"scty"` + TTLQty int `json:"ttlQty"` + } `json:"Trad"` +} + +type Response struct { + Symbol string `json:"symbol"` + Name string `json:"name"` + Market string `json:"market"` + OpeningPrice float64 `json:"openingPrice"` + MinPrice float64 `json:"minPrice"` + MaxPrice float64 `json:"maxPrice"` + AveragePrice float64 `json:"averagePrice"` + CurrentPrice float64 `json:"currentPrice"` + PriceVariation float64 `json:"priceVariation"` + IndexComponentIndicator bool `json:"indexComponentIndicator"` +} + +type RawErrorResponse struct { + BizSts struct { + Cd string `json:"cd"` + Desc string `json:"desc"` + } `json:"BizSts"` + Msg struct { + DtTm string `json:"dtTm"` + } `json:"Msg"` +} + +type Error struct { + Message string `json:"message"` +} + +func HandlerListCompanies(w http.ResponseWriter, r *http.Request) { + ticker := strings.Split(r.URL.Path, "/")[4] + log.Info("Getting quotation info for ticker: " + ticker) + if ticker == "" { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("Ticker is required")) + return + } + + client := http.Client{} + res, err := client.Get(fmt.Sprintf("https://cotacao.b3.com.br/mds/api/v1/instrumentQuotation/%s", ticker)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + defer res.Body.Close() + w.Header().Set("Content-Type", "application/json") + // add 1min cache header + w.Header().Set("Cache-Control", "max-age=60, public") + + out, err := ioutil.ReadAll(res.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + var raw RawResponse + err = json.Unmarshal(out, &raw) + if err != nil || raw.BizSts.Cd != "OK" { + var errorResponse RawErrorResponse + err = json.Unmarshal(out, &errorResponse) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + + // 404 + w.WriteHeader(http.StatusNotFound) + formatedError := Error{Message: errorResponse.BizSts.Desc} + err := json.NewEncoder(w).Encode(formatedError) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + return + } + + var response []Response + for _, trad := range raw.Trad { + response = append(response, Response{ + Symbol: trad.Scty.Symb, + Name: trad.Scty.Desc, + Market: trad.Scty.Mkt.Nm, + OpeningPrice: trad.Scty.SctyQtn.OpngPric, + MinPrice: trad.Scty.SctyQtn.MinPric, + MaxPrice: trad.Scty.SctyQtn.MaxPric, + AveragePrice: trad.Scty.SctyQtn.AvrgPric, + CurrentPrice: trad.Scty.SctyQtn.CurPrc, + PriceVariation: trad.Scty.SctyQtn.PrcFlcn, + IndexComponentIndicator: trad.Scty.IndxCmpnInd, + }) + } + + err = json.NewEncoder(w).Encode(response[0]) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) + return + } + return +} diff --git a/go.mod b/go.mod index d6a9e50..8a79f16 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,7 @@ module github.com/felipemarinho97/vercel-lambdas go 1.17 + +require github.com/sirupsen/logrus v1.9.0 + +require golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ed65537 --- /dev/null +++ b/go.sum @@ -0,0 +1,15 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=