diff --git a/date.go b/util/date.go similarity index 99% rename from date.go rename to util/date.go index 21db081..9cb13ec 100644 --- a/date.go +++ b/util/date.go @@ -1,4 +1,4 @@ -package chart +package util import ( "sync" diff --git a/date_posix.go b/util/date_posix.go similarity index 95% rename from date_posix.go rename to util/date_posix.go index 2ab23fe..1a5a80c 100644 --- a/date_posix.go +++ b/util/date_posix.go @@ -1,6 +1,6 @@ // +build !windows -package chart +package util import "time" diff --git a/date_test.go b/util/date_test.go similarity index 99% rename from date_test.go rename to util/date_test.go index cb1602c..9b9547e 100644 --- a/date_test.go +++ b/util/date_test.go @@ -1,4 +1,4 @@ -package chart +package util import ( "testing" diff --git a/date_windows.go b/util/date_windows.go similarity index 95% rename from date_windows.go rename to util/date_windows.go index fdd3173..c42a367 100644 --- a/date_windows.go +++ b/util/date_windows.go @@ -1,6 +1,6 @@ // +build windows -package chart +package util import "time" diff --git a/file_util.go b/util/file_util.go similarity index 98% rename from file_util.go rename to util/file_util.go index bdf7579..d6c561d 100644 --- a/file_util.go +++ b/util/file_util.go @@ -1,4 +1,4 @@ -package chart +package util import ( "bufio" diff --git a/generate.go b/util/generate.go similarity index 92% rename from generate.go rename to util/generate.go index 887fb9d..2615e70 100644 --- a/generate.go +++ b/util/generate.go @@ -1,4 +1,4 @@ -package chart +package util import ( "math/rand" @@ -17,8 +17,8 @@ type generate struct { rnd *rand.Rand } -// Float64 produces an array of floats from [start,end] by optional steps. -func (g generate) Float64(start, end float64, steps ...float64) []float64 { +// Values produces an array of floats from [start,end] by optional steps. +func (g generate) Values(start, end float64, steps ...float64) Sequence { var values []float64 step := 1.0 if len(steps) > 0 { @@ -34,22 +34,22 @@ func (g generate) Float64(start, end float64, steps ...float64) []float64 { values = append(values, x) } } - return values + return Sequence{Array(values)} } // 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) for x := 0; x < samples; x++ { 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) -func (g generate) RandomWithAverage(samples int, average, scale float64) []float64 { +func (g generate) RandomValuesWithAverage(samples int, average, scale float64) Sequence { values := make([]float64, samples) for x := 0; x < samples; x++ { @@ -57,7 +57,7 @@ func (g generate) RandomWithAverage(samples int, average, scale float64) []float values[x] = average + jitter } - return values + return Sequence{Array(values)} } // Days generates a sequence of timestamps by day, from -days to today. diff --git a/generate_test.go b/util/generate_test.go similarity index 99% rename from generate_test.go rename to util/generate_test.go index b062cd3..5a1eea4 100644 --- a/generate_test.go +++ b/util/generate_test.go @@ -1,4 +1,4 @@ -package chart +package util import ( "testing" diff --git a/math.go b/util/math.go similarity index 99% rename from math.go rename to util/math.go index 43ff428..0e588f0 100644 --- a/math.go +++ b/util/math.go @@ -1,4 +1,4 @@ -package chart +package util import ( "math" diff --git a/math_test.go b/util/math_test.go similarity index 99% rename from math_test.go rename to util/math_test.go index c629af4..af6750a 100644 --- a/math_test.go +++ b/util/math_test.go @@ -1,4 +1,4 @@ -package chart +package util import ( "testing" diff --git a/sequence.go b/util/sequence.go similarity index 99% rename from sequence.go rename to util/sequence.go index 5d6d839..fdc5e71 100644 --- a/sequence.go +++ b/util/sequence.go @@ -1,4 +1,4 @@ -package chart +package util import "math" diff --git a/util/sequence_test.go b/util/sequence_test.go new file mode 100644 index 0000000..c7d8682 --- /dev/null +++ b/util/sequence_test.go @@ -0,0 +1 @@ +package util diff --git a/time_util.go b/util/time.go similarity index 96% rename from time_util.go rename to util/time.go index 4e81743..88e0c8b 100644 --- a/time_util.go +++ b/util/time.go @@ -1,4 +1,4 @@ -package chart +package util import "time" @@ -17,4 +17,4 @@ func (tu timeUtil) ToFloat64(t time.Time) float64 { // Float64ToTime returns a time from a float64. func (tu timeUtil) FromFloat64(tf float64) time.Time { return time.Unix(0, int64(tf)) -} \ No newline at end of file +} diff --git a/util/time_series.go b/util/time_series.go new file mode 100644 index 0000000..d01bcc5 --- /dev/null +++ b/util/time_series.go @@ -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 +} diff --git a/value_buffer.go b/util/value_buffer.go similarity index 99% rename from value_buffer.go rename to util/value_buffer.go index 0685940..c4d856c 100644 --- a/value_buffer.go +++ b/util/value_buffer.go @@ -1,4 +1,4 @@ -package chart +package util import ( "fmt" diff --git a/value_buffer_test.go b/util/value_buffer_test.go similarity index 99% rename from value_buffer_test.go rename to util/value_buffer_test.go index 1b31bb3..9b2504e 100644 --- a/value_buffer_test.go +++ b/util/value_buffer_test.go @@ -1,4 +1,4 @@ -package chart +package util import ( "testing"