diff --git a/pkg/indicator/macd.go b/pkg/indicator/macd.go index 2535e9681..6947eb731 100644 --- a/pkg/indicator/macd.go +++ b/pkg/indicator/macd.go @@ -75,12 +75,8 @@ func (inc *MACDLegacy) Update(x float64) { inc.EmitUpdate(macd, signal, histogram) } -func (inc *MACDLegacy) Last(int) float64 { - if len(inc.Values) == 0 { - return 0.0 - } - - return inc.Values[len(inc.Values)-1] +func (inc *MACDLegacy) Last(i int) float64 { + return inc.Values.Last(i) } func (inc *MACDLegacy) Length() int { @@ -111,7 +107,7 @@ func (inc *MACDValues) Last(i int) float64 { } func (inc *MACDValues) Index(i int) float64 { - return inc.Values.Last(i) + return inc.Last(i) } func (inc *MACDValues) Length() int { diff --git a/pkg/indicator/v2_macd.go b/pkg/indicator/v2_macd.go new file mode 100644 index 000000000..2e52534ac --- /dev/null +++ b/pkg/indicator/v2_macd.go @@ -0,0 +1,29 @@ +package indicator + +type MACDStream struct { + *SubtractStream + + shortWindow, longWindow, signalWindow int + + fastEWMA, slowEWMA, signal *EWMAStream + histogram *SubtractStream +} + +func MACD2(source Float64Source, shortWindow, longWindow, signalWindow int) *MACDStream { + // bind and calculate these first + fastEWMA := EWMA2(source, shortWindow) + slowEWMA := EWMA2(source, longWindow) + macd := Subtract(fastEWMA, slowEWMA) + signal := EWMA2(macd, signalWindow) + histogram := Subtract(macd, signal) + return &MACDStream{ + SubtractStream: macd, + shortWindow: shortWindow, + longWindow: longWindow, + signalWindow: signalWindow, + fastEWMA: fastEWMA, + slowEWMA: slowEWMA, + signal: signal, + histogram: histogram, + } +} diff --git a/pkg/indicator/v2_macd_test.go b/pkg/indicator/v2_macd_test.go new file mode 100644 index 000000000..74617089a --- /dev/null +++ b/pkg/indicator/v2_macd_test.go @@ -0,0 +1,57 @@ +package indicator + +import ( + "encoding/json" + "math" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +/* +python: + +import pandas as pd +s = pd.Series([0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9]) +slow = s.ewm(span=26, adjust=False).mean() +fast = s.ewm(span=12, adjust=False).mean() +print(fast - slow) +*/ + +func Test_MACD2(t *testing.T) { + var randomPrices = []byte(`[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`) + var input []fixedpoint.Value + err := json.Unmarshal(randomPrices, &input) + assert.NoError(t, err) + + tests := []struct { + name string + kLines []types.KLine + want float64 + }{ + { + name: "random_case", + kLines: buildKLines(input), + want: 0.7967670223776384, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + prices := &PriceStream{} + macd := MACD2(prices, 12, 26, 9) + for _, k := range tt.kLines { + prices.EmitUpdate(k.Close.Float64()) + } + + got := macd.Last(0) + diff := math.Trunc((got-tt.want)*100) / 100 + if diff != 0 { + t.Errorf("MACD2() = %v, want %v", got, tt.want) + } + }) + } +}