diff --git a/date/util.go b/date/util.go index 5dfbe45..0432681 100644 --- a/date/util.go +++ b/date/util.go @@ -70,8 +70,8 @@ var ( NYSEArcaClose = ClockTime(20, 0, 0, 0, Eastern()) ) -// HolidayChecker is a function that returns if a given time falls on a holiday. -type HolidayChecker func(time.Time) bool +// HolidayProvider is a function that returns if a given time falls on a holiday. +type HolidayProvider func(time.Time) bool // IsNYSEHoliday returns if a date was/is on a nyse holiday day. func IsNYSEHoliday(t time.Time) bool { @@ -188,6 +188,16 @@ func IsNYSEHoliday(t time.Time) bool { return false } +// IsNYSEArcaHoliday returns that returns if a given time falls on a holiday. +func IsNYSEArcaHoliday(t time.Time) bool { + return IsNYSEHoliday(t) +} + +// IsNASDAQHoliday returns if a date was a NASDAQ holiday day. +func IsNASDAQHoliday(t time.Time) bool { + return IsNYSEHoliday(t) +} + // Eastern returns the eastern timezone. func Eastern() *time.Location { if _eastern == nil { @@ -226,22 +236,10 @@ func BeforeDate(before, reference time.Time) bool { return before.Year() == reference.Year() && before.Month() == reference.Month() && before.Day() < reference.Day() } -// MarketOpen returns 0930 on a given day. -func MarketOpen(on, openTime time.Time) time.Time { - onEastern := on.In(Eastern()) - return On(openTime, onEastern) -} - -// MarketClose returns 1600 on a given day. -func MarketClose(on, closeTime time.Time) time.Time { - onEastern := on.In(Eastern()) - return time.Date(onEastern.Year(), onEastern.Month(), onEastern.Day(), 16, 0, 0, 0, Eastern()) -} - // NextMarketOpen returns the next market open after a given time. -func NextMarketOpen(after, openTime time.Time, isHoliday HolidayChecker) time.Time { +func NextMarketOpen(after, openTime time.Time, isHoliday HolidayProvider) time.Time { afterEastern := after.In(Eastern()) - todaysOpen := MarketOpen(afterEastern, openTime) + todaysOpen := On(openTime, afterEastern) if afterEastern.Before(todaysOpen) && IsWeekDay(todaysOpen.Weekday()) && !isHoliday(todaysOpen) { return todaysOpen @@ -261,10 +259,10 @@ func NextMarketOpen(after, openTime time.Time, isHoliday HolidayChecker) time.Ti } // NextMarketClose returns the next market close after a given time. -func NextMarketClose(after, closeTime time.Time, isHoliday HolidayChecker) time.Time { +func NextMarketClose(after, closeTime time.Time, isHoliday HolidayProvider) time.Time { afterEastern := after.In(Eastern()) - todaysClose := MarketClose(afterEastern, closeTime) + todaysClose := On(closeTime, afterEastern) if afterEastern.Before(todaysClose) && IsWeekDay(todaysClose.Weekday()) && !isHoliday(todaysClose) { return todaysClose } @@ -283,7 +281,7 @@ func NextMarketClose(after, closeTime time.Time, isHoliday HolidayChecker) time. } // CalculateMarketSecondsBetween calculates the number of seconds the market was open between two dates. -func CalculateMarketSecondsBetween(start, end, marketOpen, marketClose time.Time, isHoliday HolidayChecker) (seconds int64) { +func CalculateMarketSecondsBetween(start, end, marketOpen, marketClose time.Time, isHoliday HolidayProvider) (seconds int64) { se := start.In(Eastern()) ee := end.In(Eastern()) diff --git a/nyse_market_hours_range.go b/market_hours_range.go similarity index 60% rename from nyse_market_hours_range.go rename to market_hours_range.go index 2c62163..3cb9400 100644 --- a/nyse_market_hours_range.go +++ b/market_hours_range.go @@ -7,65 +7,71 @@ import ( "github.com/wcharczuk/go-chart/date" ) -// NYSEMarketHoursRange is a special type of range that compresses a time range into just the +// MarketHoursRange is a special type of range that compresses a time range into just the // market (i.e. NYSE operating hours and days) range. -type NYSEMarketHoursRange struct { - Min time.Time - Max time.Time +type MarketHoursRange struct { + Min time.Time + Max time.Time + + MarketOpen time.Time + MarketClose time.Time + + HolidayProvider date.HolidayProvider + Domain int } // IsZero returns if the range is setup or not. -func (mhr NYSEMarketHoursRange) IsZero() bool { +func (mhr MarketHoursRange) IsZero() bool { return mhr.Min.IsZero() && mhr.Max.IsZero() } // GetMin returns the min value. -func (mhr NYSEMarketHoursRange) GetMin() float64 { +func (mhr MarketHoursRange) GetMin() float64 { return TimeToFloat64(mhr.Min) } // GetMax returns the max value. -func (mhr NYSEMarketHoursRange) GetMax() float64 { +func (mhr MarketHoursRange) GetMax() float64 { return TimeToFloat64(mhr.Max) } // SetMin sets the min value. -func (mhr *NYSEMarketHoursRange) SetMin(min float64) { +func (mhr *MarketHoursRange) SetMin(min float64) { mhr.Min = Float64ToTime(min) } // SetMax sets the max value. -func (mhr *NYSEMarketHoursRange) SetMax(max float64) { +func (mhr *MarketHoursRange) SetMax(max float64) { mhr.Max = Float64ToTime(max) } // GetDelta gets the delta. -func (mhr NYSEMarketHoursRange) GetDelta() float64 { +func (mhr MarketHoursRange) GetDelta() float64 { min := TimeToFloat64(mhr.Min) max := TimeToFloat64(mhr.Max) return max - min } // GetDomain gets the domain. -func (mhr NYSEMarketHoursRange) GetDomain() int { +func (mhr MarketHoursRange) GetDomain() int { return mhr.Domain } // SetDomain sets the domain. -func (mhr *NYSEMarketHoursRange) SetDomain(domain int) { +func (mhr *MarketHoursRange) SetDomain(domain int) { mhr.Domain = domain } -func (mhr NYSEMarketHoursRange) String() string { +func (mhr MarketHoursRange) String() string { return fmt.Sprintf("MarketHoursRange [%s, %s] => %d", mhr.Min.Format(DefaultDateFormat), mhr.Max.Format(DefaultDateFormat), mhr.Domain) } // Translate maps a given value into the ContinuousRange space. -func (mhr NYSEMarketHoursRange) Translate(value float64) int { +func (mhr MarketHoursRange) Translate(value float64) int { valueTime := Float64ToTime(value) - deltaSeconds := date.CalculateMarketSecondsBetween(mhr.Min, mhr.Max) - valueDelta := date.CalculateMarketSecondsBetween(mhr.Min, valueTime) + deltaSeconds := date.CalculateMarketSecondsBetween(mhr.Min, mhr.Max, mhr.MarketOpen, mhr.MarketClose, mhr.HolidayProvider) + valueDelta := date.CalculateMarketSecondsBetween(mhr.Min, valueTime, mhr.MarketOpen, mhr.MarketClose, mhr.HolidayProvider) translated := int((float64(valueDelta) / float64(deltaSeconds)) * float64(mhr.Domain)) fmt.Printf("nyse translating: %s to %d ~= %d", valueTime.Format(time.RFC3339), deltaSeconds, valueDelta) diff --git a/market_hours_range_test.go b/market_hours_range_test.go new file mode 100644 index 0000000..8f0cbd2 --- /dev/null +++ b/market_hours_range_test.go @@ -0,0 +1,23 @@ +package chart + +import ( + "testing" + "time" + + assert "github.com/blendlabs/go-assert" + "github.com/wcharczuk/go-chart/date" +) + +func TestMarketHoursRangeGetDelta(t *testing.T) { + assert := assert.New(t) + + r := &MarketHoursRange{ + Min: time.Date(2016, 07, 19, 9, 30, 0, 0, date.Eastern()), + Max: time.Date(2016, 07, 22, 16, 00, 0, 0, date.Eastern()), + MarketOpen: date.NYSEOpen, + MarketClose: date.NYSEClose, + HolidayProvider: date.IsNYSEHoliday, + } + + assert.NotZero(r.GetDelta()) +} diff --git a/nyse_market_hours_range_test.go b/nyse_market_hours_range_test.go deleted file mode 100644 index 2af1545..0000000 --- a/nyse_market_hours_range_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package chart - -import ( - "testing" - "time" - - assert "github.com/blendlabs/go-assert" - "github.com/wcharczuk/go-chart/date" -) - -func TestNYSEMarketHoursDelta(t *testing.T) { - assert := assert.New(t) - - r := &NYSEMarketHoursRange{ - Min: time.Date(2016, 07, 19, 9, 30, 0, 0, date.Eastern()), - Max: time.Date(2016, 07, 22, 16, 00, 0, 0, date.Eastern()), - } - - assert.NotZero(r.GetDelta()) -}