2016-07-22 01:09:09 -04:00
|
|
|
package chart
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2016-07-23 14:50:30 -04:00
|
|
|
// MarketHoursRange is a special type of range that compresses a time range into just the
|
2016-07-22 01:09:09 -04:00
|
|
|
// market (i.e. NYSE operating hours and days) range.
|
2016-07-23 14:50:30 -04:00
|
|
|
type MarketHoursRange struct {
|
|
|
|
Min time.Time
|
|
|
|
Max time.Time
|
|
|
|
|
|
|
|
MarketOpen time.Time
|
|
|
|
MarketClose time.Time
|
|
|
|
|
2016-07-31 19:54:09 -04:00
|
|
|
HolidayProvider HolidayProvider
|
2016-07-23 14:50:30 -04:00
|
|
|
|
2016-07-27 03:20:43 -04:00
|
|
|
ValueFormatter ValueFormatter
|
|
|
|
|
2016-07-22 01:09:09 -04:00
|
|
|
Domain int
|
|
|
|
}
|
|
|
|
|
2016-07-22 01:22:22 -04:00
|
|
|
// IsZero returns if the range is setup or not.
|
2016-07-23 14:50:30 -04:00
|
|
|
func (mhr MarketHoursRange) IsZero() bool {
|
2016-07-22 01:22:22 -04:00
|
|
|
return mhr.Min.IsZero() && mhr.Max.IsZero()
|
|
|
|
}
|
|
|
|
|
2016-07-22 01:09:09 -04:00
|
|
|
// GetMin returns the min value.
|
2016-07-23 14:50:30 -04:00
|
|
|
func (mhr MarketHoursRange) GetMin() float64 {
|
2016-07-22 01:09:09 -04:00
|
|
|
return TimeToFloat64(mhr.Min)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetMax returns the max value.
|
2016-07-23 14:50:30 -04:00
|
|
|
func (mhr MarketHoursRange) GetMax() float64 {
|
2016-07-27 11:21:05 -04:00
|
|
|
return TimeToFloat64(mhr.GetEffectiveMax())
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetEffectiveMax gets either the close on the max, or the max itself.
|
|
|
|
func (mhr MarketHoursRange) GetEffectiveMax() time.Time {
|
2016-07-31 19:54:09 -04:00
|
|
|
maxClose := Date.On(mhr.MarketClose, mhr.Max)
|
2016-07-27 11:21:05 -04:00
|
|
|
if maxClose.After(mhr.Max) {
|
|
|
|
return maxClose
|
|
|
|
}
|
|
|
|
return mhr.Max
|
2016-07-22 01:09:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetMin sets the min value.
|
2016-07-23 14:50:30 -04:00
|
|
|
func (mhr *MarketHoursRange) SetMin(min float64) {
|
2016-07-22 01:09:09 -04:00
|
|
|
mhr.Min = Float64ToTime(min)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetMax sets the max value.
|
2016-07-23 14:50:30 -04:00
|
|
|
func (mhr *MarketHoursRange) SetMax(max float64) {
|
2016-07-22 01:09:09 -04:00
|
|
|
mhr.Max = Float64ToTime(max)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetDelta gets the delta.
|
2016-07-23 14:50:30 -04:00
|
|
|
func (mhr MarketHoursRange) GetDelta() float64 {
|
2016-07-27 11:21:05 -04:00
|
|
|
min := mhr.GetMin()
|
|
|
|
max := mhr.GetMax()
|
2016-07-22 01:09:09 -04:00
|
|
|
return max - min
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetDomain gets the domain.
|
2016-07-23 14:50:30 -04:00
|
|
|
func (mhr MarketHoursRange) GetDomain() int {
|
2016-07-22 01:09:09 -04:00
|
|
|
return mhr.Domain
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetDomain sets the domain.
|
2016-07-23 14:50:30 -04:00
|
|
|
func (mhr *MarketHoursRange) SetDomain(domain int) {
|
2016-07-22 01:09:09 -04:00
|
|
|
mhr.Domain = domain
|
|
|
|
}
|
|
|
|
|
2016-07-23 18:35:49 -04:00
|
|
|
// GetHolidayProvider coalesces a userprovided holiday provider and the date.DefaultHolidayProvider.
|
2016-07-31 19:54:09 -04:00
|
|
|
func (mhr MarketHoursRange) GetHolidayProvider() HolidayProvider {
|
2016-07-23 18:35:49 -04:00
|
|
|
if mhr.HolidayProvider == nil {
|
2016-07-31 19:54:09 -04:00
|
|
|
return defaultHolidayProvider
|
2016-07-23 18:35:49 -04:00
|
|
|
}
|
|
|
|
return mhr.HolidayProvider
|
|
|
|
}
|
|
|
|
|
2016-07-31 19:54:09 -04:00
|
|
|
// GetMarketOpen returns the market open time.
|
|
|
|
func (mhr MarketHoursRange) GetMarketOpen() time.Time {
|
|
|
|
if mhr.MarketOpen.IsZero() {
|
|
|
|
return NYSEOpen
|
|
|
|
}
|
|
|
|
return mhr.MarketOpen
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetMarketClose returns the market close time.
|
|
|
|
func (mhr MarketHoursRange) GetMarketClose() time.Time {
|
|
|
|
if mhr.MarketClose.IsZero() {
|
|
|
|
return NYSEClose
|
|
|
|
}
|
|
|
|
return mhr.MarketClose
|
|
|
|
}
|
|
|
|
|
2016-07-23 18:35:49 -04:00
|
|
|
// GetTicks returns the ticks for the range.
|
|
|
|
// This is to override the default continous ticks that would be generated for the range.
|
2016-07-31 19:54:09 -04:00
|
|
|
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())
|
|
|
|
timesWidth := mhr.measureTimes(r, defaults, vf, times)
|
|
|
|
if timesWidth <= mhr.Domain {
|
|
|
|
return mhr.makeTicks(vf, times)
|
|
|
|
}
|
|
|
|
times = Sequence.MarketHourQuarters(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)
|
2016-07-23 18:35:49 -04:00
|
|
|
}
|
2016-07-31 19:54:09 -04:00
|
|
|
return mhr.makeTicks(vf, Sequence.MarketDayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider()))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (mhr *MarketHoursRange) measureTimes(r Renderer, defaults Style, vf ValueFormatter, times []time.Time) int {
|
|
|
|
defaults.GetTextOptions().WriteToRenderer(r)
|
|
|
|
var total int
|
|
|
|
for index, t := range times {
|
|
|
|
timeLabel := vf(t)
|
2016-07-23 18:35:49 -04:00
|
|
|
|
2016-07-31 19:54:09 -04:00
|
|
|
labelBox := r.MeasureText(timeLabel)
|
|
|
|
total += labelBox.Width()
|
|
|
|
if index > 0 {
|
|
|
|
total += DefaultMinimumTickHorizontalSpacing
|
|
|
|
}
|
2016-07-24 16:48:10 -04:00
|
|
|
}
|
2016-07-31 19:54:09 -04:00
|
|
|
return total
|
|
|
|
}
|
2016-07-31 00:34:41 -04:00
|
|
|
|
2016-07-31 19:54:09 -04:00
|
|
|
func (mhr *MarketHoursRange) makeTicks(vf ValueFormatter, times []time.Time) []Tick {
|
|
|
|
ticks := make([]Tick, len(times))
|
|
|
|
for index, t := range times {
|
|
|
|
ticks[index] = Tick{
|
|
|
|
Value: TimeToFloat64(t),
|
|
|
|
Label: vf(t),
|
|
|
|
}
|
|
|
|
println("make tick =>", vf(t))
|
|
|
|
}
|
2016-07-23 18:35:49 -04:00
|
|
|
return ticks
|
|
|
|
}
|
|
|
|
|
2016-07-23 14:50:30 -04:00
|
|
|
func (mhr MarketHoursRange) String() string {
|
2016-07-27 03:20:43 -04:00
|
|
|
return fmt.Sprintf("MarketHoursRange [%s, %s] => %d", mhr.Min.Format(DefaultDateMinuteFormat), mhr.Max.Format(DefaultDateMinuteFormat), mhr.Domain)
|
2016-07-22 01:09:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Translate maps a given value into the ContinuousRange space.
|
2016-07-23 14:50:30 -04:00
|
|
|
func (mhr MarketHoursRange) Translate(value float64) int {
|
2016-07-22 01:09:09 -04:00
|
|
|
valueTime := Float64ToTime(value)
|
2016-07-31 19:54:09 -04:00
|
|
|
valueTimeEastern := valueTime.In(Date.Eastern())
|
|
|
|
totalSeconds := Date.CalculateMarketSecondsBetween(mhr.Min, mhr.GetEffectiveMax(), mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
|
|
|
|
valueDelta := Date.CalculateMarketSecondsBetween(mhr.Min, valueTimeEastern, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
|
2016-07-27 03:34:10 -04:00
|
|
|
translated := int((float64(valueDelta) / float64(totalSeconds)) * float64(mhr.Domain))
|
2016-07-23 01:43:27 -04:00
|
|
|
return translated
|
2016-07-22 01:09:09 -04:00
|
|
|
}
|