Skip to content

Commit

Permalink
Fetch quotes from alphavantage and store on db
Browse files Browse the repository at this point in the history
  • Loading branch information
dude333 committed Apr 23, 2021
1 parent b5fa9ef commit e845ed3
Show file tree
Hide file tree
Showing 8 changed files with 319 additions and 62 deletions.
53 changes: 0 additions & 53 deletions cmd/fii.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ THE SOFTWARE.
package cmd

import (
"database/sql"
"fmt"
"strings"

"github.com/dude333/rapina/parsers"
"github.com/spf13/cobra"
)

Expand All @@ -44,51 +39,3 @@ func init() {
rootCmd.AddCommand(fiiCmd)
fiiCmd.PersistentFlags().IntP("num", "n", 1, "número de meses desde o último disponível")
}

//
// FIIDividends prints the dividends from 'code' fund for 'n' months,
// starting from latest.
//
func FIIDividends(code string, n int) error {
db, err := openDatabase()
if err != nil {
return err
}
code = strings.ToUpper(code)

fii, _ := parsers.NewFII(db)

cnpj, err := cnpj(fii, code)
if err != nil {
return err
}

err = fii.FetchFIIDividends(cnpj, n)

return err
}

//
// cnpj returns the CNPJ from FII code. It first checks the DB and, if not
// found, fetches from B3.
//
func cnpj(fii *parsers.FII, code string) (string, error) {
fiiDetails, err := fii.SelectFIIDetails(code)
if err != nil && err != sql.ErrNoRows {
fmt.Println("[x] error", err)
}
if err == nil && fiiDetails.DetailFund.CNPJ != "" {
fmt.Println("DB", code, fiiDetails.DetailFund.CNPJ)
return fiiDetails.DetailFund.CNPJ, nil
}
//
// Fetch online if DB fails
fiiDetails, err = fii.FetchFIIDetails(code)
if err != nil {
return "", err
}

fmt.Println("online", code, fiiDetails.DetailFund.CNPJ)

return fiiDetails.DetailFund.CNPJ, nil
}
72 changes: 72 additions & 0 deletions cmd/fii_dividends.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@ THE SOFTWARE.
package cmd

import (
"database/sql"
"fmt"
"log"
"strings"

"github.com/dude333/rapina/fetch"
"github.com/dude333/rapina/parsers"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

// fiiDividendsCmd represents the rendimentos command
Expand All @@ -51,3 +57,69 @@ var fiiDividendsCmd = &cobra.Command{
func init() {
fiiCmd.AddCommand(fiiDividendsCmd)
}

//
// FIIDividends prints the dividends from 'code' fund for 'n' months,
// starting from latest.
//
func FIIDividends(code string, n int) error {
db, err := openDatabase()
if err != nil {
return err
}
code = strings.ToUpper(code)

srv, err := fetch.NewStockServer(db, viper.GetString("apikey"))
if err != nil {
return err
}
err = srv.FetchStockQuote(fix(code))
if err != nil {
return err
}

fii, _ := parsers.NewFII(db, srv.QuoteFromDB)

cnpj, err := cnpj(fii, code)
if err != nil {
return err
}
err = fii.FetchFIIDividends(cnpj, n)
if err != nil {
return err
}

return nil
}

//
// cnpj returns the CNPJ from FII code. It first checks the DB and, if not
// found, fetches from B3.
//
func cnpj(fii *parsers.FII, code string) (string, error) {
fiiDetails, err := fii.SelectFIIDetails(code)
if err != nil && err != sql.ErrNoRows {
fmt.Println("[x] error", err)
}
if err == nil && fiiDetails.DetailFund.CNPJ != "" {
fmt.Println("DB", code, fiiDetails.DetailFund.CNPJ)
return fiiDetails.DetailFund.CNPJ, nil
}
//
// Fetch online if DB fails
fiiDetails, err = fii.FetchFIIDetails(code)
if err != nil {
return "", err
}

fmt.Println("online", code, fiiDetails.DetailFund.CNPJ)

return fiiDetails.DetailFund.CNPJ, nil
}

func fix(code string) string {
if len(code) == 4 {
return code + "11"
}
return code
}
7 changes: 4 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,16 @@ func initConfig() {
os.Exit(1)
}

// Search config in home directory with name ".cli" (without extension).
// Search config in home directory with name ".rapina" (without extension).
viper.AddConfigPath(home)
viper.SetConfigName(".cli")
viper.AddConfigPath(".")
viper.SetConfigName("config")
}

viper.AutomaticEnv() // read in environment variables that match

// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
fmt.Println("Usando arquivo de configuração:", viper.ConfigFileUsed())
}
}
77 changes: 77 additions & 0 deletions fetch/fetch_stockquote.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package fetch

import (
"crypto/tls"
"database/sql"
"fmt"
"net/http"
"net/url"
"time"

"github.com/dude333/rapina/parsers"
)

const apiServer = "https://www.alphavantage.co/"

type StockServer struct {
apiKey string
db *sql.DB
}

//
// NewStockServer returns a new instance of *StockServer
//
func NewStockServer(db *sql.DB, apiKey string) (*StockServer, error) {
if db == nil {
return nil, fmt.Errorf("invalid DB")
}
if apiKey == "" {
return nil, fmt.Errorf("invalid API key: '%s'", apiKey)
}
s := StockServer{
apiKey: apiKey,
db: db,
}
return &s, nil
}

//
// FetchStockQuote fetches the daily time series (date, daily open, daily high,
// daily low, daily close, daily volume) of the global equity specified,
// covering 20+ years of historical data.
//
func (s StockServer) FetchStockQuote(code string) error {
tr := &http.Transport{
DisableCompression: true,
IdleConnTimeout: 30 * time.Second,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}

v := url.Values{}
v.Set("function", "TIME_SERIES_DAILY")
v.Add("symbol", code+".SA")
v.Add("apikey", s.apiKey)
v.Add("outputsize", "compact")
v.Add("datatype", "csv")

u := parsers.JoinURL(apiServer, "query?"+v.Encode())

fmt.Println(u)

resp, err := client.Get(u)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("%s: %s", resp.Status, u)
}

return parsers.StockCsv(s.db, resp.Body, code)
}

func (s StockServer) QuoteFromDB(code, date string) (float64, error) {
return 9.99, nil
}
10 changes: 7 additions & 3 deletions parsers/fii.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,19 @@ import (
"github.com/pkg/errors"
)

type QuoteFn func(code, date string) (float64, error)

// FII holds the infrastructure data.
type FII struct {
db *sql.DB
db *sql.DB
quotefn QuoteFn
}

// NewFII creates a new instace of FII.
func NewFII(db *sql.DB) (*FII, error) {
func NewFII(db *sql.DB, quotefn QuoteFn) (*FII, error) {
fii := &FII{
db: db, // will accept null db when caching is no needed
db: db, // will accept null db when caching is no needed
quotefn: quotefn,
}
return fii, nil
}
Expand Down
2 changes: 1 addition & 1 deletion parsers/md5_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func TestIsNewFile(t *testing.T) {

func openDatabase() (db *sql.DB, err error) {

db, err = sql.Open("sqlite3", "../cli/.data/rapina.db")
db, err = sql.Open("sqlite3", "../bin/.data/rapina.db")
if err != nil {
return db, errors.Wrap(err, "database open failed")
}
Expand Down
Loading

0 comments on commit e845ed3

Please sign in to comment.