Skip to content

Commit

Permalink
feature: add some new ma indicators
Browse files Browse the repository at this point in the history
  • Loading branch information
zenixls2 committed Apr 19, 2022
1 parent fcaef02 commit 22d8c2e
Show file tree
Hide file tree
Showing 16 changed files with 474 additions and 28 deletions.
68 changes: 68 additions & 0 deletions pkg/indicator/dema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package indicator

import (
"github.com/c9s/bbgo/pkg/types"
)

// Refer: Double Exponential Moving Average
// Refer URL: https://investopedia.com/terms/d/double-exponential-moving-average.asp

//go:generate callbackgen -type DEMA
type DEMA struct {
types.IntervalWindow
Values types.Float64Slice
a1 *EWMA
a2 *EWMA

UpdateCallbacks []func(value float64)
}

func (inc *DEMA) Update(value float64) {
if len(inc.Values) == 0 {
inc.a1 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
inc.a2 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
}

inc.a1.Update(value)
inc.a2.Update(inc.a1.Last())
inc.Values.Push(2*inc.a1.Last() - inc.a2.Last())
if len(inc.Values) > MaxNumOfEWMA {
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
}
}

func (inc *DEMA) Last() float64 {
return inc.Values.Last()
}

func (inc *DEMA) Index(i int) float64 {
if len(inc.Values)-i-1 >= 0 {
return inc.Values[len(inc.Values)-1-i]
}
return 0
}

func (inc *DEMA) Length() int {
return len(inc.Values)
}

var _ types.Series = &DEMA{}

func (inc *DEMA) calculateAndUpdate(allKLines []types.KLine) {
for _, k := range allKLines {
inc.Update(k.Close.Float64())
inc.EmitUpdate(inc.Last())
}
}

func (inc *DEMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
if inc.Interval != interval {
return
}

inc.calculateAndUpdate(window)
}

func (inc *DEMA) Bind(updater KLineWindowUpdater) {
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
}
15 changes: 15 additions & 0 deletions pkg/indicator/dema_callbacks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 0 additions & 26 deletions pkg/indicator/ewma.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,32 +124,6 @@ func ewma(prices []float64, multiplier float64) float64 {
return prices[end]*multiplier + (1-multiplier)*ewma(prices[:end], multiplier)
}

type KLinePriceMapper func(k types.KLine) float64

func KLineOpenPriceMapper(k types.KLine) float64 {
return k.Open.Float64()
}

func KLineClosePriceMapper(k types.KLine) float64 {
return k.Close.Float64()
}

func KLineTypicalPriceMapper(k types.KLine) float64 {
return (k.High.Float64() + k.Low.Float64() + k.Close.Float64()) / 3.
}

func MapKLinePrice(kLines []types.KLine, f KLinePriceMapper) (prices []float64) {
for _, k := range kLines {
prices = append(prices, f(k))
}

return prices
}

type KLineWindowUpdater interface {
OnKLineWindowUpdate(func(interval types.Interval, window types.KLineWindow))
}

func (inc *EWMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
if inc.Interval != interval {
return
Expand Down
73 changes: 73 additions & 0 deletions pkg/indicator/hull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package indicator

import (
"math"

"github.com/c9s/bbgo/pkg/types"
)

// Refer: Hull Moving Average
// Refer URL: https://fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/hull-moving-average
//go:generate callbackgen -type HULL
type HULL struct {
types.IntervalWindow
ma1 *EWMA
ma2 *EWMA
result *EWMA

UpdateCallbacks []func(value float64)
}

func (inc *HULL) Update(value float64) {
if inc.result.Length() == 0 {
inc.ma1 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window/2}}
inc.ma2 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
inc.result = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, int(math.Sqrt(float64(inc.Window)))}}
}
inc.ma1.Update(value)
inc.ma2.Update(value)
inc.result.Update(2 * inc.ma1.Last() - inc.ma2.Last())
}

func (inc *HULL) Last() float64 {
return inc.result.Last()
}

func (inc *HULL) Index(i int) float64 {
return inc.result.Index(i)
}

func (inc *HULL) Length() int {
return inc.result.Length()
}

var _ types.Series = &HULL{}

// TODO: should we just ignore the possible overlapping?
func (inc *HULL) calculateAndUpdate(allKLines []types.KLine) {
doable := false
if inc.ma1.Length() == 0 {
doable = true
}
for _, k := range allKLines {
if !doable && k.StartTime.After(inc.ma1.LastOpenTime) {
doable = true
}
if doable {
inc.Update(k.Close.Float64())
inc.EmitUpdate(inc.Last())
}
}
}

func (inc *HULL) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
if inc.Interval != interval {
return
}

inc.calculateAndUpdate(window)
}

func (inc *HULL) Bind(updater KLineWindowUpdater) {
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
}
15 changes: 15 additions & 0 deletions pkg/indicator/hull_callbacks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/indicator/line.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ func (l *Line) Bind(updater KLineWindowUpdater) {
}

func (l *Line) Last() float64 {
return (l.end-l.start) / float64(l.startIndex - l.endIndex) * float64(l.endIndex) + l.end
return (l.end-l.start)/float64(l.startIndex-l.endIndex)*float64(l.endIndex) + l.end
}

func (l *Line) Index(i int) float64 {
return (l.end-l.start) / float64(l.startIndex - l.endIndex) * float64(l.endIndex - i) + l.end
return (l.end-l.start)/float64(l.startIndex-l.endIndex)*float64(l.endIndex-i) + l.end
}

func (l *Line) Length() int {
Expand Down
73 changes: 73 additions & 0 deletions pkg/indicator/tema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package indicator

import (
"github.com/c9s/bbgo/pkg/types"
)

// Refer: Triple Exponential Moving Average (TEMA)
// URL: https://investopedia.com/terms/t/triple-exponential-moving-average.asp

//go:generate callbackgen -type TEMA
type TEMA struct {
types.IntervalWindow
Values types.Float64Slice
A1 *EWMA
A2 *EWMA
A3 *EWMA

UpdateCallbacks []func(value float64)
}

func (inc *TEMA) Update(value float64) {
if len(inc.Values) == 0 {
inc.A1 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
inc.A2 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
inc.A3 = &EWMA{IntervalWindow: types.IntervalWindow{inc.Interval, inc.Window}}
}
inc.A1.Update(value)
a1 := inc.A1.Last()
inc.A2.Update(a1)
a2 := inc.A2.Last()
inc.A3.Update(a2)
a3 := inc.A3.Last()
inc.Values.Push(3*a1 - 3*a2 + a3)
}

func (inc *TEMA) Last() float64 {
if len(inc.Values) > 0 {
return inc.Values[len(inc.Values)-1]
}
return 0.0
}

func (inc *TEMA) Index(i int) float64 {
if i >= len(inc.Values) {
return 0
}
return inc.Values[len(inc.Values)-i-1]
}

func (inc *TEMA) Length() int {
return len(inc.Values)
}

var _ types.Series = &TEMA{}

func (inc *TEMA) calculateAndUpdate(allKLines []types.KLine) {
for _, k := range allKLines {
inc.Update(k.Close.Float64())
inc.EmitUpdate(inc.Last())
}
}

func (inc *TEMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
if inc.Interval != interval {
return
}

inc.calculateAndUpdate(window)
}

func (inc *TEMA) Bind(updater KLineWindowUpdater) {
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
}
15 changes: 15 additions & 0 deletions pkg/indicator/tema_callbacks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pkg/indicator/till.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package indicator
1 change: 1 addition & 0 deletions pkg/indicator/tsf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package indicator
29 changes: 29 additions & 0 deletions pkg/indicator/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package indicator

import "github.com/c9s/bbgo/pkg/types"

type KLinePriceMapper func(k types.KLine) float64

func KLineOpenPriceMapper(k types.KLine) float64 {
return k.Open.Float64()
}

func KLineClosePriceMapper(k types.KLine) float64 {
return k.Close.Float64()
}

func KLineTypicalPriceMapper(k types.KLine) float64 {
return (k.High.Float64() + k.Low.Float64() + k.Close.Float64()) / 3.
}

func MapKLinePrice(kLines []types.KLine, f KLinePriceMapper) (prices []float64) {
for _, k := range kLines {
prices = append(prices, f(k))
}

return prices
}

type KLineWindowUpdater interface {
OnKLineWindowUpdate(func(interval types.Interval, window types.KLineWindow))
}
1 change: 1 addition & 0 deletions pkg/indicator/vidya.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package indicator
Loading

0 comments on commit 22d8c2e

Please sign in to comment.