# README
Golang Tiingo Client
Full-featured Tiingo client that offers CSV and JSON unmarshalling from Tiingo to Golang types.
Installation
go get github.com/the-trader-dev/tiin-go
Usage
Maximum flexibility is offered with three primary ways to use tiin-go:
- As a Tiingo frontend client
- As a url builder
- Steal the types
Tiingo Frontend Client
Using tiin-go as a frontend client for the Tiingo api is the simplest way to use this package. It offers a few advantages over just using the query building & type capabilities:
- Centralized rate limiting
- Automatic authentication
- Request logging
- Automatic response body lifecycle management
package main
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"os"
"time"
tiingo "github.com/the-trader-dev/tiin-go"
"golang.org/x/time/rate"
)
func main() {
ctx := context.Background()
// Initialize client
c := tiingo.NewClient(os.Getenv("YOUR_TIINGO_TOKEN"),
// You can optionally set a rate limiter, enable logging, and change the
// default http client
tiingo.WithLogger(slog.Default()),
tiingo.WithHttpClient(&http.Client{}),
tiingo.WithRateLimiter(rate.NewLimiter(10, 1)),
)
// Get typed data with whatever the Tiingo default params are
fiveMinuteCandles, err := c.IexHistory(ctx, "AAPL", nil)
if err != nil {
panic(err)
}
// Get typed data with custom params
oneMinuteCandles, err := c.IexHistory(ctx, "AAPL", &tiingo.IexHistoryParams{
StartDate: time.Date(2024, 01, 02, 14, 30, 0, 0, time.UTC),
EndDate: time.Date(2024, 01, 02, 15, 00, 0, 0, time.UTC),
ResampleFreq: tiingo.OneMin,
AfterHours: false,
ForceFill: true,
RespFormat: tiingo.CSV,
})
if err != nil {
panic(err)
}
// Or get the raw bytes and store/handle yourself
rawJsonBytes, err := c.IexHistoryRaw(ctx, "AAPL", &tiingo.IexHistoryParams{
StartDate: time.Date(2024, 01, 02, 14, 30, 0, 0, time.UTC),
EndDate: time.Date(2024, 01, 02, 15, 00, 0, 0, time.UTC),
ResampleFreq: tiingo.FifteenMin,
})
if err != nil {
panic(err)
}
var fifteenMinuteCandles []tiingo.IexPrice
if err = json.Unmarshal(rawJsonBytes, &fiveMinuteCandles); err != nil {
panic(err)
}
fmt.Println("One minute candles:", oneMinuteCandles)
fmt.Println("Five minute candles:", fiveMinuteCandles)
fmt.Println("Fifteen minute candles:", fifteenMinuteCandles)
}
URL Builder
Using tiin-go purely as a url builder offers the best balance of control & ease. You pass in the wanted parameters into the given url function and the valid corresponding url is returned. You then do the actual request however you want. Once the request is made, you can then unmarshal the response into one of the predefined types.
NOTE: no token query param is added, you must handle authentication yourself
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
tiingo "github.com/the-trader-dev/tiin-go"
)
func main() {
// Build url
url := tiingo.EodMetadataUrl("AAPL")
// Build request
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
panic(err)
}
// Add auth
req.Header.Set("Authorization", "Token {your_token}")
// Make request
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer func() {
if err = resp.Body.Close(); err != nil {
panic(err)
}
}()
// Read body
rawBytes, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
// Unmarshal
var metadata tiingo.EodMetadata
if err = json.Unmarshal(rawBytes, &metadata); err != nil {
panic(err)
}
fmt.Println("Apple's metadata:", metadata)
}
Steal the types
This usage pattern offers the most control. You handle the entire request lifecycle and simply steal the one-to-one matching Tiingo to Golang types for easier marshalling & unmarshalling.
package main
import (
"encoding/json"
"fmt"
"github.com/gocarina/gocsv"
tiingo "github.com/the-trader-dev/tiin-go"
)
func main() {
// Raw bytes from some endpoint you requested yourself
var jsonBytes []byte
var csvBytes []byte
// Unmarshal
var jsonPrices []tiingo.EodPrice
if err := json.Unmarshal(jsonBytes, &jsonPrices); err != nil {
panic(err)
}
var csvPrices []tiingo.EodPrice
if err := gocsv.UnmarshalBytes(csvBytes, &csvPrices); err != nil {
panic(err)
}
fmt.Println("prices from json:", jsonPrices)
fmt.Println("prices from csv:", csvPrices)
}
Types
All implemented endpoints have corresponding Golang types that allow for automatic marshalling and unmarshalling both with CSV & JSON responses.
JSON
Any package that knows how to read json struct tags should be able to marshal/unmarshal successfully. However, it has only been tested with the standard library encoding/json package.
err := json.Unmarshal(rawJsonBytes, &TiingoGolangType)
CSV
Any package that knows how to read csv struct tags should be able to marshal/unmarshal successfully. However, it has only been tested with & internally uses with gocsv.
The use of gocsv is not set in stone. I do not like having dependencies just to have them. The goal is to have custom csv marshalling/unmarshalling at some point, however gocsv is a good alternative until then. If gocsv is ever removed, it will not be a breaking api change & csv tags will still be kept to keep some level of backwards compatability.
err := gocsv.UnmarshaBytes(rawCsvBytes, &TiingoGolangType)
API Surface
Complete
The following Tiingo endpoints have been implemented:
- End-of-Day
- IEX
- Fundamentals
- Search
Incomplete
The following Tiingo endpoints are not yet implemented:
- Crypto
- Forex
- Fund Fees
- Dividends
- Splits
Contributions
Contributions are welcome!
All I ask is that if you implement any of the needed endpoints, follow the same pattern as the completed ones to keep the api consistent (url builder, client method, valid csv/json parsing)