This commit is contained in:
Will Charczuk 2017-04-29 13:19:17 -07:00
parent 65a0895143
commit 376a8efa36
15 changed files with 159 additions and 21 deletions

View file

@ -1,4 +1,4 @@
package chart package util
import ( import (
"sync" "sync"

View file

@ -1,6 +1,6 @@
// +build !windows // +build !windows
package chart package util
import "time" import "time"

View file

@ -1,4 +1,4 @@
package chart package util
import ( import (
"testing" "testing"

View file

@ -1,6 +1,6 @@
// +build windows // +build windows
package chart package util
import "time" import "time"

View file

@ -1,4 +1,4 @@
package chart package util
import ( import (
"bufio" "bufio"

View file

@ -1,4 +1,4 @@
package chart package util
import ( import (
"math/rand" "math/rand"
@ -17,8 +17,8 @@ type generate struct {
rnd *rand.Rand rnd *rand.Rand
} }
// Float64 produces an array of floats from [start,end] by optional steps. // Values produces an array of floats from [start,end] by optional steps.
func (g generate) Float64(start, end float64, steps ...float64) []float64 { func (g generate) Values(start, end float64, steps ...float64) Sequence {
var values []float64 var values []float64
step := 1.0 step := 1.0
if len(steps) > 0 { if len(steps) > 0 {
@ -34,22 +34,22 @@ func (g generate) Float64(start, end float64, steps ...float64) []float64 {
values = append(values, x) values = append(values, x)
} }
} }
return values return Sequence{Array(values)}
} }
// Random generates a fixed length sequence of random values between (0, scale). // Random generates a fixed length sequence of random values between (0, scale).
func (g generate) Random(samples int, scale float64) []float64 { func (g generate) RandomValues(samples int, scale float64) Sequence {
values := make([]float64, samples) values := make([]float64, samples)
for x := 0; x < samples; x++ { for x := 0; x < samples; x++ {
values[x] = g.rnd.Float64() * scale values[x] = g.rnd.Float64() * scale
} }
return values return Sequence{Array(values)}
} }
// Random generates a fixed length sequence of random values with a given average, above and below that average by (-scale, scale) // Random generates a fixed length sequence of random values with a given average, above and below that average by (-scale, scale)
func (g generate) RandomWithAverage(samples int, average, scale float64) []float64 { func (g generate) RandomValuesWithAverage(samples int, average, scale float64) Sequence {
values := make([]float64, samples) values := make([]float64, samples)
for x := 0; x < samples; x++ { for x := 0; x < samples; x++ {
@ -57,7 +57,7 @@ func (g generate) RandomWithAverage(samples int, average, scale float64) []float
values[x] = average + jitter values[x] = average + jitter
} }
return values return Sequence{Array(values)}
} }
// Days generates a sequence of timestamps by day, from -days to today. // Days generates a sequence of timestamps by day, from -days to today.

View file

@ -1,4 +1,4 @@
package chart package util
import ( import (
"testing" "testing"

View file

@ -1,4 +1,4 @@
package chart package util
import ( import (
"math" "math"

View file

@ -1,4 +1,4 @@
package chart package util
import ( import (
"testing" "testing"

View file

@ -1,4 +1,4 @@
package chart package util
import "math" import "math"

1
util/sequence_test.go Normal file
View file

@ -0,0 +1 @@
package util

View file

@ -1,4 +1,4 @@
package chart package util
import "time" import "time"
@ -17,4 +17,4 @@ func (tu timeUtil) ToFloat64(t time.Time) float64 {
// Float64ToTime returns a time from a float64. // Float64ToTime returns a time from a float64.
func (tu timeUtil) FromFloat64(tf float64) time.Time { func (tu timeUtil) FromFloat64(tf float64) time.Time {
return time.Unix(0, int64(tf)) return time.Unix(0, int64(tf))
} }

137
util/time_series.go Normal file
View file

@ -0,0 +1,137 @@
package util
import "time"
// TimeSeries is a utility singleton with helper functions for time series generation.
var TimeSeries timeSeries
type timeSeries struct{}
// Days generates a sequence of timestamps by day, from -days to today.
func (ts timeSeries) Days(days int) []time.Time {
var values []time.Time
for day := days; day >= 0; day-- {
values = append(values, time.Now().AddDate(0, 0, -day))
}
return values
}
func (ts timeSeries) MarketHours(from, to time.Time, marketOpen, marketClose time.Time, isHoliday HolidayProvider) []time.Time {
var times []time.Time
cursor := Date.On(marketOpen, from)
toClose := Date.On(marketClose, to)
for cursor.Before(toClose) || cursor.Equal(toClose) {
todayOpen := Date.On(marketOpen, cursor)
todayClose := Date.On(marketClose, cursor)
isValidTradingDay := !isHoliday(cursor) && Date.IsWeekDay(cursor.Weekday())
if (cursor.Equal(todayOpen) || cursor.After(todayOpen)) && (cursor.Equal(todayClose) || cursor.Before(todayClose)) && isValidTradingDay {
times = append(times, cursor)
}
if cursor.After(todayClose) {
cursor = Date.NextMarketOpen(cursor, marketOpen, isHoliday)
} else {
cursor = Date.NextHour(cursor)
}
}
return times
}
func (ts timeSeries) MarketHourQuarters(from, to time.Time, marketOpen, marketClose time.Time, isHoliday HolidayProvider) []time.Time {
var times []time.Time
cursor := Date.On(marketOpen, from)
toClose := Date.On(marketClose, to)
for cursor.Before(toClose) || cursor.Equal(toClose) {
isValidTradingDay := !isHoliday(cursor) && Date.IsWeekDay(cursor.Weekday())
if isValidTradingDay {
todayOpen := Date.On(marketOpen, cursor)
todayNoon := Date.NoonOn(cursor)
today2pm := Date.On(Date.Time(14, 0, 0, 0, cursor.Location()), cursor)
todayClose := Date.On(marketClose, cursor)
times = append(times, todayOpen, todayNoon, today2pm, todayClose)
}
cursor = Date.NextDay(cursor)
}
return times
}
func (ts timeSeries) MarketDayCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday HolidayProvider) []time.Time {
var times []time.Time
cursor := Date.On(marketOpen, from)
toClose := Date.On(marketClose, to)
for cursor.Before(toClose) || cursor.Equal(toClose) {
isValidTradingDay := !isHoliday(cursor) && Date.IsWeekDay(cursor.Weekday())
if isValidTradingDay {
todayClose := Date.On(marketClose, cursor)
times = append(times, todayClose)
}
cursor = Date.NextDay(cursor)
}
return times
}
func (ts timeSeries) MarketDayAlternateCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday HolidayProvider) []time.Time {
var times []time.Time
cursor := Date.On(marketOpen, from)
toClose := Date.On(marketClose, to)
for cursor.Before(toClose) || cursor.Equal(toClose) {
isValidTradingDay := !isHoliday(cursor) && Date.IsWeekDay(cursor.Weekday())
if isValidTradingDay {
todayClose := Date.On(marketClose, cursor)
times = append(times, todayClose)
}
cursor = cursor.AddDate(0, 0, 2)
}
return times
}
func (ts timeSeries) MarketDayMondayCloses(from, to time.Time, marketOpen, marketClose time.Time, isHoliday HolidayProvider) []time.Time {
var times []time.Time
cursor := Date.On(marketClose, from)
toClose := Date.On(marketClose, to)
for cursor.Equal(toClose) || cursor.Before(toClose) {
isValidTradingDay := !isHoliday(cursor) && Date.IsWeekDay(cursor.Weekday())
if isValidTradingDay {
times = append(times, cursor)
}
cursor = Date.NextDayOfWeek(cursor, time.Monday)
}
return times
}
func (ts timeSeries) Hours(start time.Time, totalHours int) []time.Time {
times := make([]time.Time, totalHours)
last := start
for i := 0; i < totalHours; i++ {
times[i] = last
last = last.Add(time.Hour)
}
return times
}
// HoursFilled adds zero values for the data bounded by the start and end of the xdata array.
func (ts timeSeries) HoursFilled(xdata []time.Time, ydata []float64) ([]time.Time, []float64) {
start := Date.Start(xdata)
end := Date.End(xdata)
totalHours := Math.AbsInt(Date.DiffHours(start, end))
finalTimes := ts.Hours(start, totalHours+1)
finalValues := make([]float64, totalHours+1)
var hoursFromStart int
for i, xd := range xdata {
hoursFromStart = Date.DiffHours(start, xd)
finalValues[hoursFromStart] = ydata[i]
}
return finalTimes, finalValues
}

View file

@ -1,4 +1,4 @@
package chart package util
import ( import (
"fmt" "fmt"

View file

@ -1,4 +1,4 @@
package chart package util
import ( import (
"testing" "testing"