diff --git a/cointop/chart.go b/cointop/chart.go index 4b8d36d..7566161 100644 --- a/cointop/chart.go +++ b/cointop/chart.go @@ -172,23 +172,28 @@ func (ct *Cointop) ChartPoints(symbol string, name string) error { } } - // Resample cachedata - timeQuantum := timedata.CalculateTimeQuantum(cacheData) - newStart := time.Unix(start, 0).Add(timeQuantum) - newEnd := time.Unix(end, 0).Add(-timeQuantum) - timeData := timedata.ResampleTimeSeriesData(cacheData, float64(newStart.UnixMilli()), float64(newEnd.UnixMilli()), chart.GetChartDataSize(maxX)) - - // Extract just the values from the data + var labels []string var data []float64 - for i := range timeData { - value := timeData[i][1] - if math.IsNaN(value) { - value = 0.0 + timeQuantum := timedata.CalculateTimeQuantum(cacheData) // will be 0 if <2 points + if timeQuantum > 0 { + // Resample cachedata + newStart := cacheData[0][0] // use the first data point + newEnd := time.Unix(end, 0).Add(-timeQuantum) + timeData := timedata.ResampleTimeSeriesData(cacheData, newStart, float64(newEnd.UnixMilli()), chart.GetChartDataSize(maxX)) + labels = timedata.BuildTimeSeriesLabels(timeData) + + // Extract just the values from the data + for i := range timeData { + value := timeData[i][1] + if math.IsNaN(value) { + value = 0.0 + } + data = append(data, value) } - data = append(data, value) } chart.SetData(data) + chart.SetDataLabels(labels) ct.State.chartPoints = chart.GetChartPoints(maxX) return nil @@ -280,39 +285,48 @@ func (ct *Cointop) PortfolioChart() error { break // use the first one } } - newStart := time.Unix(start, 0).Add(timeQuantum) - newEnd := time.Unix(end, 0).Add(-timeQuantum) - // Resample and sum data + // If there is data, resample and sum var data []float64 - for _, cacheData := range allCacheData { - coinData := timedata.ResampleTimeSeriesData(cacheData.data, float64(newStart.UnixMilli()), float64(newEnd.UnixMilli()), chart.GetChartDataSize(maxX)) - // sum (excluding NaN) - for i := range coinData { - price := coinData[i][1] - if math.IsNaN(price) { - price = 0.0 + var labels []string + if timeQuantum > 0 { + newStart := time.Unix(start, 0).Add(timeQuantum) + newEnd := time.Unix(end, 0).Add(-timeQuantum) + + // Resample and sum data + for i, cacheData := range allCacheData { + coinData := timedata.ResampleTimeSeriesData(cacheData.data, float64(newStart.UnixMilli()), float64(newEnd.UnixMilli()), chart.GetChartDataSize(maxX)) + if i == 0 { + labels = timedata.BuildTimeSeriesLabels(coinData) } - sum := cacheData.coin.Holdings * price - if i < len(data) { - data[i] += sum - } else { - data = append(data, sum) + // sum (excluding NaN) + for i := range coinData { + price := coinData[i][1] + if math.IsNaN(price) { + price = 0.0 + } + sum := cacheData.coin.Holdings * price + if i < len(data) { + data[i] += sum + } else { + data = append(data, sum) + } } } - } - // Scale Portfolio Balances to hide value - if ct.State.hidePortfolioBalances { - var lastPrice = data[len(data)-1] - if lastPrice > 0.0 { - for i, price := range data { - data[i] = 100 * price / lastPrice + // Scale Portfolio Balances to hide value + if ct.State.hidePortfolioBalances { + var lastPrice = data[len(data)-1] + if lastPrice > 0.0 { + for i, price := range data { + data[i] = 100 * price / lastPrice + } } } } chart.SetData(data) + chart.SetDataLabels(labels) ct.State.chartPoints = chart.GetChartPoints(maxX) return nil diff --git a/pkg/chartplot/chartplot.go b/pkg/chartplot/chartplot.go index 24fb2fb..33b3884 100644 --- a/pkg/chartplot/chartplot.go +++ b/pkg/chartplot/chartplot.go @@ -55,6 +55,11 @@ func (c *ChartPlot) SetData(data []float64) { c.t.Data = data } +// SetDataLabels ... +func (c *ChartPlot) SetDataLabels(labels []string) { + c.t.DataLabels = labels +} + // GetChartDataSize ... func (c *ChartPlot) GetChartDataSize(width int) int { axisYWidth := 30 diff --git a/pkg/timedata/timedata.go b/pkg/timedata/timedata.go index c605fdc..8e25ded 100644 --- a/pkg/timedata/timedata.go +++ b/pkg/timedata/timedata.go @@ -4,11 +4,9 @@ import ( "math" "sort" "time" - - log "github.com/sirupsen/logrus" ) -// Resample the [timestamp,value] data given to numsteps between start-end (returns numSteps+1 points). +// ResampleTimeSeriesData resamples the given [timestamp,value] data to numsteps between start-end (returns numSteps+1 points). // If the data does not extend past start/end then there will likely be NaN in the output data. func ResampleTimeSeriesData(data [][]float64, start float64, end float64, numSteps int) [][]float64 { var newData [][]float64 @@ -33,7 +31,7 @@ func ResampleTimeSeriesData(data [][]float64, start float64, end float64, numSte return newData } -// Assuming that the [timestamp,value] data provided is roughly evenly spaced, calculate that interval. +// CalculateTimeQuantum determines the given [timestamp,value] data func CalculateTimeQuantum(data [][]float64) time.Duration { if len(data) > 1 { minTime := time.UnixMilli(int64(data[0][0])) @@ -43,9 +41,25 @@ func CalculateTimeQuantum(data [][]float64) time.Duration { return 0 } -// Print out all the [timestamp,value] data provided -func DebugLogPriceData(data [][]float64) { +// BuildTimeSeriesLabels returns a list of short labels representing time values from the given [timestamp,value] data +func BuildTimeSeriesLabels(data [][]float64) []string { + minTime := time.UnixMilli(int64(data[0][0])) + maxTime := time.UnixMilli(int64(data[len(data)-1][0])) + timeRange := maxTime.Sub(minTime) + + var timeFormat string + if timeRange.Hours() < 24 { + timeFormat = "15:04" + } else if timeRange.Hours() < 24*7 { + timeFormat = "Mon 15:04" + } else if timeRange.Hours() < 24*365 { + timeFormat = "02-Jan" + } else { + timeFormat = "Jan 2006" + } + var labels []string for i := range data { - log.Debugf("%s %.2f", time.Unix(int64(data[i][0]/1000), 0), data[i][1]) + labels = append(labels, time.UnixMilli(int64(data[i][0])).Format(timeFormat)) } + return labels }