fixing macd and ema performance issues.

This commit is contained in:
Will Charczuk 2016-07-18 18:29:08 -07:00
parent befd626bbe
commit 8af50213c3
2 changed files with 84 additions and 48 deletions

View file

@ -13,6 +13,8 @@ type EMASeries struct {
Period int Period int
InnerSeries ValueProvider InnerSeries ValueProvider
cache []float64
} }
// GetName returns the name of the time series. // GetName returns the name of the time series.
@ -49,39 +51,51 @@ func (ema EMASeries) GetSigma() float64 {
} }
// GetValue gets a value at a given index. // GetValue gets a value at a given index.
func (ema EMASeries) GetValue(index int) (x, y float64) { func (ema *EMASeries) GetValue(index int) (x, y float64) {
if ema.InnerSeries == nil { if ema.InnerSeries == nil {
return return
} }
if len(ema.cache) == 0 {
ema.ensureCachedValues()
}
vx, _ := ema.InnerSeries.GetValue(index) vx, _ := ema.InnerSeries.GetValue(index)
x = vx x = vx
y = ema.compute(ema.GetPeriod(), index) y = ema.cache[index]
return return
} }
// GetLastValue computes the last moving average value but walking back window size samples, // GetLastValue computes the last moving average value but walking back window size samples,
// and recomputing the last moving average chunk. // and recomputing the last moving average chunk.
func (ema EMASeries) GetLastValue() (x, y float64) { func (ema *EMASeries) GetLastValue() (x, y float64) {
if ema.InnerSeries == nil { if ema.InnerSeries == nil {
return return
} }
if len(ema.cache) == 0 {
ema.ensureCachedValues()
}
lastIndex := ema.InnerSeries.Len() - 1 lastIndex := ema.InnerSeries.Len() - 1
x, _ = ema.InnerSeries.GetValue(lastIndex) x, _ = ema.InnerSeries.GetValue(lastIndex)
y = ema.compute(ema.GetPeriod(), lastIndex) y = ema.cache[lastIndex]
return return
} }
func (ema EMASeries) compute(period, index int) float64 { func (ema *EMASeries) ensureCachedValues() {
_, v := ema.InnerSeries.GetValue(index) seriesLength := ema.InnerSeries.Len()
if index == 0 { ema.cache = make([]float64, seriesLength)
return v sigma := ema.GetSigma()
for x := 0; x < seriesLength; x++ {
_, y := ema.InnerSeries.GetValue(x)
if x == 0 {
ema.cache[x] = y
continue
}
previousEMA := ema.cache[x-1]
ema.cache[x] = ((y - previousEMA) * sigma) + previousEMA
} }
previousEMA := ema.compute(period-1, index-1)
return ((v - previousEMA) * ema.GetSigma()) + previousEMA
} }
// Render renders the series. // Render renders the series.
func (ema EMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) { func (ema *EMASeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
style := ema.Style.WithDefaultsFrom(defaults) style := ema.Style.WithDefaultsFrom(defaults)
DrawLineSeries(r, canvasBox, xrange, yrange, style, ema) DrawLineSeries(r, canvasBox, xrange, yrange, style, ema)
} }

View file

@ -20,6 +20,9 @@ type MACDSeries struct {
PrimaryPeriod int PrimaryPeriod int
SecondaryPeriod int SecondaryPeriod int
SignalPeriod int SignalPeriod int
signal *MACDSignalSeries
macdl *MACDLineSeries
} }
// GetPeriods returns the primary and secondary periods. // GetPeriods returns the primary and secondary periods.
@ -67,33 +70,39 @@ func (macd MACDSeries) Len() int {
} }
// GetValue gets a value at a given index. For MACD it is the signal value. // GetValue gets a value at a given index. For MACD it is the signal value.
func (macd MACDSeries) GetValue(index int) (x float64, y float64) { func (macd *MACDSeries) GetValue(index int) (x float64, y float64) {
if macd.InnerSeries == nil { if macd.InnerSeries == nil {
return return
} }
w1, w2, sig := macd.GetPeriods() if macd.signal == nil || macd.macdl == nil {
macd.ensureChildSeries()
}
_, lv := macd.macdl.GetValue(index)
_, sv := macd.signal.GetValue(index)
x, _ = macd.InnerSeries.GetValue(index) x, _ = macd.InnerSeries.GetValue(index)
y = lv - sv
signal := MACDSignalSeries{ return
}
func (macd *MACDSeries) ensureChildSeries() {
w1, w2, sig := macd.GetPeriods()
macd.signal = &MACDSignalSeries{
InnerSeries: macd.InnerSeries, InnerSeries: macd.InnerSeries,
PrimaryPeriod: w1, PrimaryPeriod: w1,
SecondaryPeriod: w2, SecondaryPeriod: w2,
SignalPeriod: sig, SignalPeriod: sig,
} }
macdl := MACDLineSeries{ macd.macdl = &MACDLineSeries{
InnerSeries: macd.InnerSeries, InnerSeries: macd.InnerSeries,
PrimaryPeriod: w1, PrimaryPeriod: w1,
SecondaryPeriod: w2, SecondaryPeriod: w2,
} }
_, lv := macdl.GetValue(index)
_, sv := signal.GetValue(index)
y = lv - sv
return
} }
// MACDSignalSeries computes the EMA of the MACDLineSeries. // MACDSignalSeries computes the EMA of the MACDLineSeries.
@ -106,6 +115,8 @@ type MACDSignalSeries struct {
PrimaryPeriod int PrimaryPeriod int
SecondaryPeriod int SecondaryPeriod int
SignalPeriod int SignalPeriod int
signal *EMASeries
} }
// GetPeriods returns the primary and secondary periods. // GetPeriods returns the primary and secondary periods.
@ -144,7 +155,7 @@ func (macds MACDSignalSeries) GetYAxis() YAxisType {
} }
// Len returns the number of elements in the series. // Len returns the number of elements in the series.
func (macds MACDSignalSeries) Len() int { func (macds *MACDSignalSeries) Len() int {
if macds.InnerSeries == nil { if macds.InnerSeries == nil {
return 0 return 0
} }
@ -153,30 +164,34 @@ func (macds MACDSignalSeries) Len() int {
} }
// GetValue gets a value at a given index. For MACD it is the signal value. // GetValue gets a value at a given index. For MACD it is the signal value.
func (macds MACDSignalSeries) GetValue(index int) (x float64, y float64) { func (macds *MACDSignalSeries) GetValue(index int) (x float64, y float64) {
if macds.InnerSeries == nil { if macds.InnerSeries == nil {
return return
} }
if macds.signal == nil {
macds.ensureSignal()
}
x, _ = macds.InnerSeries.GetValue(index)
_, y = macds.signal.GetValue(index)
return
}
func (macds *MACDSignalSeries) ensureSignal() {
w1, w2, sig := macds.GetPeriods() w1, w2, sig := macds.GetPeriods()
x, _ = macds.InnerSeries.GetValue(index) macds.signal = &EMASeries{
InnerSeries: &MACDLineSeries{
signal := EMASeries{
InnerSeries: MACDLineSeries{
InnerSeries: macds.InnerSeries, InnerSeries: macds.InnerSeries,
PrimaryPeriod: w1, PrimaryPeriod: w1,
SecondaryPeriod: w2, SecondaryPeriod: w2,
}, },
Period: sig, Period: sig,
} }
_, y = signal.GetValue(index)
return
} }
// Render renders the series. // Render renders the series.
func (macds MACDSignalSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) { func (macds *MACDSignalSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
style := macds.Style.WithDefaultsFrom(defaults) style := macds.Style.WithDefaultsFrom(defaults)
DrawLineSeries(r, canvasBox, xrange, yrange, style, macds) DrawLineSeries(r, canvasBox, xrange, yrange, style, macds)
} }
@ -191,6 +206,9 @@ type MACDLineSeries struct {
PrimaryPeriod int PrimaryPeriod int
SecondaryPeriod int SecondaryPeriod int
ema1 *EMASeries
ema2 *EMASeries
Sigma float64 Sigma float64
} }
@ -225,7 +243,7 @@ func (macdl MACDLineSeries) GetPeriods() (w1, w2 int) {
} }
// Len returns the number of elements in the series. // Len returns the number of elements in the series.
func (macdl MACDLineSeries) Len() int { func (macdl *MACDLineSeries) Len() int {
if macdl.InnerSeries == nil { if macdl.InnerSeries == nil {
return 0 return 0
} }
@ -234,34 +252,38 @@ func (macdl MACDLineSeries) Len() int {
} }
// GetValue gets a value at a given index. For MACD it is the signal value. // GetValue gets a value at a given index. For MACD it is the signal value.
func (macdl MACDLineSeries) GetValue(index int) (x float64, y float64) { func (macdl *MACDLineSeries) GetValue(index int) (x float64, y float64) {
if macdl.InnerSeries == nil { if macdl.InnerSeries == nil {
return return
} }
if macdl.ema1 == nil && macdl.ema2 == nil {
w1, w2 := macdl.GetPeriods() macdl.ensureEMASeries()
}
x, _ = macdl.InnerSeries.GetValue(index) x, _ = macdl.InnerSeries.GetValue(index)
ema1 := EMASeries{ _, emav1 := macdl.ema1.GetValue(index)
InnerSeries: macdl.InnerSeries, _, emav2 := macdl.ema2.GetValue(index)
Period: w1,
}
ema2 := EMASeries{
InnerSeries: macdl.InnerSeries,
Period: w2,
}
_, emav1 := ema1.GetValue(index)
_, emav2 := ema2.GetValue(index)
y = emav2 - emav1 y = emav2 - emav1
return return
} }
func (macdl *MACDLineSeries) ensureEMASeries() {
w1, w2 := macdl.GetPeriods()
macdl.ema1 = &EMASeries{
InnerSeries: macdl.InnerSeries,
Period: w1,
}
macdl.ema2 = &EMASeries{
InnerSeries: macdl.InnerSeries,
Period: w2,
}
}
// Render renders the series. // Render renders the series.
func (macdl MACDLineSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) { func (macdl *MACDLineSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
style := macdl.Style.WithDefaultsFrom(defaults) style := macdl.Style.WithDefaultsFrom(defaults)
DrawLineSeries(r, canvasBox, xrange, yrange, style, macdl) DrawLineSeries(r, canvasBox, xrange, yrange, style, macdl)
} }