refactors
This commit is contained in:
parent
dea8e71d6f
commit
859c573d3d
7 changed files with 129 additions and 73 deletions
4
Makefile
Normal file
4
Makefile
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
all: test
|
||||||
|
|
||||||
|
test:
|
||||||
|
@go test ./...
|
28
chart.go
28
chart.go
|
@ -186,16 +186,22 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if xrange == nil {
|
if c.XAxis.Range == nil {
|
||||||
xrange = &ContinuousRange{}
|
xrange = &ContinuousRange{}
|
||||||
|
} else {
|
||||||
|
xrange = c.XAxis.Range
|
||||||
}
|
}
|
||||||
|
|
||||||
if yrange == nil {
|
if c.YAxis.Range == nil {
|
||||||
yrange = &ContinuousRange{}
|
yrange = &ContinuousRange{}
|
||||||
|
} else {
|
||||||
|
yrange = c.YAxis.Range
|
||||||
}
|
}
|
||||||
|
|
||||||
if yrangeAlt == nil {
|
if c.YAxisSecondary.Range == nil {
|
||||||
yrangeAlt = &ContinuousRange{}
|
yrangeAlt = &ContinuousRange{}
|
||||||
|
} else {
|
||||||
|
yrangeAlt = c.YAxisSecondary.Range
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.XAxis.Ticks) > 0 {
|
if len(c.XAxis.Ticks) > 0 {
|
||||||
|
@ -204,13 +210,9 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
tickMin = math.Min(tickMin, t.Value)
|
tickMin = math.Min(tickMin, t.Value)
|
||||||
tickMax = math.Max(tickMax, t.Value)
|
tickMax = math.Max(tickMax, t.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
xrange.SetMin(tickMin)
|
xrange.SetMin(tickMin)
|
||||||
xrange.SetMax(tickMax)
|
xrange.SetMax(tickMax)
|
||||||
} else if c.XAxis.Range != nil && !c.XAxis.Range.IsZero() {
|
} else if xrange.IsZero() {
|
||||||
xrange.SetMin(c.XAxis.Range.GetMin())
|
|
||||||
xrange.SetMax(c.XAxis.Range.GetMax())
|
|
||||||
} else {
|
|
||||||
xrange.SetMin(minx)
|
xrange.SetMin(minx)
|
||||||
xrange.SetMax(maxx)
|
xrange.SetMax(maxx)
|
||||||
}
|
}
|
||||||
|
@ -223,10 +225,7 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
}
|
}
|
||||||
yrange.SetMin(tickMin)
|
yrange.SetMin(tickMin)
|
||||||
yrange.SetMax(tickMax)
|
yrange.SetMax(tickMax)
|
||||||
} else if c.YAxis.Range != nil && !c.YAxis.Range.IsZero() {
|
} else if yrange.IsZero() {
|
||||||
yrange.SetMin(c.YAxis.Range.GetMin())
|
|
||||||
yrange.SetMax(c.YAxis.Range.GetMax())
|
|
||||||
} else {
|
|
||||||
yrange.SetMin(miny)
|
yrange.SetMin(miny)
|
||||||
yrange.SetMax(maxy)
|
yrange.SetMax(maxy)
|
||||||
|
|
||||||
|
@ -245,10 +244,7 @@ func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||||
}
|
}
|
||||||
yrangeAlt.SetMin(tickMin)
|
yrangeAlt.SetMin(tickMin)
|
||||||
yrangeAlt.SetMax(tickMax)
|
yrangeAlt.SetMax(tickMax)
|
||||||
} else if c.YAxisSecondary.Range != nil && !c.YAxisSecondary.Range.IsZero() {
|
} else if seriesMappedToSecondaryAxis && yrangeAlt.IsZero() {
|
||||||
yrangeAlt.SetMin(c.YAxisSecondary.Range.GetMin())
|
|
||||||
yrangeAlt.SetMax(c.YAxisSecondary.Range.GetMax())
|
|
||||||
} else if seriesMappedToSecondaryAxis {
|
|
||||||
yrangeAlt.SetMin(minya)
|
yrangeAlt.SetMin(minya)
|
||||||
yrangeAlt.SetMax(maxya)
|
yrangeAlt.SetMax(maxya)
|
||||||
|
|
||||||
|
|
|
@ -50,43 +50,28 @@ var (
|
||||||
_eastern *time.Location
|
_eastern *time.Location
|
||||||
)
|
)
|
||||||
|
|
||||||
// Eastern returns the eastern timezone.
|
var (
|
||||||
func Eastern() *time.Location {
|
// NYSEOpen is when the NYSE opens.
|
||||||
if _eastern == nil {
|
NYSEOpen = ClockTime(9, 30, 0, 0, Eastern())
|
||||||
_easternLock.Lock()
|
|
||||||
defer _easternLock.Unlock()
|
|
||||||
if _eastern == nil {
|
|
||||||
_eastern, _ = time.LoadLocation("America/New_York")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _eastern
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional returns a pointer reference to a given time.
|
// NYSEClose is when the NYSE closes.
|
||||||
func Optional(t time.Time) *time.Time {
|
NYSEClose = ClockTime(16, 0, 0, 0, Eastern())
|
||||||
return &t
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsWeekDay returns if the day is a monday->friday.
|
// NASDAQOpen is when NASDAQ opens.
|
||||||
func IsWeekDay(day time.Weekday) bool {
|
NASDAQOpen = ClockTime(9, 30, 0, 0, Eastern())
|
||||||
return !IsWeekendDay(day)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsWeekendDay returns if the day is a monday->friday.
|
// NASDAQClose is when NASDAQ closes.
|
||||||
func IsWeekendDay(day time.Weekday) bool {
|
NASDAQClose = ClockTime(16, 0, 0, 0, Eastern())
|
||||||
return day == time.Saturday || day == time.Sunday
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeforeDate returns if a timestamp is strictly before another date (ignoring hours, minutes etc.)
|
// NYSEArcaOpen is when NYSEARCA opens.
|
||||||
func BeforeDate(before, reference time.Time) bool {
|
NYSEArcaOpen = ClockTime(4, 0, 0, 0, Eastern())
|
||||||
if before.Year() < reference.Year() {
|
|
||||||
return true
|
// NYSEArcaClose is when NYSEARCA closes.
|
||||||
}
|
NYSEArcaClose = ClockTime(20, 0, 0, 0, Eastern())
|
||||||
if before.Month() < reference.Month() {
|
)
|
||||||
return true
|
|
||||||
}
|
// HolidayChecker is a function that returns if a given time falls on a holiday.
|
||||||
return before.Year() == reference.Year() && before.Month() == reference.Month() && before.Day() < reference.Day()
|
type HolidayChecker func(time.Time) bool
|
||||||
}
|
|
||||||
|
|
||||||
// IsNYSEHoliday returns if a date was/is on a nyse holiday day.
|
// IsNYSEHoliday returns if a date was/is on a nyse holiday day.
|
||||||
func IsNYSEHoliday(t time.Time) bool {
|
func IsNYSEHoliday(t time.Time) bool {
|
||||||
|
@ -203,24 +188,62 @@ func IsNYSEHoliday(t time.Time) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eastern returns the eastern timezone.
|
||||||
|
func Eastern() *time.Location {
|
||||||
|
if _eastern == nil {
|
||||||
|
_easternLock.Lock()
|
||||||
|
defer _easternLock.Unlock()
|
||||||
|
if _eastern == nil {
|
||||||
|
_eastern, _ = time.LoadLocation("America/New_York")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _eastern
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional returns a pointer reference to a given time.
|
||||||
|
func Optional(t time.Time) *time.Time {
|
||||||
|
return &t
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsWeekDay returns if the day is a monday->friday.
|
||||||
|
func IsWeekDay(day time.Weekday) bool {
|
||||||
|
return !IsWeekendDay(day)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsWeekendDay returns if the day is a monday->friday.
|
||||||
|
func IsWeekendDay(day time.Weekday) bool {
|
||||||
|
return day == time.Saturday || day == time.Sunday
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeDate returns if a timestamp is strictly before another date (ignoring hours, minutes etc.)
|
||||||
|
func BeforeDate(before, reference time.Time) bool {
|
||||||
|
if before.Year() < reference.Year() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if before.Month() < reference.Month() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return before.Year() == reference.Year() && before.Month() == reference.Month() && before.Day() < reference.Day()
|
||||||
|
}
|
||||||
|
|
||||||
// MarketOpen returns 0930 on a given day.
|
// MarketOpen returns 0930 on a given day.
|
||||||
func MarketOpen(on time.Time) time.Time {
|
func MarketOpen(on, openTime time.Time) time.Time {
|
||||||
onEastern := on.In(Eastern())
|
onEastern := on.In(Eastern())
|
||||||
return time.Date(onEastern.Year(), onEastern.Month(), onEastern.Day(), 9, 30, 0, 0, Eastern())
|
return On(openTime, onEastern)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarketClose returns 1600 on a given day.
|
// MarketClose returns 1600 on a given day.
|
||||||
func MarketClose(on time.Time) time.Time {
|
func MarketClose(on, closeTime time.Time) time.Time {
|
||||||
onEastern := on.In(Eastern())
|
onEastern := on.In(Eastern())
|
||||||
return time.Date(onEastern.Year(), onEastern.Month(), onEastern.Day(), 16, 0, 0, 0, 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.
|
// NextMarketOpen returns the next market open after a given time.
|
||||||
func NextMarketOpen(after time.Time) time.Time {
|
func NextMarketOpen(after, openTime time.Time, isHoliday HolidayChecker) time.Time {
|
||||||
afterEastern := after.In(Eastern())
|
afterEastern := after.In(Eastern())
|
||||||
todaysOpen := MarketOpen(afterEastern)
|
todaysOpen := MarketOpen(afterEastern, openTime)
|
||||||
|
|
||||||
if afterEastern.Before(todaysOpen) && IsWeekDay(todaysOpen.Weekday()) && !IsNYSEHoliday(todaysOpen) {
|
if afterEastern.Before(todaysOpen) && IsWeekDay(todaysOpen.Weekday()) && !isHoliday(todaysOpen) {
|
||||||
return todaysOpen
|
return todaysOpen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,19 +253,19 @@ func NextMarketOpen(after time.Time) time.Time {
|
||||||
|
|
||||||
for cursorDay := 1; cursorDay < 6; cursorDay++ {
|
for cursorDay := 1; cursorDay < 6; cursorDay++ {
|
||||||
newDay := todaysOpen.AddDate(0, 0, cursorDay)
|
newDay := todaysOpen.AddDate(0, 0, cursorDay)
|
||||||
if IsWeekDay(newDay.Weekday()) && !IsNYSEHoliday(afterEastern) {
|
if IsWeekDay(newDay.Weekday()) && !isHoliday(afterEastern) {
|
||||||
return time.Date(newDay.Year(), newDay.Month(), newDay.Day(), 9, 30, 0, 0, Eastern())
|
return On(openTime, newDay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Epoch //we should never reach this.
|
return Epoch //we should never reach this.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextMarketClose returns the next market close after a given time.
|
// NextMarketClose returns the next market close after a given time.
|
||||||
func NextMarketClose(after time.Time) time.Time {
|
func NextMarketClose(after, closeTime time.Time, isHoliday HolidayChecker) time.Time {
|
||||||
afterEastern := after.In(Eastern())
|
afterEastern := after.In(Eastern())
|
||||||
|
|
||||||
todaysClose := MarketClose(afterEastern)
|
todaysClose := MarketClose(afterEastern, closeTime)
|
||||||
if afterEastern.Before(todaysClose) && IsWeekDay(todaysClose.Weekday()) && !IsNYSEHoliday(todaysClose) {
|
if afterEastern.Before(todaysClose) && IsWeekDay(todaysClose.Weekday()) && !isHoliday(todaysClose) {
|
||||||
return todaysClose
|
return todaysClose
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,36 +275,36 @@ func NextMarketClose(after time.Time) time.Time {
|
||||||
|
|
||||||
for cursorDay := 1; cursorDay < 6; cursorDay++ {
|
for cursorDay := 1; cursorDay < 6; cursorDay++ {
|
||||||
newDay := todaysClose.AddDate(0, 0, cursorDay)
|
newDay := todaysClose.AddDate(0, 0, cursorDay)
|
||||||
if IsWeekDay(newDay.Weekday()) && !IsNYSEHoliday(newDay) {
|
if IsWeekDay(newDay.Weekday()) && !isHoliday(newDay) {
|
||||||
return time.Date(newDay.Year(), newDay.Month(), newDay.Day(), 16, 0, 0, 0, Eastern())
|
return On(closeTime, newDay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Epoch //we should never reach this.
|
return Epoch //we should never reach this.
|
||||||
}
|
}
|
||||||
|
|
||||||
// CalculateMarketSecondsBetween calculates the number of seconds the market was open between two dates.
|
// CalculateMarketSecondsBetween calculates the number of seconds the market was open between two dates.
|
||||||
func CalculateMarketSecondsBetween(start, end time.Time) (seconds int64) {
|
func CalculateMarketSecondsBetween(start, end, marketOpen, marketClose time.Time, isHoliday HolidayChecker) (seconds int64) {
|
||||||
se := start.In(Eastern())
|
se := start.In(Eastern())
|
||||||
ee := end.In(Eastern())
|
ee := end.In(Eastern())
|
||||||
|
|
||||||
startMarketOpen := NextMarketOpen(se)
|
startMarketOpen := NextMarketOpen(se, marketOpen, isHoliday)
|
||||||
startMarketClose := NextMarketClose(se)
|
startMarketClose := NextMarketClose(se, marketClose, isHoliday)
|
||||||
|
|
||||||
if (se.Equal(startMarketOpen) || se.After(startMarketOpen)) && se.Before(startMarketClose) {
|
if (se.Equal(startMarketOpen) || se.After(startMarketOpen)) && se.Before(startMarketClose) {
|
||||||
seconds += int64(startMarketClose.Sub(se) / time.Second)
|
seconds += int64(startMarketClose.Sub(se) / time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor := NextMarketOpen(startMarketClose)
|
cursor := NextMarketOpen(startMarketClose, marketClose, isHoliday)
|
||||||
for BeforeDate(cursor, ee) {
|
for BeforeDate(cursor, ee) {
|
||||||
if IsWeekDay(cursor.Weekday()) && !IsNYSEHoliday(cursor) {
|
if IsWeekDay(cursor.Weekday()) && !isHoliday(cursor) {
|
||||||
close := NextMarketClose(cursor)
|
close := NextMarketClose(cursor, marketClose, isHoliday)
|
||||||
seconds += int64(close.Sub(cursor) / time.Second)
|
seconds += int64(close.Sub(cursor) / time.Second)
|
||||||
}
|
}
|
||||||
cursor = cursor.AddDate(0, 0, 1)
|
cursor = cursor.AddDate(0, 0, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
finalMarketOpen := NextMarketOpen(cursor)
|
finalMarketOpen := NextMarketOpen(cursor, marketOpen, isHoliday)
|
||||||
finalMarketClose := NextMarketClose(cursor)
|
finalMarketClose := NextMarketClose(cursor, marketClose, isHoliday)
|
||||||
if end.After(finalMarketOpen) {
|
if end.After(finalMarketOpen) {
|
||||||
if end.Before(finalMarketClose) {
|
if end.Before(finalMarketClose) {
|
||||||
seconds += int64(end.Sub(finalMarketOpen) / time.Second)
|
seconds += int64(end.Sub(finalMarketOpen) / time.Second)
|
||||||
|
@ -293,6 +316,16 @@ func CalculateMarketSecondsBetween(start, end time.Time) (seconds int64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClockTime returns a new time.Time for the given clock components.
|
||||||
|
func ClockTime(hour, min, sec, nsec int, loc *time.Location) time.Time {
|
||||||
|
return time.Date(0, 0, 0, hour, min, sec, nsec, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On returns the clock components of clock (hour,minute,second) on the date components of d.
|
||||||
|
func On(clock, d time.Time) time.Time {
|
||||||
|
return time.Date(d.Year(), d.Month(), d.Day(), clock.Hour(), clock.Minute(), clock.Second(), clock.Nanosecond(), clock.Location())
|
||||||
|
}
|
||||||
|
|
||||||
// Format returns a string representation of a date.
|
// Format returns a string representation of a date.
|
||||||
func format(t time.Time) string {
|
func format(t time.Time) string {
|
||||||
return t.Format("2006-01-02")
|
return t.Format("2006-01-02")
|
|
@ -17,7 +17,7 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
|
||||||
Style: chart.Style{
|
Style: chart.Style{
|
||||||
Show: true,
|
Show: true,
|
||||||
},
|
},
|
||||||
Range: chart.Range{
|
Range: &chart.ContinuousRange{
|
||||||
Min: 0.0,
|
Min: 0.0,
|
||||||
Max: 4.0,
|
Max: 4.0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,7 +43,7 @@ func (mhr *NYSEMarketHoursRange) SetMax(max float64) {
|
||||||
// GetDelta gets the delta.
|
// GetDelta gets the delta.
|
||||||
func (mhr NYSEMarketHoursRange) GetDelta() float64 {
|
func (mhr NYSEMarketHoursRange) GetDelta() float64 {
|
||||||
min := TimeToFloat64(mhr.Min)
|
min := TimeToFloat64(mhr.Min)
|
||||||
max := TimeToFloat64(mhr.Min)
|
max := TimeToFloat64(mhr.Max)
|
||||||
return max - min
|
return max - min
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,5 +66,8 @@ func (mhr NYSEMarketHoursRange) Translate(value float64) int {
|
||||||
valueTime := Float64ToTime(value)
|
valueTime := Float64ToTime(value)
|
||||||
deltaSeconds := date.CalculateMarketSecondsBetween(mhr.Min, mhr.Max)
|
deltaSeconds := date.CalculateMarketSecondsBetween(mhr.Min, mhr.Max)
|
||||||
valueDelta := date.CalculateMarketSecondsBetween(mhr.Min, valueTime)
|
valueDelta := date.CalculateMarketSecondsBetween(mhr.Min, valueTime)
|
||||||
return int(float64(valueDelta) / float64(deltaSeconds))
|
|
||||||
|
translated := int((float64(valueDelta) / float64(deltaSeconds)) * float64(mhr.Domain))
|
||||||
|
fmt.Printf("nyse translating: %s to %d ~= %d", valueTime.Format(time.RFC3339), deltaSeconds, valueDelta)
|
||||||
|
return translated
|
||||||
}
|
}
|
||||||
|
|
20
nyse_market_hours_range_test.go
Normal file
20
nyse_market_hours_range_test.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
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())
|
||||||
|
}
|
Loading…
Reference in a new issue