market hours tweaks.
This commit is contained in:
parent
c3a066aecd
commit
b1cd8bd2e3
8 changed files with 253 additions and 39 deletions
64
date.go
64
date.go
|
@ -52,22 +52,22 @@ var (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// NYSEOpen is when the NYSE opens.
|
// NYSEOpen is when the NYSE opens.
|
||||||
NYSEOpen = Date.ClockTime(9, 30, 0, 0, Date.Eastern())
|
NYSEOpen = Date.Time(9, 30, 0, 0, Date.Eastern())
|
||||||
|
|
||||||
// NYSEClose is when the NYSE closes.
|
// NYSEClose is when the NYSE closes.
|
||||||
NYSEClose = Date.ClockTime(16, 0, 0, 0, Date.Eastern())
|
NYSEClose = Date.Time(16, 0, 0, 0, Date.Eastern())
|
||||||
|
|
||||||
// NASDAQOpen is when NASDAQ opens.
|
// NASDAQOpen is when NASDAQ opens.
|
||||||
NASDAQOpen = Date.ClockTime(9, 30, 0, 0, Date.Eastern())
|
NASDAQOpen = Date.Time(9, 30, 0, 0, Date.Eastern())
|
||||||
|
|
||||||
// NASDAQClose is when NASDAQ closes.
|
// NASDAQClose is when NASDAQ closes.
|
||||||
NASDAQClose = Date.ClockTime(16, 0, 0, 0, Date.Eastern())
|
NASDAQClose = Date.Time(16, 0, 0, 0, Date.Eastern())
|
||||||
|
|
||||||
// NYSEArcaOpen is when NYSEARCA opens.
|
// NYSEArcaOpen is when NYSEARCA opens.
|
||||||
NYSEArcaOpen = Date.ClockTime(4, 0, 0, 0, Date.Eastern())
|
NYSEArcaOpen = Date.Time(4, 0, 0, 0, Date.Eastern())
|
||||||
|
|
||||||
// NYSEArcaClose is when NYSEARCA closes.
|
// NYSEArcaClose is when NYSEARCA closes.
|
||||||
NYSEArcaClose = Date.ClockTime(20, 0, 0, 0, Date.Eastern())
|
NYSEArcaClose = Date.Time(20, 0, 0, 0, Date.Eastern())
|
||||||
)
|
)
|
||||||
|
|
||||||
// HolidayProvider is a function that returns if a given time falls on a holiday.
|
// HolidayProvider is a function that returns if a given time falls on a holiday.
|
||||||
|
@ -220,17 +220,22 @@ func (d date) Eastern() *time.Location {
|
||||||
return _eastern
|
return _eastern
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClockTime returns a new time.Time for the given clock components.
|
// Time returns a new time.Time for the given clock components.
|
||||||
func (d date) ClockTime(hour, min, sec, nsec int, loc *time.Location) time.Time {
|
func (d date) Time(hour, min, sec, nsec int, loc *time.Location) time.Time {
|
||||||
return time.Date(0, 0, 0, hour, min, sec, nsec, loc)
|
return time.Date(0, 0, 0, hour, min, sec, nsec, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d date) Date(year, month, day int, loc *time.Location) time.Time {
|
||||||
|
return time.Date(year, time.Month(month), day, 12, 0, 0, 0, loc)
|
||||||
|
}
|
||||||
|
|
||||||
// On returns the clock components of clock (hour,minute,second) on the date components of d.
|
// On returns the clock components of clock (hour,minute,second) on the date components of d.
|
||||||
func (d date) On(clock, cd time.Time) time.Time {
|
func (d date) On(clock, cd time.Time) time.Time {
|
||||||
return time.Date(cd.Year(), cd.Month(), cd.Day(), clock.Hour(), clock.Minute(), clock.Second(), clock.Nanosecond(), clock.Location())
|
tzAdjusted := cd.In(clock.Location())
|
||||||
|
return time.Date(tzAdjusted.Year(), tzAdjusted.Month(), tzAdjusted.Day(), clock.Hour(), clock.Minute(), clock.Second(), clock.Nanosecond(), clock.Location())
|
||||||
}
|
}
|
||||||
|
|
||||||
// NoonOn is a shortcut for On(ClockTime(12,0,0), cd) a.k.a. noon on a given date.
|
// NoonOn is a shortcut for On(Time(12,0,0), cd) a.k.a. noon on a given date.
|
||||||
func (d date) NoonOn(cd time.Time) time.Time {
|
func (d date) NoonOn(cd time.Time) time.Time {
|
||||||
return time.Date(cd.Year(), cd.Month(), cd.Day(), 12, 0, 0, 0, cd.Location())
|
return time.Date(cd.Year(), cd.Month(), cd.Day(), 12, 0, 0, 0, cd.Location())
|
||||||
}
|
}
|
||||||
|
@ -252,19 +257,20 @@ func (d date) IsWeekendDay(day time.Weekday) bool {
|
||||||
|
|
||||||
// Before returns if a timestamp is strictly before another date (ignoring hours, minutes etc.)
|
// Before returns if a timestamp is strictly before another date (ignoring hours, minutes etc.)
|
||||||
func (d date) Before(before, reference time.Time) bool {
|
func (d date) Before(before, reference time.Time) bool {
|
||||||
if before.Year() < reference.Year() {
|
tzAdjustedBefore := before.In(reference.Location())
|
||||||
|
if tzAdjustedBefore.Year() < reference.Year() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if before.Month() < reference.Month() {
|
if tzAdjustedBefore.Month() < reference.Month() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return before.Year() == reference.Year() && before.Month() == reference.Month() && before.Day() < reference.Day()
|
return tzAdjustedBefore.Year() == reference.Year() && tzAdjustedBefore.Month() == reference.Month() && tzAdjustedBefore.Day() < reference.Day()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextMarketOpen returns the next market open after a given time.
|
// NextMarketOpen returns the next market open after a given time.
|
||||||
func (d date) NextMarketOpen(after, openTime time.Time, isHoliday HolidayProvider) time.Time {
|
func (d date) NextMarketOpen(after, openTime time.Time, isHoliday HolidayProvider) time.Time {
|
||||||
afterEastern := after.In(d.Eastern())
|
afterLocalized := after.In(openTime.Location())
|
||||||
todaysOpen := d.On(openTime, afterEastern)
|
todaysOpen := d.On(openTime, afterLocalized)
|
||||||
|
|
||||||
if isHoliday == nil {
|
if isHoliday == nil {
|
||||||
isHoliday = defaultHolidayProvider
|
isHoliday = defaultHolidayProvider
|
||||||
|
@ -272,7 +278,7 @@ func (d date) NextMarketOpen(after, openTime time.Time, isHoliday HolidayProvide
|
||||||
|
|
||||||
todayIsValidTradingDay := d.IsWeekDay(todaysOpen.Weekday()) && !isHoliday(todaysOpen)
|
todayIsValidTradingDay := d.IsWeekDay(todaysOpen.Weekday()) && !isHoliday(todaysOpen)
|
||||||
|
|
||||||
if (afterEastern.Equal(todaysOpen) || afterEastern.Before(todaysOpen)) && todayIsValidTradingDay {
|
if (afterLocalized.Equal(todaysOpen) || afterLocalized.Before(todaysOpen)) && todayIsValidTradingDay {
|
||||||
return todaysOpen
|
return todaysOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,18 +294,18 @@ func (d date) NextMarketOpen(after, openTime time.Time, isHoliday HolidayProvide
|
||||||
|
|
||||||
// NextMarketClose returns the next market close after a given time.
|
// NextMarketClose returns the next market close after a given time.
|
||||||
func (d date) NextMarketClose(after, closeTime time.Time, isHoliday HolidayProvider) time.Time {
|
func (d date) NextMarketClose(after, closeTime time.Time, isHoliday HolidayProvider) time.Time {
|
||||||
afterEastern := after.In(d.Eastern())
|
afterLocalized := after.In(closeTime.Location())
|
||||||
|
|
||||||
if isHoliday == nil {
|
if isHoliday == nil {
|
||||||
isHoliday = defaultHolidayProvider
|
isHoliday = defaultHolidayProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
todaysClose := d.On(closeTime, afterEastern)
|
todaysClose := d.On(closeTime, afterLocalized)
|
||||||
if afterEastern.Before(todaysClose) && d.IsWeekDay(todaysClose.Weekday()) && !isHoliday(todaysClose) {
|
if afterLocalized.Before(todaysClose) && d.IsWeekDay(todaysClose.Weekday()) && !isHoliday(todaysClose) {
|
||||||
return todaysClose
|
return todaysClose
|
||||||
}
|
}
|
||||||
|
|
||||||
if afterEastern.Equal(todaysClose) { //rare but it might happen.
|
if afterLocalized.Equal(todaysClose) { //rare but it might happen.
|
||||||
return todaysClose
|
return todaysClose
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,3 +382,21 @@ func (d date) NextHour(ts time.Time) time.Time {
|
||||||
final := advanced.Add(-minutes)
|
final := advanced.Add(-minutes)
|
||||||
return time.Date(final.Year(), final.Month(), final.Day(), final.Hour(), 0, 0, 0, final.Location())
|
return time.Date(final.Year(), final.Month(), final.Day(), final.Hour(), 0, 0, 0, final.Location())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NextDayOfWeek returns the next instance of a given weekday after a given timestamp.
|
||||||
|
func (d date) NextDayOfWeek(after time.Time, dayOfWeek time.Weekday) time.Time {
|
||||||
|
afterWeekday := after.Weekday()
|
||||||
|
if afterWeekday == dayOfWeek {
|
||||||
|
return after.AddDate(0, 0, 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 vs 5 ~ add 4 days
|
||||||
|
if afterWeekday < dayOfWeek {
|
||||||
|
dayDelta := int(dayOfWeek - afterWeekday)
|
||||||
|
return after.AddDate(0, 0, dayDelta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5 vs 1, add 7-(5-1) ~ 3 days
|
||||||
|
dayDelta := 7 - int(afterWeekday-dayOfWeek)
|
||||||
|
return after.AddDate(0, 0, dayDelta)
|
||||||
|
}
|
||||||
|
|
102
date_test.go
102
date_test.go
|
@ -12,6 +12,54 @@ func parse(v string) time.Time {
|
||||||
return ts
|
return ts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDateTime(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
ts := Date.Time(5, 6, 7, 8, time.UTC)
|
||||||
|
assert.Equal(05, ts.Hour())
|
||||||
|
assert.Equal(06, ts.Minute())
|
||||||
|
assert.Equal(07, ts.Second())
|
||||||
|
assert.Equal(8, ts.Nanosecond())
|
||||||
|
assert.Equal(time.UTC, ts.Location())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDateDate(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
ts := Date.Date(2015, 5, 6, time.UTC)
|
||||||
|
assert.Equal(2015, ts.Year())
|
||||||
|
assert.Equal(5, ts.Month())
|
||||||
|
assert.Equal(6, ts.Day())
|
||||||
|
assert.Equal(time.UTC, ts.Location())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDateOn(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
ts := Date.On(Date.Time(5, 4, 3, 2, time.UTC), Date.Date(2016, 6, 7, Date.Eastern()))
|
||||||
|
assert.Equal(2016, ts.Year())
|
||||||
|
assert.Equal(6, ts.Month())
|
||||||
|
assert.Equal(7, ts.Day())
|
||||||
|
assert.Equal(5, ts.Hour())
|
||||||
|
assert.Equal(4, ts.Minute())
|
||||||
|
assert.Equal(3, ts.Second())
|
||||||
|
assert.Equal(2, ts.Nanosecond())
|
||||||
|
assert.Equal(time.UTC, ts.Location())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDateNoonOn(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
noon := Date.NoonOn(time.Date(2016, 04, 03, 02, 01, 0, 0, time.UTC))
|
||||||
|
|
||||||
|
assert.Equal(2016, noon.Year())
|
||||||
|
assert.Equal(4, noon.Month())
|
||||||
|
assert.Equal(3, noon.Day())
|
||||||
|
assert.Equal(12, noon.Hour())
|
||||||
|
assert.Equal(0, noon.Minute())
|
||||||
|
assert.Equal(time.UTC, noon.Location())
|
||||||
|
}
|
||||||
|
|
||||||
func TestDateBefore(t *testing.T) {
|
func TestDateBefore(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
@ -25,6 +73,17 @@ func TestDateBefore(t *testing.T) {
|
||||||
assert.False(Date.Before(parse("2017-08-03"), parse("2016-07-01")))
|
assert.False(Date.Before(parse("2017-08-03"), parse("2016-07-01")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDateBeforeHandlesTimezones(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
tuesdayUTC := time.Date(2016, 8, 02, 22, 00, 0, 0, time.UTC)
|
||||||
|
mondayUTC := time.Date(2016, 8, 01, 1, 00, 0, 0, time.UTC)
|
||||||
|
sundayEST := time.Date(2016, 7, 31, 22, 00, 0, 0, Date.Eastern())
|
||||||
|
|
||||||
|
assert.True(Date.Before(sundayEST, tuesdayUTC))
|
||||||
|
assert.False(Date.Before(sundayEST, mondayUTC))
|
||||||
|
}
|
||||||
|
|
||||||
func TestNextMarketOpen(t *testing.T) {
|
func TestNextMarketOpen(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
@ -44,6 +103,10 @@ func TestNextMarketOpen(t *testing.T) {
|
||||||
assert.True(mondayOpen.Equal(Date.NextMarketOpen(afterFriday, NYSEOpen, Date.IsNYSEHoliday)))
|
assert.True(mondayOpen.Equal(Date.NextMarketOpen(afterFriday, NYSEOpen, Date.IsNYSEHoliday)))
|
||||||
assert.True(mondayOpen.Equal(Date.NextMarketOpen(weekend, NYSEOpen, Date.IsNYSEHoliday)))
|
assert.True(mondayOpen.Equal(Date.NextMarketOpen(weekend, NYSEOpen, Date.IsNYSEHoliday)))
|
||||||
|
|
||||||
|
assert.Equal(Date.Eastern(), todayOpen.Location())
|
||||||
|
assert.Equal(Date.Eastern(), tomorrowOpen.Location())
|
||||||
|
assert.Equal(Date.Eastern(), mondayOpen.Location())
|
||||||
|
|
||||||
testRegression := time.Date(2016, 07, 18, 16, 0, 0, 0, Date.Eastern())
|
testRegression := time.Date(2016, 07, 18, 16, 0, 0, 0, Date.Eastern())
|
||||||
shouldbe := time.Date(2016, 07, 19, 9, 30, 0, 0, Date.Eastern())
|
shouldbe := time.Date(2016, 07, 19, 9, 30, 0, 0, Date.Eastern())
|
||||||
|
|
||||||
|
@ -68,6 +131,10 @@ func TestNextMarketClose(t *testing.T) {
|
||||||
assert.True(tomorrowClose.Equal(Date.NextMarketClose(afterClose, NYSEClose, Date.IsNYSEHoliday)))
|
assert.True(tomorrowClose.Equal(Date.NextMarketClose(afterClose, NYSEClose, Date.IsNYSEHoliday)))
|
||||||
assert.True(mondayClose.Equal(Date.NextMarketClose(afterFriday, NYSEClose, Date.IsNYSEHoliday)))
|
assert.True(mondayClose.Equal(Date.NextMarketClose(afterFriday, NYSEClose, Date.IsNYSEHoliday)))
|
||||||
assert.True(mondayClose.Equal(Date.NextMarketClose(weekend, NYSEClose, Date.IsNYSEHoliday)))
|
assert.True(mondayClose.Equal(Date.NextMarketClose(weekend, NYSEClose, Date.IsNYSEHoliday)))
|
||||||
|
|
||||||
|
assert.Equal(Date.Eastern(), todayClose.Location())
|
||||||
|
assert.Equal(Date.Eastern(), tomorrowClose.Location())
|
||||||
|
assert.Equal(Date.Eastern(), mondayClose.Location())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCalculateMarketSecondsBetween(t *testing.T) {
|
func TestCalculateMarketSecondsBetween(t *testing.T) {
|
||||||
|
@ -120,3 +187,38 @@ func TestDateNextHour(t *testing.T) {
|
||||||
assert.Equal(12, next.Hour())
|
assert.Equal(12, next.Hour())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDateNextDayOfWeek(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
weds := Date.Date(2016, 8, 10, time.UTC)
|
||||||
|
fri := Date.Date(2016, 8, 12, time.UTC)
|
||||||
|
sun := Date.Date(2016, 8, 14, time.UTC)
|
||||||
|
mon := Date.Date(2016, 8, 15, time.UTC)
|
||||||
|
weds2 := Date.Date(2016, 8, 17, time.UTC)
|
||||||
|
|
||||||
|
nextFri := Date.NextDayOfWeek(weds, time.Friday)
|
||||||
|
nextSunday := Date.NextDayOfWeek(weds, time.Sunday)
|
||||||
|
nextMonday := Date.NextDayOfWeek(weds, time.Monday)
|
||||||
|
nextWeds := Date.NextDayOfWeek(weds, time.Wednesday)
|
||||||
|
|
||||||
|
assert.Equal(fri.Year(), nextFri.Year())
|
||||||
|
assert.Equal(fri.Month(), nextFri.Month())
|
||||||
|
assert.Equal(fri.Day(), nextFri.Day())
|
||||||
|
|
||||||
|
assert.Equal(sun.Year(), nextSunday.Year())
|
||||||
|
assert.Equal(sun.Month(), nextSunday.Month())
|
||||||
|
assert.Equal(sun.Day(), nextSunday.Day())
|
||||||
|
|
||||||
|
assert.Equal(mon.Year(), nextMonday.Year())
|
||||||
|
assert.Equal(mon.Month(), nextMonday.Month())
|
||||||
|
assert.Equal(mon.Day(), nextMonday.Day())
|
||||||
|
|
||||||
|
assert.Equal(weds2.Year(), nextWeds.Year())
|
||||||
|
assert.Equal(weds2.Month(), nextWeds.Month())
|
||||||
|
assert.Equal(weds2.Day(), nextWeds.Day())
|
||||||
|
|
||||||
|
assert.Equal(time.UTC, nextFri.Location())
|
||||||
|
assert.Equal(time.UTC, nextSunday.Location())
|
||||||
|
assert.Equal(time.UTC, nextMonday.Location())
|
||||||
|
}
|
||||||
|
|
|
@ -2,14 +2,13 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/wcharczuk/go-chart"
|
"github.com/wcharczuk/go-chart"
|
||||||
)
|
)
|
||||||
|
|
||||||
func drawChart(res http.ResponseWriter, req *http.Request) {
|
func drawChart(res http.ResponseWriter, req *http.Request) {
|
||||||
start := time.Date(2016, 07, 04, 12, 0, 0, 0, chart.Date.Eastern())
|
start := chart.Date.Date(2016, 6, 20, chart.Date.Eastern())
|
||||||
end := time.Date(2016, 07, 06, 12, 0, 0, 0, chart.Date.Eastern())
|
end := chart.Date.Date(2016, 07, 21, chart.Date.Eastern())
|
||||||
xv := chart.Sequence.MarketHours(start, end, chart.NYSEOpen, chart.NYSEClose, chart.Date.IsNYSEHoliday)
|
xv := chart.Sequence.MarketHours(start, end, chart.NYSEOpen, chart.NYSEClose, chart.Date.IsNYSEHoliday)
|
||||||
yv := chart.Sequence.RandomWithAverage(len(xv), 200, 10)
|
yv := chart.Sequence.RandomWithAverage(len(xv), 200, 10)
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,11 @@ type MarketHoursRange struct {
|
||||||
Domain int
|
Domain int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTimezone returns the timezone for the market hours range.
|
||||||
|
func (mhr MarketHoursRange) GetTimezone() *time.Location {
|
||||||
|
return mhr.GetMarketOpen().Location()
|
||||||
|
}
|
||||||
|
|
||||||
// IsZero returns if the range is setup or not.
|
// IsZero returns if the range is setup or not.
|
||||||
func (mhr MarketHoursRange) IsZero() bool {
|
func (mhr MarketHoursRange) IsZero() bool {
|
||||||
return mhr.Min.IsZero() && mhr.Max.IsZero()
|
return mhr.Min.IsZero() && mhr.Max.IsZero()
|
||||||
|
@ -48,11 +53,13 @@ func (mhr MarketHoursRange) GetEffectiveMax() time.Time {
|
||||||
// SetMin sets the min value.
|
// SetMin sets the min value.
|
||||||
func (mhr *MarketHoursRange) SetMin(min float64) {
|
func (mhr *MarketHoursRange) SetMin(min float64) {
|
||||||
mhr.Min = Float64ToTime(min)
|
mhr.Min = Float64ToTime(min)
|
||||||
|
mhr.Min = mhr.Min.In(mhr.GetTimezone())
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetMax sets the max value.
|
// SetMax sets the max value.
|
||||||
func (mhr *MarketHoursRange) SetMax(max float64) {
|
func (mhr *MarketHoursRange) SetMax(max float64) {
|
||||||
mhr.Max = Float64ToTime(max)
|
mhr.Max = Float64ToTime(max)
|
||||||
|
mhr.Max = mhr.Max.In(mhr.GetTimezone())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDelta gets the delta.
|
// GetDelta gets the delta.
|
||||||
|
@ -99,18 +106,38 @@ func (mhr MarketHoursRange) GetMarketClose() time.Time {
|
||||||
// GetTicks returns the ticks for the range.
|
// GetTicks returns the ticks for the range.
|
||||||
// This is to override the default continous ticks that would be generated for the range.
|
// This is to override the default continous ticks that would be generated for the range.
|
||||||
func (mhr *MarketHoursRange) GetTicks(r Renderer, defaults Style, vf ValueFormatter) []Tick {
|
func (mhr *MarketHoursRange) GetTicks(r Renderer, defaults Style, vf ValueFormatter) []Tick {
|
||||||
println("GetTicks() domain:", mhr.Domain)
|
|
||||||
times := Sequence.MarketHours(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
times := Sequence.MarketHours(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
||||||
timesWidth := mhr.measureTimes(r, defaults, vf, times)
|
timesWidth := mhr.measureTimes(r, defaults, vf, times)
|
||||||
if timesWidth <= mhr.Domain {
|
if timesWidth <= mhr.Domain {
|
||||||
return mhr.makeTicks(vf, times)
|
return mhr.makeTicks(vf, times)
|
||||||
}
|
}
|
||||||
|
|
||||||
times = Sequence.MarketHourQuarters(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
times = Sequence.MarketHourQuarters(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
||||||
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
||||||
if timesWidth <= mhr.Domain {
|
if timesWidth <= mhr.Domain {
|
||||||
return mhr.makeTicks(vf, times)
|
return mhr.makeTicks(vf, times)
|
||||||
}
|
}
|
||||||
return mhr.makeTicks(vf, Sequence.MarketDayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider()))
|
|
||||||
|
times = Sequence.MarketDayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
||||||
|
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
||||||
|
if timesWidth <= mhr.Domain {
|
||||||
|
return mhr.makeTicks(vf, times)
|
||||||
|
}
|
||||||
|
|
||||||
|
times = Sequence.MarketDayAlternateCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
||||||
|
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
||||||
|
if timesWidth <= mhr.Domain {
|
||||||
|
return mhr.makeTicks(vf, times)
|
||||||
|
}
|
||||||
|
|
||||||
|
times = Sequence.MarketDayMondayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
|
||||||
|
timesWidth = mhr.measureTimes(r, defaults, vf, times)
|
||||||
|
if timesWidth <= mhr.Domain {
|
||||||
|
return mhr.makeTicks(vf, times)
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenerateContinuousTicks(r, mhr, false, defaults, vf)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mhr *MarketHoursRange) measureTimes(r Renderer, defaults Style, vf ValueFormatter, times []time.Time) int {
|
func (mhr *MarketHoursRange) measureTimes(r Renderer, defaults Style, vf ValueFormatter, times []time.Time) int {
|
||||||
|
@ -135,13 +162,12 @@ func (mhr *MarketHoursRange) makeTicks(vf ValueFormatter, times []time.Time) []T
|
||||||
Value: TimeToFloat64(t),
|
Value: TimeToFloat64(t),
|
||||||
Label: vf(t),
|
Label: vf(t),
|
||||||
}
|
}
|
||||||
println("make tick =>", vf(t))
|
|
||||||
}
|
}
|
||||||
return ticks
|
return ticks
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mhr MarketHoursRange) String() string {
|
func (mhr MarketHoursRange) String() string {
|
||||||
return fmt.Sprintf("MarketHoursRange [%s, %s] => %d", mhr.Min.Format(DefaultDateMinuteFormat), mhr.Max.Format(DefaultDateMinuteFormat), mhr.Domain)
|
return fmt.Sprintf("MarketHoursRange [%s, %s] => %d", mhr.Min.Format(time.RFC3339), mhr.Max.Format(time.RFC3339), mhr.Domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate maps a given value into the ContinuousRange space.
|
// Translate maps a given value into the ContinuousRange space.
|
||||||
|
|
|
@ -43,18 +43,30 @@ func TestMarketHoursRangeTranslate(t *testing.T) {
|
||||||
func TestMarketHoursRangeGetTicks(t *testing.T) {
|
func TestMarketHoursRangeGetTicks(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
r := &MarketHoursRange{
|
r, err := PNG(1024, 1024)
|
||||||
Min: time.Date(2016, 07, 18, 9, 30, 0, 0, Date.Eastern()),
|
assert.Nil(err)
|
||||||
Max: time.Date(2016, 07, 22, 16, 00, 0, 0, Date.Eastern()),
|
|
||||||
|
f, err := GetDefaultFont()
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
defaults := Style{
|
||||||
|
Font: f,
|
||||||
|
FontSize: 10,
|
||||||
|
FontColor: ColorBlack,
|
||||||
|
}
|
||||||
|
|
||||||
|
ra := &MarketHoursRange{
|
||||||
|
Min: Date.On(NYSEOpen, Date.Date(2016, 07, 18, Date.Eastern())),
|
||||||
|
Max: Date.On(NYSEClose, Date.Date(2016, 07, 22, Date.Eastern())),
|
||||||
MarketOpen: NYSEOpen,
|
MarketOpen: NYSEOpen,
|
||||||
MarketClose: NYSEClose,
|
MarketClose: NYSEClose,
|
||||||
HolidayProvider: Date.IsNYSEHoliday,
|
HolidayProvider: Date.IsNYSEHoliday,
|
||||||
Domain: 1000,
|
Domain: 1024,
|
||||||
}
|
}
|
||||||
|
|
||||||
ticks := r.GetTicks(TimeValueFormatter)
|
ticks := ra.GetTicks(r, defaults, TimeValueFormatter)
|
||||||
assert.NotEmpty(ticks)
|
assert.NotEmpty(ticks)
|
||||||
assert.Len(ticks, 24)
|
assert.Len(ticks, 5)
|
||||||
assert.NotEqual(TimeToFloat64(r.Min), ticks[0].Value)
|
assert.NotEqual(TimeToFloat64(ra.Min), ticks[0].Value)
|
||||||
assert.NotEmpty(ticks[0].Label)
|
assert.NotEmpty(ticks[0].Label)
|
||||||
}
|
}
|
||||||
|
|
35
sequence.go
35
sequence.go
|
@ -99,7 +99,7 @@ func (s sequence) MarketHourQuarters(from, to time.Time, marketOpen, marketClose
|
||||||
if isValidTradingDay {
|
if isValidTradingDay {
|
||||||
todayOpen := Date.On(marketOpen, cursor)
|
todayOpen := Date.On(marketOpen, cursor)
|
||||||
todayNoon := Date.NoonOn(cursor)
|
todayNoon := Date.NoonOn(cursor)
|
||||||
today2pm := Date.On(Date.ClockTime(2, 0, 0, 0, cursor.Location()), cursor)
|
today2pm := Date.On(Date.Time(14, 0, 0, 0, cursor.Location()), cursor)
|
||||||
todayClose := Date.On(marketClose, cursor)
|
todayClose := Date.On(marketClose, cursor)
|
||||||
times = append(times, todayOpen, todayNoon, today2pm, todayClose)
|
times = append(times, todayOpen, todayNoon, today2pm, todayClose)
|
||||||
}
|
}
|
||||||
|
@ -124,3 +124,36 @@ func (s sequence) MarketDayCloses(from, to time.Time, marketOpen, marketClose ti
|
||||||
}
|
}
|
||||||
return times
|
return times
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s sequence) 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 (s sequence) 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)
|
||||||
|
}
|
||||||
|
println("advance to next monday", cursor.Format(DefaultDateFormat))
|
||||||
|
cursor = Date.NextDayOfWeek(cursor, time.Monday)
|
||||||
|
println(cursor.Format(DefaultDateFormat))
|
||||||
|
}
|
||||||
|
return times
|
||||||
|
}
|
||||||
|
|
|
@ -22,5 +22,24 @@ func TestSequenceMarketHours(t *testing.T) {
|
||||||
|
|
||||||
today := time.Date(2016, 07, 01, 12, 0, 0, 0, Date.Eastern())
|
today := time.Date(2016, 07, 01, 12, 0, 0, 0, Date.Eastern())
|
||||||
mh := Sequence.MarketHours(today, today, NYSEOpen, NYSEClose, Date.IsNYSEHoliday)
|
mh := Sequence.MarketHours(today, today, NYSEOpen, NYSEClose, Date.IsNYSEHoliday)
|
||||||
assert.Len(mh, 7)
|
assert.Len(mh, 8)
|
||||||
|
assert.Equal(Date.Eastern(), mh[0].Location())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSequenceMarketQuarters(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
today := time.Date(2016, 07, 01, 12, 0, 0, 0, Date.Eastern())
|
||||||
|
mh := Sequence.MarketHourQuarters(today, today, NYSEOpen, NYSEClose, Date.IsNYSEHoliday)
|
||||||
|
assert.Len(mh, 4)
|
||||||
|
assert.Equal(9, mh[0].Hour())
|
||||||
|
assert.Equal(30, mh[0].Minute())
|
||||||
|
assert.Equal(Date.Eastern(), mh[0].Location())
|
||||||
|
|
||||||
|
assert.Equal(12, mh[1].Hour())
|
||||||
|
assert.Equal(00, mh[1].Minute())
|
||||||
|
assert.Equal(Date.Eastern(), mh[1].Location())
|
||||||
|
|
||||||
|
assert.Equal(14, mh[2].Hour())
|
||||||
|
assert.Equal(00, mh[2].Minute())
|
||||||
|
assert.Equal(Date.Eastern(), mh[2].Location())
|
||||||
}
|
}
|
||||||
|
|
5
xaxis.go
5
xaxis.go
|
@ -72,7 +72,7 @@ func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
|
||||||
|
|
||||||
tp := xa.GetTickPosition()
|
tp := xa.GetTickPosition()
|
||||||
|
|
||||||
var left, right, top, bottom = math.MaxInt32, 0, math.MaxInt32, 0
|
var left, right, bottom = math.MaxInt32, 0, 0
|
||||||
for index, t := range ticks {
|
for index, t := range ticks {
|
||||||
v := t.Value
|
v := t.Value
|
||||||
tickStyle.GetTextOptions().WriteToRenderer(r)
|
tickStyle.GetTextOptions().WriteToRenderer(r)
|
||||||
|
@ -94,14 +94,13 @@ func (xa XAxis) Measure(r Renderer, canvasBox Box, ra Range, defaults Style, tic
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
top = Math.MinInt(top, canvasBox.Bottom)
|
|
||||||
left = Math.MinInt(left, ltx)
|
left = Math.MinInt(left, ltx)
|
||||||
right = Math.MaxInt(right, rtx)
|
right = Math.MaxInt(right, rtx)
|
||||||
bottom = Math.MaxInt(bottom, ty)
|
bottom = Math.MaxInt(bottom, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Box{
|
return Box{
|
||||||
Top: top,
|
Top: canvasBox.Bottom,
|
||||||
Left: left,
|
Left: left,
|
||||||
Right: right,
|
Right: right,
|
||||||
Bottom: bottom,
|
Bottom: bottom,
|
||||||
|
|
Loading…
Reference in a new issue