Skip to content

Commit

Permalink
Merge pull request #6 from piquette/develop
Browse files Browse the repository at this point in the history
Adds options and futures quotes.
  • Loading branch information
ackleymi committed Jul 15, 2018
2 parents 9370edd + 0aa526e commit d2ada33
Show file tree
Hide file tree
Showing 23 changed files with 630 additions and 167 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ cache:

env:
global:
- FINANCE_MOCK_VERSION=0.0.4
- FINANCE_MOCK_VERSION=0.0.5

go:
- "1.9"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Status | Description | Source
[x] | ETF quote(s) | Yahoo finance
[x] | Mutual fund quote(s) | Yahoo finance
[x] | Historical quotes | Yahoo finance
[x] | Options straddles | Yahoo finance
[ ] | Options chains | Yahoo finance
[ ] | Symbols list | BATS

Expand Down
98 changes: 58 additions & 40 deletions history/client.go → chart/client.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package history
package chart

import (
"context"

finance "github.com/piquette/finance-go"
"github.com/piquette/finance-go/datetime"
form "github.com/piquette/finance-go/form"
"github.com/piquette/finance-go/iter"
"github.com/shopspring/decimal"
)

// Client is used to invoke quote APIs.
// Client is used to invoke chart APIs.
type Client struct {
B finance.Backend
}
Expand All @@ -23,87 +25,85 @@ type Params struct {
finance.Params `form:"-"`

// Accessible fields.
Symbol string `form:"-"`
Start *Datetime `form:"-"`
End *Datetime `form:"-"`
Interval Interval `form:"-"`
Symbol string `form:"-"`
Start *datetime.Datetime `form:"-"`
End *datetime.Datetime `form:"-"`
Interval datetime.Interval `form:"-"`

IncludeExt bool `form:"includePrePost"`

// Internal request fields.
interval string `form:"interval"`
start int `form:"period1"`
end int `form:"period2"`
region string `form:"region"`
domain string `form:"corsDomain"`
}

// Chart is a structure containing results
// Iter is a structure containing results
// and related metadata for a
// yfin chart request.
type Chart struct {
*finance.Iter
type Iter struct {
*iter.Iter
}

// Bar returns the next Bar
// visited by a call to Next.
func (ch *Chart) Bar() *finance.ChartBar {
return ch.Current().(*finance.ChartBar)
func (i *Iter) Bar() *finance.ChartBar {
return i.Current().(*finance.ChartBar)
}

// Meta returns the chart metadata
// related to a chart response.
func (ch *Chart) Meta() *finance.ChartMeta {
return ch.Meta()
func (i *Iter) Meta() *finance.ChartMeta {
return i.Iter.Meta().(*finance.ChartMeta)
}

// Get returns a historical chart.
// and requires a params
// struct as an argument.
func Get(params *Params) *Chart {
func Get(params *Params) *Iter {
return getC().Get(params)
}

// Get returns a historical chart.
func (c Client) Get(params *Params) *Chart {

if params.Context == nil {
ctx := context.TODO()
params.Context = &ctx
}
func (c Client) Get(params *Params) *Iter {

// Construct request from params input.
// TODO: validate symbol..
if params == nil || len(params.Symbol) == 0 {
return &Chart{finance.GetErrIter(finance.CreateArgumentError())}
return &Iter{iter.NewE(finance.CreateArgumentError())}
}

// Start and End times.
if params.Context == nil {
ctx := context.TODO()
params.Context = &ctx
}

// Start and End times
params.start = -1
params.end = -1
if params.Start != nil {
params.start = params.Start.ToUnix()
params.start = params.Start.Unix()
}
if params.End != nil {
params.end = params.End.ToUnix()
params.end = params.End.Unix()
}
if params.start > params.end {
return &Chart{finance.GetErrIter(finance.CreateChartTimeError())}
return &Iter{iter.NewE(finance.CreateChartTimeError())}
}

// Parse interval.
if params.Interval != "" {
params.interval = string(params.Interval)
}

// Set meta data.
params.domain = "com.finance.yahoo"
params.region = "US"

// Build request.
body := &form.Values{}
form.AppendTo(body, params)
// Set request meta data.
body.Set("region", "US")
body.Set("corsDomain", "com.finance.yahoo")

return &Chart{finance.GetChartIter(body, func(b *form.Values) (m interface{}, bars []interface{}, err error) {
return &Iter{iter.New(body, func(b *form.Values) (m interface{}, bars []interface{}, err error) {

resp := response{}
err = c.B.Call("v8/finance/chart/"+params.Symbol, body, params.Context, &resp)
Expand All @@ -116,22 +116,22 @@ func (c Client) Get(params *Params) *Chart {
return
}

chartResp := resp.Inner.Result[0]
if chartResp == nil || chartResp.Indicators == nil {
result := resp.Inner.Results[0]
if result == nil || result.Indicators == nil {
err = finance.CreateRemoteErrorS("no results in chart response")
return
}

barQuotes := chartResp.Indicators.Quote
barQuotes := result.Indicators.Quote
if barQuotes == nil || barQuotes[0] == nil {
err = finance.CreateRemoteErrorS("no results in chart response")
return
}
adjCloses := chartResp.Indicators.Adjclose
adjCloses := result.Indicators.Adjclose

// Process chart response
// and chart meta data.
for i, t := range chartResp.Timestamp {
for i, t := range result.Timestamp {

b := &finance.ChartBar{
Timestamp: t,
Expand All @@ -149,14 +149,32 @@ func (c Client) Get(params *Params) *Chart {
bars = append(bars, b)
}

return chartResp.Meta, bars, nil
return result.Meta, bars, nil
})}
}

// response is a yfin chart response.
type response struct {
Inner struct {
Result []*finance.ChartResponse `json:"result"`
Error *finance.YfinError `json:"error"`
Results []*result `json:"result"`
Error *finance.YfinError `json:"error"`
} `json:"chart"`
}

// result is an umbrella object for chart results.
type result struct {
Meta finance.ChartMeta `json:"meta"`
Timestamp []int `json:"timestamp"`
Indicators *struct {
Quote []*struct {
Open []float64 `json:"open"`
Low []float64 `json:"low"`
High []float64 `json:"high"`
Close []float64 `json:"close"`
Volume []int `json:"volume"`
} `json:"quote"`
Adjclose []*struct {
Adjclose []float64 `json:"adjclose"`
} `json:"adjclose"`
} `json:"indicators"`
}
18 changes: 9 additions & 9 deletions history/client_test.go → chart/client_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package history
package chart

import (
"testing"
Expand All @@ -9,26 +9,26 @@ import (

func TestGetEquityChart(t *testing.T) {
p := &Params{Symbol: tests.TestEquitySymbol}
chart := Get(p)
assert.True(t, chart.Next())
iter := Get(p)
assert.True(t, iter.Next())
}

func TestGetETFChart(t *testing.T) {
p := &Params{Symbol: tests.TestETFSymbol}
chart := Get(p)
assert.True(t, chart.Next())
iter := Get(p)
assert.True(t, iter.Next())
}

func TestGetFutureChart(t *testing.T) {
p := &Params{Symbol: tests.TestFutureSymbol}
chart := Get(p)
assert.True(t, chart.Next())
iter := Get(p)
assert.True(t, iter.Next())
}

func TestGetIndexChart(t *testing.T) {
p := &Params{Symbol: tests.TestIndexSymbol}
chart := Get(p)
assert.True(t, chart.Next())
iter := Get(p)
assert.True(t, iter.Next())
}

func TestGetOptionChart(t *testing.T) {
Expand Down
9 changes: 5 additions & 4 deletions crypto/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

finance "github.com/piquette/finance-go"
form "github.com/piquette/finance-go/form"
"github.com/piquette/finance-go/iter"
)

// Client is used to invoke quote APIs.
Expand All @@ -30,7 +31,7 @@ type Params struct {
// The embedded Iter carries methods with it;
// see its documentation for details.
type Iter struct {
*finance.Iter
*iter.Iter
}

// CryptoPair returns the most recent CryptoPair
Expand Down Expand Up @@ -72,14 +73,14 @@ func (c Client) ListP(params *Params) *Iter {
// Validate input.
// TODO: validate symbols..
if params == nil || len(params.Symbols) == 0 {
return &Iter{finance.GetErrIter(finance.CreateArgumentError())}
return &Iter{iter.NewE(finance.CreateArgumentError())}
}
params.sym = strings.Join(params.Symbols, ",")

body := &form.Values{}
form.AppendTo(body, params)

return &Iter{finance.GetIter(body, func(b *form.Values) ([]interface{}, error) {
return &Iter{iter.New(body, func(b *form.Values) (interface{}, []interface{}, error) {

resp := response{}
err := c.B.Call("/v7/finance/quote", body, params.Context, &resp)
Expand All @@ -95,7 +96,7 @@ func (c Client) ListP(params *Params) *Iter {
err = finance.CreateRemoteError(resp.Inner.Error)
}

return ret, err
return nil, ret, err
})}
}

Expand Down
30 changes: 14 additions & 16 deletions history/time.go → datetime/datetime.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package history
package datetime

import (
"time"
Expand Down Expand Up @@ -58,18 +58,24 @@ type Datetime struct {
t *time.Time
}

// NewDatetime creates a new instance of Datetime.
func NewDatetime(t time.Time) *Datetime {
// New creates a new instance of Datetime from a go time struct.
func New(t *time.Time) *Datetime {
year, month, day := t.Date()
return &Datetime{
Month: int(month),
Day: day,
Year: year,
t: &t,
t: t,
}
}

// Time returns a time object from a datetime.
// FromUnix returns a new instance of Datetime from a unix timestamp.
func FromUnix(timestamp int) *Datetime {
t := time.Unix(int64(timestamp), 0)
return New(&t)
}

// Time returns a go time struct from a datetime.
func (d *Datetime) Time() *time.Time {
if d.t != nil {
return d.t
Expand All @@ -78,22 +84,14 @@ func (d *Datetime) Time() *time.Time {
return d.Time()
}

// ToUnix converts a Datetime struct to
// a valid unix timestamp.
func (d *Datetime) ToUnix() int {
// Unix returns a valid unix timestamp from Datetime fields.
func (d *Datetime) Unix() int {
if d.t != nil {
return int(d.t.Unix())
}

d.calculateTime()
return d.ToUnix()
}

// NewDatetimeU converts a valid unix timestamp
// to a datetime object.
func NewDatetimeU(timestamp int) *Datetime {
t := time.Unix(int64(timestamp), 0)
return NewDatetime(t)
return d.Unix()
}

func (d *Datetime) calculateTime() {
Expand Down

0 comments on commit d2ada33

Please sign in to comment.