Categorygithub.com/the-trader-dev/tiin-go
repositorypackage
0.1.1
Repository: https://github.com/the-trader-dev/tiin-go.git
Documentation: pkg.go.dev

# 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:

  1. As a Tiingo frontend client
  2. As a url builder
  3. 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:

  1. Centralized rate limiting
  2. Automatic authentication
  3. Request logging
  4. 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)