From 5853434aecf946ca11e11439ab415e957b50b930 Mon Sep 17 00:00:00 2001 From: c9s Date: Mon, 10 Jul 2023 16:54:22 +0800 Subject: [PATCH] all: move v2 indicator to indicator/v2 --- pkg/bbgo/indicator_set.go | 64 +++++++++---------- pkg/indicator/ewma.go | 4 +- pkg/indicator/ewma_test.go | 8 +-- pkg/indicator/float64updater_callbacks.go | 15 ----- pkg/indicator/ghfilter_test.go | 2 +- pkg/indicator/mapper.go | 33 ---------- pkg/indicator/pivot.go | 12 +--- pkg/indicator/types.go | 22 ------- pkg/indicator/util.go | 2 +- pkg/indicator/v2.go | 19 ------ pkg/indicator/{v2_atr.go => v2/atr.go} | 2 +- .../{v2_atr_test.go => v2/atr_test.go} | 2 +- pkg/indicator/{v2_atrp.go => v2/atrp.go} | 8 ++- pkg/indicator/{v2_boll.go => v2/boll.go} | 20 +++--- pkg/indicator/v2/cma.go | 28 ++++++++ pkg/indicator/{v2_cross.go => v2/cross.go} | 9 +-- pkg/indicator/{v2_ewma.go => v2/ewma.go} | 12 ++-- .../{klinestream.go => v2/klines.go} | 8 ++- .../{ => v2}/klinestream_callbacks.go | 2 +- pkg/indicator/{v2_macd.go => v2/macd.go} | 8 ++- .../{v2_macd_test.go => v2/macd_test.go} | 10 ++- .../{v2_multiply.go => v2/multiply.go} | 19 +++--- pkg/indicator/v2/pivothigh.go | 34 ++++++++++ pkg/indicator/v2/pivotlow.go | 34 ++++++++++ pkg/indicator/{v2_price.go => v2/price.go} | 32 ++++------ pkg/indicator/{v2_rma.go => v2/rma.go} | 23 ++++--- pkg/indicator/{v2_rsi.go => v2/rsi.go} | 28 ++++++-- .../{v2_rsi_test.go => v2/rsi_test.go} | 8 +-- pkg/indicator/{v2_sma.go => v2/sma.go} | 16 +++-- pkg/indicator/{v2_stddev.go => v2/stddev.go} | 8 +-- pkg/indicator/{v2_stoch.go => v2/stoch.go} | 12 ++-- .../{v2_stoch_test.go => v2/stoch_test.go} | 4 +- .../{ => v2}/stochstream_callbacks.go | 2 +- .../{v2_subtract.go => v2/subtract.go} | 15 +++-- .../{v2_test.go => v2/subtract_test.go} | 10 +-- pkg/indicator/{v2_tr.go => v2/tr.go} | 6 +- .../{v2_tr_test.go => v2/tr_test.go} | 2 +- pkg/indicator/v2_cma.go | 23 ------- pkg/indicator/v2_pivothigh.go | 27 -------- pkg/indicator/v2_pivotlow.go | 27 -------- pkg/indicator/volatility.go | 4 +- pkg/indicator/vwap.go | 4 +- pkg/indicator/vwap_test.go | 2 +- pkg/risk/riskcontrol/circuit_break.go | 6 +- pkg/risk/riskcontrol/circuit_break_test.go | 5 +- pkg/strategy/bollmaker/dynamic_spread.go | 7 +- pkg/strategy/bollmaker/strategy.go | 6 +- pkg/strategy/bollmaker/trend.go | 6 +- .../factorzoo/factors/price_mean_reversion.go | 4 +- .../factors/price_volume_divergence.go | 4 +- pkg/strategy/factorzoo/factors/return_rate.go | 2 +- pkg/strategy/fmaker/A18.go | 2 +- pkg/strategy/fmaker/A2.go | 2 +- pkg/strategy/fmaker/A3.go | 2 +- pkg/strategy/fmaker/A34.go | 2 +- pkg/strategy/fmaker/R.go | 2 +- pkg/strategy/fmaker/S0.go | 2 +- pkg/strategy/fmaker/S1.go | 2 +- pkg/strategy/fmaker/S2.go | 2 +- pkg/strategy/fmaker/S3.go | 2 +- pkg/strategy/fmaker/S4.go | 2 +- pkg/strategy/fmaker/S5.go | 2 +- pkg/strategy/fmaker/S6.go | 2 +- pkg/strategy/fmaker/S7.go | 2 +- pkg/strategy/harmonic/shark.go | 2 +- pkg/strategy/irr/neg_return_rate.go | 2 +- pkg/strategy/rsicross/strategy.go | 10 +-- pkg/strategy/scmaker/intensity.go | 14 ++-- pkg/strategy/scmaker/strategy.go | 20 +++--- pkg/{indicator => types}/float64updater.go | 2 +- pkg/types/float64updater_callbacks.go | 15 +++++ pkg/types/kline.go | 38 +++++++++++ .../series_float64.go} | 40 ++++++++---- 73 files changed, 443 insertions(+), 396 deletions(-) delete mode 100644 pkg/indicator/float64updater_callbacks.go delete mode 100644 pkg/indicator/mapper.go delete mode 100644 pkg/indicator/types.go delete mode 100644 pkg/indicator/v2.go rename pkg/indicator/{v2_atr.go => v2/atr.go} (91%) rename pkg/indicator/{v2_atr_test.go => v2/atr_test.go} (99%) rename pkg/indicator/{v2_atrp.go => v2/atrp.go} (72%) rename pkg/indicator/{v2_boll.go => v2/boll.go} (69%) create mode 100644 pkg/indicator/v2/cma.go rename pkg/indicator/{v2_cross.go => v2/cross.go} (83%) rename pkg/indicator/{v2_ewma.go => v2/ewma.go} (59%) rename pkg/indicator/{klinestream.go => v2/klines.go} (90%) rename pkg/indicator/{ => v2}/klinestream_callbacks.go (94%) rename pkg/indicator/{v2_macd.go => v2/macd.go} (80%) rename pkg/indicator/{v2_macd_test.go => v2/macd_test.go} (87%) rename pkg/indicator/{v2_multiply.go => v2/multiply.go} (57%) create mode 100644 pkg/indicator/v2/pivothigh.go create mode 100644 pkg/indicator/v2/pivotlow.go rename pkg/indicator/{v2_price.go => v2/price.go} (59%) rename pkg/indicator/{v2_rma.go => v2/rma.go} (66%) rename pkg/indicator/{v2_rsi.go => v2/rsi.go} (66%) rename pkg/indicator/{v2_rsi_test.go => v2/rsi_test.go} (86%) rename pkg/indicator/{v2_sma.go => v2/sma.go} (57%) rename pkg/indicator/{v2_stddev.go => v2/stddev.go} (71%) rename pkg/indicator/{v2_stoch.go => v2/stoch.go} (88%) rename pkg/indicator/{v2_stoch_test.go => v2/stoch_test.go} (99%) rename pkg/indicator/{ => v2}/stochstream_callbacks.go (93%) rename pkg/indicator/{v2_subtract.go => v2/subtract.go} (71%) rename pkg/indicator/{v2_test.go => v2/subtract_test.go} (72%) rename pkg/indicator/{v2_tr.go => v2/tr.go} (89%) rename pkg/indicator/{v2_tr_test.go => v2/tr_test.go} (99%) delete mode 100644 pkg/indicator/v2_cma.go delete mode 100644 pkg/indicator/v2_pivothigh.go delete mode 100644 pkg/indicator/v2_pivotlow.go rename pkg/{indicator => types}/float64updater.go (86%) create mode 100644 pkg/types/float64updater_callbacks.go rename pkg/{indicator/float64series.go => types/series_float64.go} (73%) diff --git a/pkg/bbgo/indicator_set.go b/pkg/bbgo/indicator_set.go index 8dfa08a48..0d5dbd363 100644 --- a/pkg/bbgo/indicator_set.go +++ b/pkg/bbgo/indicator_set.go @@ -3,7 +3,7 @@ package bbgo import ( "github.com/sirupsen/logrus" - "github.com/c9s/bbgo/pkg/indicator" + "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/types" ) @@ -16,8 +16,8 @@ type IndicatorSet struct { store *MarketDataStore // caches - kLines map[types.Interval]*indicator.KLineStream - closePrices map[types.Interval]*indicator.PriceStream + kLines map[types.Interval]*indicatorv2.KLineStream + closePrices map[types.Interval]*indicatorv2.PriceStream } func NewIndicatorSet(symbol string, stream types.Stream, store *MarketDataStore) *IndicatorSet { @@ -26,17 +26,17 @@ func NewIndicatorSet(symbol string, stream types.Stream, store *MarketDataStore) store: store, stream: stream, - kLines: make(map[types.Interval]*indicator.KLineStream), - closePrices: make(map[types.Interval]*indicator.PriceStream), + kLines: make(map[types.Interval]*indicatorv2.KLineStream), + closePrices: make(map[types.Interval]*indicatorv2.PriceStream), } } -func (i *IndicatorSet) KLines(interval types.Interval) *indicator.KLineStream { +func (i *IndicatorSet) KLines(interval types.Interval) *indicatorv2.KLineStream { if kLines, ok := i.kLines[interval]; ok { return kLines } - kLines := indicator.KLines(i.stream, i.Symbol, interval) + kLines := indicatorv2.KLines(i.stream, i.Symbol, interval) if kLinesWindow, ok := i.store.KLinesOfInterval(interval); ok { kLines.BackFill(*kLinesWindow) } else { @@ -47,60 +47,60 @@ func (i *IndicatorSet) KLines(interval types.Interval) *indicator.KLineStream { return kLines } -func (i *IndicatorSet) OPEN(interval types.Interval) *indicator.PriceStream { - return indicator.OpenPrices(i.KLines(interval)) +func (i *IndicatorSet) OPEN(interval types.Interval) *indicatorv2.PriceStream { + return indicatorv2.OpenPrices(i.KLines(interval)) } -func (i *IndicatorSet) HIGH(interval types.Interval) *indicator.PriceStream { - return indicator.HighPrices(i.KLines(interval)) +func (i *IndicatorSet) HIGH(interval types.Interval) *indicatorv2.PriceStream { + return indicatorv2.HighPrices(i.KLines(interval)) } -func (i *IndicatorSet) LOW(interval types.Interval) *indicator.PriceStream { - return indicator.LowPrices(i.KLines(interval)) +func (i *IndicatorSet) LOW(interval types.Interval) *indicatorv2.PriceStream { + return indicatorv2.LowPrices(i.KLines(interval)) } -func (i *IndicatorSet) CLOSE(interval types.Interval) *indicator.PriceStream { +func (i *IndicatorSet) CLOSE(interval types.Interval) *indicatorv2.PriceStream { if closePrices, ok := i.closePrices[interval]; ok { return closePrices } - closePrices := indicator.ClosePrices(i.KLines(interval)) + closePrices := indicatorv2.ClosePrices(i.KLines(interval)) i.closePrices[interval] = closePrices return closePrices } -func (i *IndicatorSet) VOLUME(interval types.Interval) *indicator.PriceStream { - return indicator.Volumes(i.KLines(interval)) +func (i *IndicatorSet) VOLUME(interval types.Interval) *indicatorv2.PriceStream { + return indicatorv2.Volumes(i.KLines(interval)) } -func (i *IndicatorSet) RSI(iw types.IntervalWindow) *indicator.RSIStream { - return indicator.RSI2(i.CLOSE(iw.Interval), iw.Window) +func (i *IndicatorSet) RSI(iw types.IntervalWindow) *indicatorv2.RSIStream { + return indicatorv2.RSI2(i.CLOSE(iw.Interval), iw.Window) } -func (i *IndicatorSet) EMA(iw types.IntervalWindow) *indicator.EWMAStream { +func (i *IndicatorSet) EMA(iw types.IntervalWindow) *indicatorv2.EWMAStream { return i.EWMA(iw) } -func (i *IndicatorSet) EWMA(iw types.IntervalWindow) *indicator.EWMAStream { - return indicator.EWMA2(i.CLOSE(iw.Interval), iw.Window) +func (i *IndicatorSet) EWMA(iw types.IntervalWindow) *indicatorv2.EWMAStream { + return indicatorv2.EWMA2(i.CLOSE(iw.Interval), iw.Window) } -func (i *IndicatorSet) STOCH(iw types.IntervalWindow, dPeriod int) *indicator.StochStream { - return indicator.Stoch2(i.KLines(iw.Interval), iw.Window, dPeriod) +func (i *IndicatorSet) STOCH(iw types.IntervalWindow, dPeriod int) *indicatorv2.StochStream { + return indicatorv2.Stoch(i.KLines(iw.Interval), iw.Window, dPeriod) } -func (i *IndicatorSet) BOLL(iw types.IntervalWindow, k float64) *indicator.BOLLStream { - return indicator.BOLL2(i.CLOSE(iw.Interval), iw.Window, k) +func (i *IndicatorSet) BOLL(iw types.IntervalWindow, k float64) *indicatorv2.BOLLStream { + return indicatorv2.BOLL(i.CLOSE(iw.Interval), iw.Window, k) } -func (i *IndicatorSet) MACD(interval types.Interval, shortWindow, longWindow, signalWindow int) *indicator.MACDStream { - return indicator.MACD2(i.CLOSE(interval), shortWindow, longWindow, signalWindow) +func (i *IndicatorSet) MACD(interval types.Interval, shortWindow, longWindow, signalWindow int) *indicatorv2.MACDStream { + return indicatorv2.MACD2(i.CLOSE(interval), shortWindow, longWindow, signalWindow) } -func (i *IndicatorSet) ATR(interval types.Interval, window int) *indicator.ATRStream { - return indicator.ATR2(i.KLines(interval), window) +func (i *IndicatorSet) ATR(interval types.Interval, window int) *indicatorv2.ATRStream { + return indicatorv2.ATR2(i.KLines(interval), window) } -func (i *IndicatorSet) ATRP(interval types.Interval, window int) *indicator.ATRPStream { - return indicator.ATRP2(i.KLines(interval), window) +func (i *IndicatorSet) ATRP(interval types.Interval, window int) *indicatorv2.ATRPStream { + return indicatorv2.ATRP2(i.KLines(interval), window) } diff --git a/pkg/indicator/ewma.go b/pkg/indicator/ewma.go index 8d7b13698..7abce2f83 100644 --- a/pkg/indicator/ewma.go +++ b/pkg/indicator/ewma.go @@ -76,9 +76,9 @@ func (inc *EWMA) PushK(k types.KLine) { inc.EmitUpdate(inc.Last(0)) } -func CalculateKLinesEMA(allKLines []types.KLine, priceF KLineValueMapper, window int) float64 { +func CalculateKLinesEMA(allKLines []types.KLine, priceF types.KLineValueMapper, window int) float64 { var multiplier = 2.0 / (float64(window) + 1) - return ewma(MapKLinePrice(allKLines, priceF), multiplier) + return ewma(types.MapKLinePrice(allKLines, priceF), multiplier) } // see https://www.investopedia.com/ask/answers/122314/what-exponential-moving-average-ema-formula-and-how-ema-calculated.asp diff --git a/pkg/indicator/ewma_test.go b/pkg/indicator/ewma_test.go index 23bc81285..3bea83ebf 100644 --- a/pkg/indicator/ewma_test.go +++ b/pkg/indicator/ewma_test.go @@ -1027,7 +1027,7 @@ func buildKLines(prices []fixedpoint.Value) (klines []types.KLine) { func Test_calculateEWMA(t *testing.T) { type args struct { allKLines []types.KLine - priceF KLineValueMapper + priceF types.KLineValueMapper window int } var input []fixedpoint.Value @@ -1043,7 +1043,7 @@ func Test_calculateEWMA(t *testing.T) { name: "ETHUSDT EMA 7", args: args{ allKLines: buildKLines(input), - priceF: KLineClosePriceMapper, + priceF: types.KLineClosePriceMapper, window: 7, }, want: 571.72, // with open price, binance desktop returns 571.45, trading view returns 570.8957, for close price, binance mobile returns 571.72 @@ -1052,7 +1052,7 @@ func Test_calculateEWMA(t *testing.T) { name: "ETHUSDT EMA 25", args: args{ allKLines: buildKLines(input), - priceF: KLineClosePriceMapper, + priceF: types.KLineClosePriceMapper, window: 25, }, want: 571.30, @@ -1061,7 +1061,7 @@ func Test_calculateEWMA(t *testing.T) { name: "ETHUSDT EMA 99", args: args{ allKLines: buildKLines(input), - priceF: KLineClosePriceMapper, + priceF: types.KLineClosePriceMapper, window: 99, }, want: 577.62, // binance mobile uses 577.58 diff --git a/pkg/indicator/float64updater_callbacks.go b/pkg/indicator/float64updater_callbacks.go deleted file mode 100644 index 322660863..000000000 --- a/pkg/indicator/float64updater_callbacks.go +++ /dev/null @@ -1,15 +0,0 @@ -// Code generated by "callbackgen -type Float64Updater"; DO NOT EDIT. - -package indicator - -import () - -func (f *Float64Updater) OnUpdate(cb func(v float64)) { - f.updateCallbacks = append(f.updateCallbacks, cb) -} - -func (f *Float64Updater) EmitUpdate(v float64) { - for _, cb := range f.updateCallbacks { - cb(v) - } -} diff --git a/pkg/indicator/ghfilter_test.go b/pkg/indicator/ghfilter_test.go index 849438859..d93553d96 100644 --- a/pkg/indicator/ghfilter_test.go +++ b/pkg/indicator/ghfilter_test.go @@ -6070,7 +6070,7 @@ func Test_GHFilter(t *testing.T) { func Test_GHFilterEstimationAccurate(t *testing.T) { type args struct { allKLines []types.KLine - priceF KLineValueMapper + priceF types.KLineValueMapper window int } var klines []types.KLine diff --git a/pkg/indicator/mapper.go b/pkg/indicator/mapper.go deleted file mode 100644 index e169bef16..000000000 --- a/pkg/indicator/mapper.go +++ /dev/null @@ -1,33 +0,0 @@ -package indicator - -import "github.com/c9s/bbgo/pkg/types" - -type KLineValueMapper 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 KLinePriceVolumeMapper(k types.KLine) float64 { - return k.Close.Mul(k.Volume).Float64() -} - -func KLineVolumeMapper(k types.KLine) float64 { - return k.Volume.Float64() -} - -func MapKLinePrice(kLines []types.KLine, f KLineValueMapper) (prices []float64) { - for _, k := range kLines { - prices = append(prices, f(k)) - } - - return prices -} diff --git a/pkg/indicator/pivot.go b/pkg/indicator/pivot.go index c32db25cd..617d8347e 100644 --- a/pkg/indicator/pivot.go +++ b/pkg/indicator/pivot.go @@ -52,7 +52,7 @@ func (inc *Pivot) CalculateAndUpdate(klines []types.KLine) { recentT := klines[end-(inc.Window-1) : end+1] - l, h, err := calculatePivot(recentT, inc.Window, KLineLowPriceMapper, KLineHighPriceMapper) + l, h, err := calculatePivot(recentT, inc.Window, types.KLineLowPriceMapper, types.KLineHighPriceMapper) if err != nil { log.WithError(err).Error("can not calculate pivots") return @@ -90,7 +90,7 @@ func (inc *Pivot) Bind(updater KLineWindowUpdater) { updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate) } -func calculatePivot(klines []types.KLine, window int, valLow KLineValueMapper, valHigh KLineValueMapper) (float64, float64, error) { +func calculatePivot(klines []types.KLine, window int, valLow types.KLineValueMapper, valHigh types.KLineValueMapper) (float64, float64, error) { length := len(klines) if length == 0 || length < window { return 0., 0., fmt.Errorf("insufficient elements for calculating with window = %d", window) @@ -115,11 +115,3 @@ func calculatePivot(klines []types.KLine, window int, valLow KLineValueMapper, v return pl, ph, nil } - -func KLineLowPriceMapper(k types.KLine) float64 { - return k.Low.Float64() -} - -func KLineHighPriceMapper(k types.KLine) float64 { - return k.High.Float64() -} diff --git a/pkg/indicator/types.go b/pkg/indicator/types.go deleted file mode 100644 index 7e750b9da..000000000 --- a/pkg/indicator/types.go +++ /dev/null @@ -1,22 +0,0 @@ -package indicator - -import "github.com/c9s/bbgo/pkg/types" - -type Float64Calculator interface { - Calculate(x float64) float64 - PushAndEmit(x float64) -} - -type Float64Source interface { - types.Series - OnUpdate(f func(v float64)) -} - -type Float64Subscription interface { - types.Series - AddSubscriber(f func(v float64)) -} - -type Float64Truncator interface { - Truncate() -} diff --git a/pkg/indicator/util.go b/pkg/indicator/util.go index f0a76d2b0..ce5c209b2 100644 --- a/pkg/indicator/util.go +++ b/pkg/indicator/util.go @@ -7,7 +7,7 @@ func max(x, y int) int { return y } -func min(x, y int) int { +func Min(x, y int) int { if x < y { return x } diff --git a/pkg/indicator/v2.go b/pkg/indicator/v2.go deleted file mode 100644 index 80fd68095..000000000 --- a/pkg/indicator/v2.go +++ /dev/null @@ -1,19 +0,0 @@ -package indicator - -/* -NEW INDICATOR DESIGN: - -klines := kLines(marketDataStream) -closePrices := closePrices(klines) -macd := MACD(klines, {Fast: 12, Slow: 10}) - -equals to: - -klines := KLines(marketDataStream) -closePrices := ClosePrice(klines) -fastEMA := EMA(closePrices, 7) -slowEMA := EMA(closePrices, 25) -macd := Subtract(fastEMA, slowEMA) -signal := EMA(macd, 16) -histogram := Subtract(macd, signal) -*/ diff --git a/pkg/indicator/v2_atr.go b/pkg/indicator/v2/atr.go similarity index 91% rename from pkg/indicator/v2_atr.go rename to pkg/indicator/v2/atr.go index fac3e8e06..da92c5c5b 100644 --- a/pkg/indicator/v2_atr.go +++ b/pkg/indicator/v2/atr.go @@ -1,4 +1,4 @@ -package indicator +package indicatorv2 type ATRStream struct { // embedded struct diff --git a/pkg/indicator/v2_atr_test.go b/pkg/indicator/v2/atr_test.go similarity index 99% rename from pkg/indicator/v2_atr_test.go rename to pkg/indicator/v2/atr_test.go index 23b3cc970..7beb2be4c 100644 --- a/pkg/indicator/v2_atr_test.go +++ b/pkg/indicator/v2/atr_test.go @@ -1,4 +1,4 @@ -package indicator +package indicatorv2 import ( "encoding/json" diff --git a/pkg/indicator/v2_atrp.go b/pkg/indicator/v2/atrp.go similarity index 72% rename from pkg/indicator/v2_atrp.go rename to pkg/indicator/v2/atrp.go index bb9b982d3..e22810ff7 100644 --- a/pkg/indicator/v2_atrp.go +++ b/pkg/indicator/v2/atrp.go @@ -1,12 +1,14 @@ -package indicator +package indicatorv2 + +import "github.com/c9s/bbgo/pkg/types" type ATRPStream struct { - *Float64Series + *types.Float64Series } func ATRP2(source KLineSubscription, window int) *ATRPStream { s := &ATRPStream{ - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), } tr := TR2(source) atr := RMA2(tr, window, true) diff --git a/pkg/indicator/v2_boll.go b/pkg/indicator/v2/boll.go similarity index 69% rename from pkg/indicator/v2_boll.go rename to pkg/indicator/v2/boll.go index ee0dfd15b..eef85c513 100644 --- a/pkg/indicator/v2_boll.go +++ b/pkg/indicator/v2/boll.go @@ -1,10 +1,14 @@ -package indicator +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) type BOLLStream struct { // the band series - *Float64Series + *types.Float64Series - UpBand, DownBand *Float64Series + UpBand, DownBand *types.Float64Series window int k float64 @@ -20,15 +24,15 @@ type BOLLStream struct { // // -> calculate SMA // -> calculate stdDev -> calculate bandWidth -> get latest SMA -> upBand, downBand -func BOLL2(source Float64Source, window int, k float64) *BOLLStream { +func BOLL(source types.Float64Source, window int, k float64) *BOLLStream { // bind these indicators before our main calculator sma := SMA2(source, window) - stdDev := StdDev2(source, window) + stdDev := StdDev(source, window) s := &BOLLStream{ - Float64Series: NewFloat64Series(), - UpBand: NewFloat64Series(), - DownBand: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), + UpBand: types.NewFloat64Series(), + DownBand: types.NewFloat64Series(), window: window, k: k, SMA: sma, diff --git a/pkg/indicator/v2/cma.go b/pkg/indicator/v2/cma.go new file mode 100644 index 000000000..9d0cbea64 --- /dev/null +++ b/pkg/indicator/v2/cma.go @@ -0,0 +1,28 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/indicator" + "github.com/c9s/bbgo/pkg/types" +) + +type CMAStream struct { + *types.Float64Series +} + +func CMA2(source types.Float64Source) *CMAStream { + s := &CMAStream{ + Float64Series: types.NewFloat64Series(), + } + s.Bind(source, s) + return s +} + +func (s *CMAStream) Calculate(x float64) float64 { + l := float64(s.Slice.Length()) + cma := (s.Slice.Last(0)*l + x) / (l + 1.) + return cma +} + +func (s *CMAStream) Truncate() { + s.Slice.Truncate(indicator.MaxNumOfEWMA) +} diff --git a/pkg/indicator/v2_cross.go b/pkg/indicator/v2/cross.go similarity index 83% rename from pkg/indicator/v2_cross.go rename to pkg/indicator/v2/cross.go index 835e87c30..8f1b39767 100644 --- a/pkg/indicator/v2_cross.go +++ b/pkg/indicator/v2/cross.go @@ -1,7 +1,8 @@ -package indicator +package indicatorv2 import ( "github.com/c9s/bbgo/pkg/datatype/floats" + "github.com/c9s/bbgo/pkg/types" ) type CrossType float64 @@ -13,7 +14,7 @@ const ( // CrossStream subscribes 2 upstreams, and calculate the cross signal type CrossStream struct { - *Float64Series + *types.Float64Series a, b floats.Slice } @@ -21,9 +22,9 @@ type CrossStream struct { // Cross creates the CrossStream object: // // cross := Cross(fastEWMA, slowEWMA) -func Cross(a, b Float64Source) *CrossStream { +func Cross(a, b types.Float64Source) *CrossStream { s := &CrossStream{ - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), } a.OnUpdate(func(v float64) { s.a.Push(v) diff --git a/pkg/indicator/v2_ewma.go b/pkg/indicator/v2/ewma.go similarity index 59% rename from pkg/indicator/v2_ewma.go rename to pkg/indicator/v2/ewma.go index 16feb9901..fdd9745f0 100644 --- a/pkg/indicator/v2_ewma.go +++ b/pkg/indicator/v2/ewma.go @@ -1,15 +1,17 @@ -package indicator +package indicatorv2 + +import "github.com/c9s/bbgo/pkg/types" type EWMAStream struct { - *Float64Series + *types.Float64Series window int multiplier float64 } -func EWMA2(source Float64Source, window int) *EWMAStream { +func EWMA2(source types.Float64Source, window int) *EWMAStream { s := &EWMAStream{ - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), window: window, multiplier: 2.0 / float64(1+window), } @@ -18,7 +20,7 @@ func EWMA2(source Float64Source, window int) *EWMAStream { } func (s *EWMAStream) Calculate(v float64) float64 { - last := s.slice.Last(0) + last := s.Slice.Last(0) if last == 0.0 { return v } diff --git a/pkg/indicator/klinestream.go b/pkg/indicator/v2/klines.go similarity index 90% rename from pkg/indicator/klinestream.go rename to pkg/indicator/v2/klines.go index 11215b3e7..b61d1199d 100644 --- a/pkg/indicator/klinestream.go +++ b/pkg/indicator/v2/klines.go @@ -1,4 +1,4 @@ -package indicator +package indicatorv2 import "github.com/c9s/bbgo/pkg/types" @@ -60,3 +60,9 @@ func KLines(source types.Stream, symbol string, interval types.Interval) *KLineS return s } + +type KLineSubscription interface { + AddSubscriber(f func(k types.KLine)) + Length() int + Last(i int) *types.KLine +} diff --git a/pkg/indicator/klinestream_callbacks.go b/pkg/indicator/v2/klinestream_callbacks.go similarity index 94% rename from pkg/indicator/klinestream_callbacks.go rename to pkg/indicator/v2/klinestream_callbacks.go index eb6e59dbb..6cf2e46ce 100644 --- a/pkg/indicator/klinestream_callbacks.go +++ b/pkg/indicator/v2/klinestream_callbacks.go @@ -1,6 +1,6 @@ // Code generated by "callbackgen -type KLineStream"; DO NOT EDIT. -package indicator +package indicatorv2 import ( "github.com/c9s/bbgo/pkg/types" diff --git a/pkg/indicator/v2_macd.go b/pkg/indicator/v2/macd.go similarity index 80% rename from pkg/indicator/v2_macd.go rename to pkg/indicator/v2/macd.go index de62cd496..1679cf6c0 100644 --- a/pkg/indicator/v2_macd.go +++ b/pkg/indicator/v2/macd.go @@ -1,4 +1,8 @@ -package indicator +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) type MACDStream struct { *SubtractStream @@ -9,7 +13,7 @@ type MACDStream struct { Histogram *SubtractStream } -func MACD2(source Float64Source, shortWindow, longWindow, signalWindow int) *MACDStream { +func MACD2(source types.Float64Source, shortWindow, longWindow, signalWindow int) *MACDStream { // bind and calculate these first fastEWMA := EWMA2(source, shortWindow) slowEWMA := EWMA2(source, longWindow) diff --git a/pkg/indicator/v2_macd_test.go b/pkg/indicator/v2/macd_test.go similarity index 87% rename from pkg/indicator/v2_macd_test.go rename to pkg/indicator/v2/macd_test.go index 6f86d0ffa..3b6b72405 100644 --- a/pkg/indicator/v2_macd_test.go +++ b/pkg/indicator/v2/macd_test.go @@ -1,4 +1,4 @@ -package indicator +package indicatorv2 import ( "encoding/json" @@ -21,6 +21,14 @@ fast = s.ewm(span=12, adjust=False).mean() print(fast - slow) */ +func buildKLines(prices []fixedpoint.Value) (klines []types.KLine) { + for _, p := range prices { + klines = append(klines, types.KLine{Close: p}) + } + + return klines +} + 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 diff --git a/pkg/indicator/v2_multiply.go b/pkg/indicator/v2/multiply.go similarity index 57% rename from pkg/indicator/v2_multiply.go rename to pkg/indicator/v2/multiply.go index ee76bdc00..89543685b 100644 --- a/pkg/indicator/v2_multiply.go +++ b/pkg/indicator/v2/multiply.go @@ -1,15 +1,18 @@ -package indicator +package indicatorv2 -import "github.com/c9s/bbgo/pkg/datatype/floats" +import ( + "github.com/c9s/bbgo/pkg/datatype/floats" + "github.com/c9s/bbgo/pkg/types" +) type MultiplyStream struct { - *Float64Series + *types.Float64Series a, b floats.Slice } -func Multiply(a, b Float64Source) *MultiplyStream { +func Multiply(a, b types.Float64Source) *MultiplyStream { s := &MultiplyStream{ - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), } a.OnUpdate(func(v float64) { @@ -29,13 +32,13 @@ func (s *MultiplyStream) calculate() { return } - if s.a.Length() > s.slice.Length() { - var numNewElems = s.a.Length() - s.slice.Length() + if s.a.Length() > s.Slice.Length() { + var numNewElems = s.a.Length() - s.Slice.Length() var tailA = s.a.Tail(numNewElems) var tailB = s.b.Tail(numNewElems) var tailC = tailA.Mul(tailB) for _, f := range tailC { - s.slice.Push(f) + s.Slice.Push(f) s.EmitUpdate(f) } } diff --git a/pkg/indicator/v2/pivothigh.go b/pkg/indicator/v2/pivothigh.go new file mode 100644 index 000000000..74267ab88 --- /dev/null +++ b/pkg/indicator/v2/pivothigh.go @@ -0,0 +1,34 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/datatype/floats" + "github.com/c9s/bbgo/pkg/types" +) + +type PivotHighStream struct { + *types.Float64Series + rawValues floats.Slice + window, rightWindow int +} + +func PivotHigh2(source types.Float64Source, window, rightWindow int) *PivotHighStream { + s := &PivotHighStream{ + Float64Series: types.NewFloat64Series(), + window: window, + rightWindow: rightWindow, + } + + s.Subscribe(source, func(x float64) { + s.rawValues.Push(x) + if low, ok := s.calculatePivotHigh(s.rawValues, s.window, s.rightWindow); ok { + s.PushAndEmit(low) + } + }) + return s +} + +func (s *PivotHighStream) calculatePivotHigh(highs floats.Slice, left, right int) (float64, bool) { + return floats.FindPivot(highs, left, right, func(a, pivot float64) bool { + return a < pivot + }) +} diff --git a/pkg/indicator/v2/pivotlow.go b/pkg/indicator/v2/pivotlow.go new file mode 100644 index 000000000..cdd7aadde --- /dev/null +++ b/pkg/indicator/v2/pivotlow.go @@ -0,0 +1,34 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/datatype/floats" + "github.com/c9s/bbgo/pkg/types" +) + +type PivotLowStream struct { + *types.Float64Series + rawValues floats.Slice + window, rightWindow int +} + +func PivotLow(source types.Float64Source, window, rightWindow int) *PivotLowStream { + s := &PivotLowStream{ + Float64Series: types.NewFloat64Series(), + window: window, + rightWindow: rightWindow, + } + + s.Subscribe(source, func(x float64) { + s.rawValues.Push(x) + if low, ok := s.calculatePivotLow(s.rawValues, s.window, s.rightWindow); ok { + s.PushAndEmit(low) + } + }) + return s +} + +func (s *PivotLowStream) calculatePivotLow(lows floats.Slice, left, right int) (float64, bool) { + return floats.FindPivot(lows, left, right, func(a, pivot float64) bool { + return a > pivot + }) +} diff --git a/pkg/indicator/v2_price.go b/pkg/indicator/v2/price.go similarity index 59% rename from pkg/indicator/v2_price.go rename to pkg/indicator/v2/price.go index d4c546e57..7f261a14e 100644 --- a/pkg/indicator/v2_price.go +++ b/pkg/indicator/v2/price.go @@ -1,24 +1,18 @@ -package indicator +package indicatorv2 import ( "github.com/c9s/bbgo/pkg/types" ) -type KLineSubscription interface { - AddSubscriber(f func(k types.KLine)) - Length() int - Last(i int) *types.KLine -} - type PriceStream struct { - *Float64Series + *types.Float64Series - mapper KLineValueMapper + mapper types.KLineValueMapper } -func Price(source KLineSubscription, mapper KLineValueMapper) *PriceStream { +func Price(source KLineSubscription, mapper types.KLineValueMapper) *PriceStream { s := &PriceStream{ - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), mapper: mapper, } @@ -37,37 +31,37 @@ func Price(source KLineSubscription, mapper KLineValueMapper) *PriceStream { func (s *PriceStream) AddSubscriber(f func(v float64)) { s.OnUpdate(f) - if len(s.slice) == 0 { + if len(s.Slice) == 0 { return } // push historical value to the subscriber - for _, v := range s.slice { + for _, v := range s.Slice { f(v) } } func (s *PriceStream) PushAndEmit(v float64) { - s.slice.Push(v) + s.Slice.Push(v) s.EmitUpdate(v) } func ClosePrices(source KLineSubscription) *PriceStream { - return Price(source, KLineClosePriceMapper) + return Price(source, types.KLineClosePriceMapper) } func LowPrices(source KLineSubscription) *PriceStream { - return Price(source, KLineLowPriceMapper) + return Price(source, types.KLineLowPriceMapper) } func HighPrices(source KLineSubscription) *PriceStream { - return Price(source, KLineHighPriceMapper) + return Price(source, types.KLineHighPriceMapper) } func OpenPrices(source KLineSubscription) *PriceStream { - return Price(source, KLineOpenPriceMapper) + return Price(source, types.KLineOpenPriceMapper) } func Volumes(source KLineSubscription) *PriceStream { - return Price(source, KLineVolumeMapper) + return Price(source, types.KLineVolumeMapper) } diff --git a/pkg/indicator/v2_rma.go b/pkg/indicator/v2/rma.go similarity index 66% rename from pkg/indicator/v2_rma.go rename to pkg/indicator/v2/rma.go index 0464ad96d..247b469f9 100644 --- a/pkg/indicator/v2_rma.go +++ b/pkg/indicator/v2/rma.go @@ -1,8 +1,15 @@ -package indicator +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +const MaxNumOfRMA = 1000 +const MaxNumOfRMATruncateSize = 500 type RMAStream struct { // embedded structs - *Float64Series + *types.Float64Series // config fields Adjust bool @@ -12,9 +19,9 @@ type RMAStream struct { sum, previous float64 } -func RMA2(source Float64Source, window int, adjust bool) *RMAStream { +func RMA2(source types.Float64Source, window int, adjust bool) *RMAStream { s := &RMAStream{ - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), window: window, Adjust: adjust, } @@ -41,17 +48,17 @@ func (s *RMAStream) Calculate(x float64) float64 { if s.counter < s.window { // we can use x, but we need to use 0. to make the same behavior as the result from python pandas_ta - s.slice.Push(0) + s.Slice.Push(0) } - s.slice.Push(tmp) + s.Slice.Push(tmp) s.previous = tmp return tmp } func (s *RMAStream) Truncate() { - if len(s.slice) > MaxNumOfRMA { - s.slice = s.slice[MaxNumOfRMATruncateSize-1:] + if len(s.Slice) > MaxNumOfRMA { + s.Slice = s.Slice[MaxNumOfRMATruncateSize-1:] } } diff --git a/pkg/indicator/v2_rsi.go b/pkg/indicator/v2/rsi.go similarity index 66% rename from pkg/indicator/v2_rsi.go rename to pkg/indicator/v2/rsi.go index 11cf984c9..d4ca1489d 100644 --- a/pkg/indicator/v2_rsi.go +++ b/pkg/indicator/v2/rsi.go @@ -1,20 +1,24 @@ -package indicator +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) type RSIStream struct { // embedded structs - *Float64Series + *types.Float64Series // config fields window int // private states - source Float64Source + source types.Float64Source } -func RSI2(source Float64Source, window int) *RSIStream { +func RSI2(source types.Float64Source, window int) *RSIStream { s := &RSIStream{ source: source, - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), window: window, } s.Bind(source, s) @@ -42,3 +46,17 @@ func (s *RSIStream) Calculate(_ float64) float64 { rsi := 100.0 - (100.0 / (1.0 + rs)) return rsi } + +func max(x, y int) int { + if x > y { + return x + } + return y +} + +func min(x, y int) int { + if x < y { + return x + } + return y +} diff --git a/pkg/indicator/v2_rsi_test.go b/pkg/indicator/v2/rsi_test.go similarity index 86% rename from pkg/indicator/v2_rsi_test.go rename to pkg/indicator/v2/rsi_test.go index 311624024..779132d34 100644 --- a/pkg/indicator/v2_rsi_test.go +++ b/pkg/indicator/v2/rsi_test.go @@ -1,4 +1,4 @@ -package indicator +package indicatorv2 import ( "encoding/json" @@ -75,11 +75,11 @@ func Test_RSI2(t *testing.T) { prices.PushAndEmit(price) } - assert.Equal(t, floats.Slice(tt.values), prices.slice) + assert.Equal(t, floats.Slice(tt.values), prices.Slice) - if assert.Equal(t, len(tt.want), len(rsi.slice)) { + if assert.Equal(t, len(tt.want), len(rsi.Slice)) { for i, v := range tt.want { - assert.InDelta(t, v, rsi.slice[i], 0.000001, "Expected rsi.slice[%d] to be %v, but got %v", i, v, rsi.slice[i]) + assert.InDelta(t, v, rsi.Slice[i], 0.000001, "Expected rsi.slice[%d] to be %v, but got %v", i, v, rsi.Slice[i]) } } }) diff --git a/pkg/indicator/v2_sma.go b/pkg/indicator/v2/sma.go similarity index 57% rename from pkg/indicator/v2_sma.go rename to pkg/indicator/v2/sma.go index b6a79277c..dd054c58c 100644 --- a/pkg/indicator/v2_sma.go +++ b/pkg/indicator/v2/sma.go @@ -1,16 +1,20 @@ -package indicator +package indicatorv2 -import "github.com/c9s/bbgo/pkg/types" +import ( + "github.com/c9s/bbgo/pkg/types" +) + +const MaxNumOfSMA = 5_000 type SMAStream struct { - *Float64Series + *types.Float64Series window int rawValues *types.Queue } -func SMA2(source Float64Source, window int) *SMAStream { +func SMA2(source types.Float64Source, window int) *SMAStream { s := &SMAStream{ - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), window: window, rawValues: types.NewQueue(window), } @@ -25,5 +29,5 @@ func (s *SMAStream) Calculate(v float64) float64 { } func (s *SMAStream) Truncate() { - s.slice = s.slice.Truncate(MaxNumOfSMA) + s.Slice = s.Slice.Truncate(MaxNumOfSMA) } diff --git a/pkg/indicator/v2_stddev.go b/pkg/indicator/v2/stddev.go similarity index 71% rename from pkg/indicator/v2_stddev.go rename to pkg/indicator/v2/stddev.go index 9e465f970..a9e94974d 100644 --- a/pkg/indicator/v2_stddev.go +++ b/pkg/indicator/v2/stddev.go @@ -1,9 +1,9 @@ -package indicator +package indicatorv2 import "github.com/c9s/bbgo/pkg/types" type StdDevStream struct { - *Float64Series + *types.Float64Series rawValues *types.Queue @@ -11,9 +11,9 @@ type StdDevStream struct { multiplier float64 } -func StdDev2(source Float64Source, window int) *StdDevStream { +func StdDev(source types.Float64Source, window int) *StdDevStream { s := &StdDevStream{ - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), rawValues: types.NewQueue(window), window: window, } diff --git a/pkg/indicator/v2_stoch.go b/pkg/indicator/v2/stoch.go similarity index 88% rename from pkg/indicator/v2_stoch.go rename to pkg/indicator/v2/stoch.go index 462e4b851..808169e43 100644 --- a/pkg/indicator/v2_stoch.go +++ b/pkg/indicator/v2/stoch.go @@ -1,10 +1,12 @@ -package indicator +package indicatorv2 import ( "github.com/c9s/bbgo/pkg/datatype/floats" "github.com/c9s/bbgo/pkg/types" ) +const DPeriod int = 3 + // Stochastic Oscillator // - https://www.investopedia.com/terms/s/stochasticoscillator.asp // @@ -30,7 +32,7 @@ type StochStream struct { } // Stochastic Oscillator -func Stoch2(source KLineSubscription, window, dPeriod int) *StochStream { +func Stoch(source KLineSubscription, window, dPeriod int) *StochStream { highPrices := HighPrices(source) lowPrices := LowPrices(source) @@ -42,8 +44,8 @@ func Stoch2(source KLineSubscription, window, dPeriod int) *StochStream { } source.AddSubscriber(func(kLine types.KLine) { - lowest := s.lowPrices.slice.Tail(s.window).Min() - highest := s.highPrices.slice.Tail(s.window).Max() + lowest := s.lowPrices.Slice.Tail(s.window).Min() + highest := s.highPrices.Slice.Tail(s.window).Max() var k float64 = 50.0 var d float64 = 0.0 @@ -53,7 +55,7 @@ func Stoch2(source KLineSubscription, window, dPeriod int) *StochStream { } s.K.Push(k) - + d = s.K.Tail(s.dPeriod).Mean() s.D.Push(d) s.EmitUpdate(k, d) diff --git a/pkg/indicator/v2_stoch_test.go b/pkg/indicator/v2/stoch_test.go similarity index 99% rename from pkg/indicator/v2_stoch_test.go rename to pkg/indicator/v2/stoch_test.go index fa5f05b09..73a3becca 100644 --- a/pkg/indicator/v2_stoch_test.go +++ b/pkg/indicator/v2/stoch_test.go @@ -1,4 +1,4 @@ -package indicator +package indicatorv2 import ( "encoding/json" @@ -58,7 +58,7 @@ func TestSTOCH2_update(t *testing.T) { t.Run(tt.name, func(t *testing.T) { stream := &types.StandardStream{} kLines := KLines(stream, "", "") - kd := Stoch2(kLines, tt.window, DPeriod) + kd := Stoch(kLines, tt.window, DPeriod) for _, k := range tt.kLines { stream.EmitKLineClosed(k) diff --git a/pkg/indicator/stochstream_callbacks.go b/pkg/indicator/v2/stochstream_callbacks.go similarity index 93% rename from pkg/indicator/stochstream_callbacks.go rename to pkg/indicator/v2/stochstream_callbacks.go index 539b13992..b4bc8bb80 100644 --- a/pkg/indicator/stochstream_callbacks.go +++ b/pkg/indicator/v2/stochstream_callbacks.go @@ -1,6 +1,6 @@ // Code generated by "callbackgen -type StochStream"; DO NOT EDIT. -package indicator +package indicatorv2 import () diff --git a/pkg/indicator/v2_subtract.go b/pkg/indicator/v2/subtract.go similarity index 71% rename from pkg/indicator/v2_subtract.go rename to pkg/indicator/v2/subtract.go index 33a191730..622570e2d 100644 --- a/pkg/indicator/v2_subtract.go +++ b/pkg/indicator/v2/subtract.go @@ -1,12 +1,13 @@ -package indicator +package indicatorv2 import ( "github.com/c9s/bbgo/pkg/datatype/floats" + "github.com/c9s/bbgo/pkg/types" ) // SubtractStream subscribes 2 upstream data, and then subtract these 2 values type SubtractStream struct { - *Float64Series + *types.Float64Series a, b floats.Slice i int @@ -14,9 +15,9 @@ type SubtractStream struct { // Subtract creates the SubtractStream object // subtract := Subtract(longEWMA, shortEWMA) -func Subtract(a, b Float64Source) *SubtractStream { +func Subtract(a, b types.Float64Source) *SubtractStream { s := &SubtractStream{ - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), } a.OnUpdate(func(v float64) { @@ -35,13 +36,13 @@ func (s *SubtractStream) calculate() { return } - if s.a.Length() > s.slice.Length() { - var numNewElems = s.a.Length() - s.slice.Length() + if s.a.Length() > s.Slice.Length() { + var numNewElems = s.a.Length() - s.Slice.Length() var tailA = s.a.Tail(numNewElems) var tailB = s.b.Tail(numNewElems) var tailC = tailA.Sub(tailB) for _, f := range tailC { - s.slice.Push(f) + s.Slice.Push(f) s.EmitUpdate(f) } } diff --git a/pkg/indicator/v2_test.go b/pkg/indicator/v2/subtract_test.go similarity index 72% rename from pkg/indicator/v2_test.go rename to pkg/indicator/v2/subtract_test.go index 717713d11..1ef3aace3 100644 --- a/pkg/indicator/v2_test.go +++ b/pkg/indicator/v2/subtract_test.go @@ -1,4 +1,4 @@ -package indicator +package indicatorv2 import ( "testing" @@ -21,10 +21,10 @@ func Test_v2_Subtract(t *testing.T) { stream.EmitKLineClosed(types.KLine{Close: fixedpoint.NewFromFloat(19_000.0 + i)}) } - t.Logf("fastEMA: %+v", fastEMA.slice) - t.Logf("slowEMA: %+v", slowEMA.slice) + t.Logf("fastEMA: %+v", fastEMA.Slice) + t.Logf("slowEMA: %+v", slowEMA.Slice) assert.Equal(t, len(subtract.a), len(subtract.b)) - assert.Equal(t, len(subtract.a), len(subtract.slice)) - assert.InDelta(t, subtract.slice[0], subtract.a[0]-subtract.b[0], 0.0001) + assert.Equal(t, len(subtract.a), len(subtract.Slice)) + assert.InDelta(t, subtract.Slice[0], subtract.a[0]-subtract.b[0], 0.0001) } diff --git a/pkg/indicator/v2_tr.go b/pkg/indicator/v2/tr.go similarity index 89% rename from pkg/indicator/v2_tr.go rename to pkg/indicator/v2/tr.go index 98f4bdc7b..e33044cf2 100644 --- a/pkg/indicator/v2_tr.go +++ b/pkg/indicator/v2/tr.go @@ -1,4 +1,4 @@ -package indicator +package indicatorv2 import ( "math" @@ -9,7 +9,7 @@ import ( // This TRStream calculates the ATR first type TRStream struct { // embedded struct - *Float64Series + *types.Float64Series // private states previousClose float64 @@ -17,7 +17,7 @@ type TRStream struct { func TR2(source KLineSubscription) *TRStream { s := &TRStream{ - Float64Series: NewFloat64Series(), + Float64Series: types.NewFloat64Series(), } source.AddSubscriber(func(k types.KLine) { diff --git a/pkg/indicator/v2_tr_test.go b/pkg/indicator/v2/tr_test.go similarity index 99% rename from pkg/indicator/v2_tr_test.go rename to pkg/indicator/v2/tr_test.go index 1067529c9..82afd8269 100644 --- a/pkg/indicator/v2_tr_test.go +++ b/pkg/indicator/v2/tr_test.go @@ -1,4 +1,4 @@ -package indicator +package indicatorv2 import ( "encoding/json" diff --git a/pkg/indicator/v2_cma.go b/pkg/indicator/v2_cma.go deleted file mode 100644 index 9bc2c8994..000000000 --- a/pkg/indicator/v2_cma.go +++ /dev/null @@ -1,23 +0,0 @@ -package indicator - -type CMAStream struct { - *Float64Series -} - -func CMA2(source Float64Source) *CMAStream { - s := &CMAStream{ - Float64Series: NewFloat64Series(), - } - s.Bind(source, s) - return s -} - -func (s *CMAStream) Calculate(x float64) float64 { - l := float64(s.slice.Length()) - cma := (s.slice.Last(0)*l + x) / (l + 1.) - return cma -} - -func (s *CMAStream) Truncate() { - s.slice.Truncate(MaxNumOfEWMA) -} diff --git a/pkg/indicator/v2_pivothigh.go b/pkg/indicator/v2_pivothigh.go deleted file mode 100644 index 9e4143816..000000000 --- a/pkg/indicator/v2_pivothigh.go +++ /dev/null @@ -1,27 +0,0 @@ -package indicator - -import ( - "github.com/c9s/bbgo/pkg/datatype/floats" -) - -type PivotHighStream struct { - *Float64Series - rawValues floats.Slice - window, rightWindow int -} - -func PivotHigh2(source Float64Source, window, rightWindow int) *PivotHighStream { - s := &PivotHighStream{ - Float64Series: NewFloat64Series(), - window: window, - rightWindow: rightWindow, - } - - s.Subscribe(source, func(x float64) { - s.rawValues.Push(x) - if low, ok := calculatePivotHigh(s.rawValues, s.window, s.rightWindow); ok { - s.PushAndEmit(low) - } - }) - return s -} diff --git a/pkg/indicator/v2_pivotlow.go b/pkg/indicator/v2_pivotlow.go deleted file mode 100644 index 1fa78e054..000000000 --- a/pkg/indicator/v2_pivotlow.go +++ /dev/null @@ -1,27 +0,0 @@ -package indicator - -import ( - "github.com/c9s/bbgo/pkg/datatype/floats" -) - -type PivotLowStream struct { - *Float64Series - rawValues floats.Slice - window, rightWindow int -} - -func PivotLow2(source Float64Source, window, rightWindow int) *PivotLowStream { - s := &PivotLowStream{ - Float64Series: NewFloat64Series(), - window: window, - rightWindow: rightWindow, - } - - s.Subscribe(source, func(x float64) { - s.rawValues.Push(x) - if low, ok := calculatePivotLow(s.rawValues, s.window, s.rightWindow); ok { - s.PushAndEmit(low) - } - }) - return s -} diff --git a/pkg/indicator/volatility.go b/pkg/indicator/volatility.go index 3c5efce96..aeb9979be 100644 --- a/pkg/indicator/volatility.go +++ b/pkg/indicator/volatility.go @@ -58,7 +58,7 @@ func (inc *Volatility) CalculateAndUpdate(allKLines []types.KLine) { var recentT = allKLines[end-(inc.Window-1) : end+1] - volatility, err := calculateVOLATILITY(recentT, inc.Window, KLineClosePriceMapper) + volatility, err := calculateVOLATILITY(recentT, inc.Window, types.KLineClosePriceMapper) if err != nil { log.WithError(err).Error("can not calculate volatility") return @@ -86,7 +86,7 @@ func (inc *Volatility) Bind(updater KLineWindowUpdater) { updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate) } -func calculateVOLATILITY(klines []types.KLine, window int, priceF KLineValueMapper) (float64, error) { +func calculateVOLATILITY(klines []types.KLine, window int, priceF types.KLineValueMapper) (float64, error) { length := len(klines) if length == 0 || length < window { return 0.0, fmt.Errorf("insufficient elements for calculating VOL with window = %d", window) diff --git a/pkg/indicator/vwap.go b/pkg/indicator/vwap.go index 3f2e08085..2dbe5228c 100644 --- a/pkg/indicator/vwap.go +++ b/pkg/indicator/vwap.go @@ -71,7 +71,7 @@ func (inc *VWAP) Length() int { var _ types.SeriesExtend = &VWAP{} func (inc *VWAP) PushK(k types.KLine) { - inc.Update(KLineTypicalPriceMapper(k), k.Volume.Float64()) + inc.Update(types.KLineTypicalPriceMapper(k), k.Volume.Float64()) } func (inc *VWAP) CalculateAndUpdate(allKLines []types.KLine) { @@ -99,7 +99,7 @@ func (inc *VWAP) Bind(updater KLineWindowUpdater) { updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate) } -func calculateVWAP(klines []types.KLine, priceF KLineValueMapper, window int) float64 { +func calculateVWAP(klines []types.KLine, priceF types.KLineValueMapper, window int) float64 { vwap := VWAP{IntervalWindow: types.IntervalWindow{Window: window}} for _, k := range klines { vwap.Update(priceF(k), k.Volume.Float64()) diff --git a/pkg/indicator/vwap_test.go b/pkg/indicator/vwap_test.go index 7929b4bba..3aab3db34 100644 --- a/pkg/indicator/vwap_test.go +++ b/pkg/indicator/vwap_test.go @@ -63,7 +63,7 @@ func Test_calculateVWAP(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - priceF := KLineTypicalPriceMapper + priceF := types.KLineTypicalPriceMapper got := calculateVWAP(tt.kLines, priceF, tt.window) diff := math.Trunc((got-tt.want)*100) / 100 if diff != 0 { diff --git a/pkg/risk/riskcontrol/circuit_break.go b/pkg/risk/riskcontrol/circuit_break.go index a89833ed8..753d8d236 100644 --- a/pkg/risk/riskcontrol/circuit_break.go +++ b/pkg/risk/riskcontrol/circuit_break.go @@ -4,14 +4,14 @@ import ( log "github.com/sirupsen/logrus" "github.com/c9s/bbgo/pkg/fixedpoint" - "github.com/c9s/bbgo/pkg/indicator" + "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/types" ) type CircuitBreakRiskControl struct { // Since price could be fluctuated large, // use an EWMA to smooth it in running time - price *indicator.EWMAStream + price *indicatorv2.EWMAStream position *types.Position profitStats *types.ProfitStats lossThreshold fixedpoint.Value @@ -19,7 +19,7 @@ type CircuitBreakRiskControl struct { func NewCircuitBreakRiskControl( position *types.Position, - price *indicator.EWMAStream, + price *indicatorv2.EWMAStream, lossThreshold fixedpoint.Value, profitStats *types.ProfitStats) *CircuitBreakRiskControl { diff --git a/pkg/risk/riskcontrol/circuit_break_test.go b/pkg/risk/riskcontrol/circuit_break_test.go index 1ee01c59b..0e5d00dda 100644 --- a/pkg/risk/riskcontrol/circuit_break_test.go +++ b/pkg/risk/riskcontrol/circuit_break_test.go @@ -6,12 +6,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/c9s/bbgo/pkg/fixedpoint" - "github.com/c9s/bbgo/pkg/indicator" + indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/types" ) func Test_IsHalted(t *testing.T) { - var ( price = 30000.00 realizedPnL = fixedpoint.NewFromFloat(-100.0) @@ -19,7 +18,7 @@ func Test_IsHalted(t *testing.T) { ) window := types.IntervalWindow{Window: 30, Interval: types.Interval1m} - priceEWMA := indicator.EWMA2(nil, window.Window) + priceEWMA := indicatorv2.EWMA2(nil, window.Window) priceEWMA.PushAndEmit(price) cases := []struct { diff --git a/pkg/strategy/bollmaker/dynamic_spread.go b/pkg/strategy/bollmaker/dynamic_spread.go index b6ede2156..f236c34ea 100644 --- a/pkg/strategy/bollmaker/dynamic_spread.go +++ b/pkg/strategy/bollmaker/dynamic_spread.go @@ -7,6 +7,7 @@ import ( "github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/indicator" + indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/types" ) @@ -28,7 +29,7 @@ type DynamicSpreadSettings struct { } // Initialize dynamic spreads and preload SMAs -func (ds *DynamicSpreadSettings) Initialize(symbol string, session *bbgo.ExchangeSession, neutralBoll, defaultBoll *indicator.BOLLStream) { +func (ds *DynamicSpreadSettings) Initialize(symbol string, session *bbgo.ExchangeSession, neutralBoll, defaultBoll *indicatorv2.BOLLStream) { switch { case ds.AmpSpreadSettings != nil: ds.AmpSpreadSettings.initialize(symbol, session) @@ -164,10 +165,10 @@ type DynamicSpreadBollWidthRatioSettings struct { // A positive number. The greater factor, the sharper weighting function. Default set to 1.0 . Sensitivity float64 `json:"sensitivity"` - defaultBoll, neutralBoll *indicator.BOLLStream + defaultBoll, neutralBoll *indicatorv2.BOLLStream } -func (ds *DynamicSpreadBollWidthRatioSettings) initialize(neutralBoll, defaultBoll *indicator.BOLLStream) { +func (ds *DynamicSpreadBollWidthRatioSettings) initialize(neutralBoll, defaultBoll *indicatorv2.BOLLStream) { ds.neutralBoll = neutralBoll ds.defaultBoll = defaultBoll if ds.Sensitivity <= 0. { diff --git a/pkg/strategy/bollmaker/strategy.go b/pkg/strategy/bollmaker/strategy.go index a2233b44d..d8f32b8c2 100644 --- a/pkg/strategy/bollmaker/strategy.go +++ b/pkg/strategy/bollmaker/strategy.go @@ -6,7 +6,7 @@ import ( "math" "sync" - "github.com/c9s/bbgo/pkg/indicator" + indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/util" "github.com/pkg/errors" @@ -158,10 +158,10 @@ type Strategy struct { groupID uint32 // defaultBoll is the BOLLINGER indicator we used for predicting the price. - defaultBoll *indicator.BOLLStream + defaultBoll *indicatorv2.BOLLStream // neutralBoll is the neutral price section - neutralBoll *indicator.BOLLStream + neutralBoll *indicatorv2.BOLLStream // StrategyController bbgo.StrategyController diff --git a/pkg/strategy/bollmaker/trend.go b/pkg/strategy/bollmaker/trend.go index 7e4ddcf71..0ff0cc9d3 100644 --- a/pkg/strategy/bollmaker/trend.go +++ b/pkg/strategy/bollmaker/trend.go @@ -1,6 +1,8 @@ package bollmaker -import "github.com/c9s/bbgo/pkg/indicator" +import ( + indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" +) type PriceTrend string @@ -11,7 +13,7 @@ const ( UnknownTrend PriceTrend = "unknown" ) -func detectPriceTrend(inc *indicator.BOLLStream, price float64) PriceTrend { +func detectPriceTrend(inc *indicatorv2.BOLLStream, price float64) PriceTrend { if inBetween(price, inc.DownBand.Last(0), inc.UpBand.Last(0)) { return NeutralTrend } diff --git a/pkg/strategy/factorzoo/factors/price_mean_reversion.go b/pkg/strategy/factorzoo/factors/price_mean_reversion.go index fbc4c5ad4..ed3e5bd04 100644 --- a/pkg/strategy/factorzoo/factors/price_mean_reversion.go +++ b/pkg/strategy/factorzoo/factors/price_mean_reversion.go @@ -82,13 +82,13 @@ func (inc *PMR) PushK(k types.KLine) { return } - inc.Update(indicator.KLineClosePriceMapper(k)) + inc.Update(types.KLineClosePriceMapper(k)) inc.EndTime = k.EndTime.Time() inc.EmitUpdate(inc.Last(0)) } func CalculateKLinesPMR(allKLines []types.KLine, window int) float64 { - return pmr(indicator.MapKLinePrice(allKLines, indicator.KLineClosePriceMapper), window) + return pmr(types.MapKLinePrice(allKLines, types.KLineClosePriceMapper), window) } func pmr(prices []float64, window int) float64 { diff --git a/pkg/strategy/factorzoo/factors/price_volume_divergence.go b/pkg/strategy/factorzoo/factors/price_volume_divergence.go index f531eb214..16697bdbf 100644 --- a/pkg/strategy/factorzoo/factors/price_volume_divergence.go +++ b/pkg/strategy/factorzoo/factors/price_volume_divergence.go @@ -89,13 +89,13 @@ func (inc *PVD) PushK(k types.KLine) { return } - inc.Update(indicator.KLineClosePriceMapper(k), indicator.KLineVolumeMapper(k)) + inc.Update(types.KLineClosePriceMapper(k), types.KLineVolumeMapper(k)) inc.EndTime = k.EndTime.Time() inc.EmitUpdate(inc.Last(0)) } func CalculateKLinesPVD(allKLines []types.KLine, window int) float64 { - return pvd(indicator.MapKLinePrice(allKLines, indicator.KLineClosePriceMapper), indicator.MapKLinePrice(allKLines, indicator.KLineVolumeMapper), window) + return pvd(types.MapKLinePrice(allKLines, types.KLineClosePriceMapper), types.MapKLinePrice(allKLines, types.KLineVolumeMapper), window) } func pvd(prices []float64, volumes []float64, window int) float64 { diff --git a/pkg/strategy/factorzoo/factors/return_rate.go b/pkg/strategy/factorzoo/factors/return_rate.go index 114e4b9d9..08baf9883 100644 --- a/pkg/strategy/factorzoo/factors/return_rate.go +++ b/pkg/strategy/factorzoo/factors/return_rate.go @@ -81,7 +81,7 @@ func (inc *RR) PushK(k types.KLine) { return } - inc.Update(indicator.KLineClosePriceMapper(k)) + inc.Update(types.KLineClosePriceMapper(k)) inc.EndTime = k.EndTime.Time() inc.EmitUpdate(inc.Last(0)) } diff --git a/pkg/strategy/fmaker/A18.go b/pkg/strategy/fmaker/A18.go index 35dc7368e..17628e529 100644 --- a/pkg/strategy/fmaker/A18.go +++ b/pkg/strategy/fmaker/A18.go @@ -42,7 +42,7 @@ func (inc *A18) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateA18(recentT, indicator.KLineClosePriceMapper) + val, err := calculateA18(recentT, types.KLineClosePriceMapper) if err != nil { log.WithError(err).Error("can not calculate") return diff --git a/pkg/strategy/fmaker/A2.go b/pkg/strategy/fmaker/A2.go index cf674e1a6..b49c3e3b3 100644 --- a/pkg/strategy/fmaker/A2.go +++ b/pkg/strategy/fmaker/A2.go @@ -42,7 +42,7 @@ func (inc *A2) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateA2(recentT, KLineLowPriceMapper, KLineHighPriceMapper, indicator.KLineClosePriceMapper) + val, err := calculateA2(recentT, KLineLowPriceMapper, KLineHighPriceMapper, types.KLineClosePriceMapper) if err != nil { log.WithError(err).Error("can not calculate") return diff --git a/pkg/strategy/fmaker/A3.go b/pkg/strategy/fmaker/A3.go index 02118def8..479913c31 100644 --- a/pkg/strategy/fmaker/A3.go +++ b/pkg/strategy/fmaker/A3.go @@ -43,7 +43,7 @@ func (inc *A3) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateA3(recentT, KLineLowPriceMapper, KLineHighPriceMapper, indicator.KLineClosePriceMapper) + val, err := calculateA3(recentT, KLineLowPriceMapper, KLineHighPriceMapper, types.KLineClosePriceMapper) if err != nil { log.WithError(err).Error("can not calculate pivots") return diff --git a/pkg/strategy/fmaker/A34.go b/pkg/strategy/fmaker/A34.go index ba60aeedd..2714f426c 100644 --- a/pkg/strategy/fmaker/A34.go +++ b/pkg/strategy/fmaker/A34.go @@ -42,7 +42,7 @@ func (inc *A34) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateA34(recentT, indicator.KLineClosePriceMapper) + val, err := calculateA34(recentT, types.KLineClosePriceMapper) if err != nil { log.WithError(err).Error("can not calculate pivots") return diff --git a/pkg/strategy/fmaker/R.go b/pkg/strategy/fmaker/R.go index dc4f0eabf..7bd64f608 100644 --- a/pkg/strategy/fmaker/R.go +++ b/pkg/strategy/fmaker/R.go @@ -46,7 +46,7 @@ func (inc *R) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateR(recentT, indicator.KLineOpenPriceMapper, indicator.KLineClosePriceMapper) + val, err := calculateR(recentT, types.KLineOpenPriceMapper, types.KLineClosePriceMapper) if err != nil { log.WithError(err).Error("can not calculate pivots") return diff --git a/pkg/strategy/fmaker/S0.go b/pkg/strategy/fmaker/S0.go index 7d2eda158..fab32816a 100644 --- a/pkg/strategy/fmaker/S0.go +++ b/pkg/strategy/fmaker/S0.go @@ -42,7 +42,7 @@ func (inc *S0) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateS0(recentT, indicator.KLineClosePriceMapper) + val, err := calculateS0(recentT, types.KLineClosePriceMapper) if err != nil { log.WithError(err).Error("can not calculate") return diff --git a/pkg/strategy/fmaker/S1.go b/pkg/strategy/fmaker/S1.go index 68962a579..2eaa27f3c 100644 --- a/pkg/strategy/fmaker/S1.go +++ b/pkg/strategy/fmaker/S1.go @@ -40,7 +40,7 @@ func (inc *S1) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - correlation, err := calculateS1(recentT, inc.Window, KLineAmplitudeMapper, indicator.KLineVolumeMapper) + correlation, err := calculateS1(recentT, inc.Window, KLineAmplitudeMapper, types.KLineVolumeMapper) if err != nil { log.WithError(err).Error("can not calculate correlation") return diff --git a/pkg/strategy/fmaker/S2.go b/pkg/strategy/fmaker/S2.go index d63488a98..ea80d61ab 100644 --- a/pkg/strategy/fmaker/S2.go +++ b/pkg/strategy/fmaker/S2.go @@ -40,7 +40,7 @@ func (inc *S2) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - correlation, err := calculateS2(recentT, inc.Window, indicator.KLineOpenPriceMapper, indicator.KLineVolumeMapper) + correlation, err := calculateS2(recentT, inc.Window, types.KLineOpenPriceMapper, types.KLineVolumeMapper) if err != nil { log.WithError(err).Error("can not calculate correlation") return diff --git a/pkg/strategy/fmaker/S3.go b/pkg/strategy/fmaker/S3.go index e00f83625..f3bd88082 100644 --- a/pkg/strategy/fmaker/S3.go +++ b/pkg/strategy/fmaker/S3.go @@ -42,7 +42,7 @@ func (inc *S3) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateS3(recentT, indicator.KLineClosePriceMapper, indicator.KLineOpenPriceMapper) + val, err := calculateS3(recentT, types.KLineClosePriceMapper, types.KLineOpenPriceMapper) if err != nil { log.WithError(err).Error("can not calculate") return diff --git a/pkg/strategy/fmaker/S4.go b/pkg/strategy/fmaker/S4.go index b7f8cfd8c..97e97595a 100644 --- a/pkg/strategy/fmaker/S4.go +++ b/pkg/strategy/fmaker/S4.go @@ -42,7 +42,7 @@ func (inc *S4) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateS4(recentT, indicator.KLineClosePriceMapper) + val, err := calculateS4(recentT, types.KLineClosePriceMapper) if err != nil { log.WithError(err).Error("can not calculate") return diff --git a/pkg/strategy/fmaker/S5.go b/pkg/strategy/fmaker/S5.go index 8fd2edfb7..77e7070e0 100644 --- a/pkg/strategy/fmaker/S5.go +++ b/pkg/strategy/fmaker/S5.go @@ -42,7 +42,7 @@ func (inc *S5) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateS5(recentT, indicator.KLineVolumeMapper) + val, err := calculateS5(recentT, types.KLineVolumeMapper) if err != nil { log.WithError(err).Error("can not calculate pivots") return diff --git a/pkg/strategy/fmaker/S6.go b/pkg/strategy/fmaker/S6.go index 8c7f73c16..5e6bac789 100644 --- a/pkg/strategy/fmaker/S6.go +++ b/pkg/strategy/fmaker/S6.go @@ -42,7 +42,7 @@ func (inc *S6) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateS6(recentT, indicator.KLineHighPriceMapper, indicator.KLineLowPriceMapper, indicator.KLineClosePriceMapper, indicator.KLineVolumeMapper) + val, err := calculateS6(recentT, types.KLineHighPriceMapper, types.KLineLowPriceMapper, types.KLineClosePriceMapper, types.KLineVolumeMapper) if err != nil { log.WithError(err).Error("can not calculate") return diff --git a/pkg/strategy/fmaker/S7.go b/pkg/strategy/fmaker/S7.go index 048078fe2..e8e060d7a 100644 --- a/pkg/strategy/fmaker/S7.go +++ b/pkg/strategy/fmaker/S7.go @@ -42,7 +42,7 @@ func (inc *S7) CalculateAndUpdate(klines []types.KLine) { var recentT = klines[end-(inc.Window-1) : end+1] - val, err := calculateS7(recentT, indicator.KLineOpenPriceMapper, indicator.KLineClosePriceMapper) + val, err := calculateS7(recentT, types.KLineOpenPriceMapper, types.KLineClosePriceMapper) if err != nil { log.WithError(err).Error("can not calculate") return diff --git a/pkg/strategy/harmonic/shark.go b/pkg/strategy/harmonic/shark.go index a23d7e62e..5071f35cb 100644 --- a/pkg/strategy/harmonic/shark.go +++ b/pkg/strategy/harmonic/shark.go @@ -72,7 +72,7 @@ func (inc *SHARK) PushK(k types.KLine) { return } - inc.Update(indicator.KLineHighPriceMapper(k), indicator.KLineLowPriceMapper(k), indicator.KLineClosePriceMapper(k)) + inc.Update(types.KLineHighPriceMapper(k), types.KLineLowPriceMapper(k), types.KLineClosePriceMapper(k)) inc.EndTime = k.EndTime.Time() inc.EmitUpdate(inc.Last(0)) } diff --git a/pkg/strategy/irr/neg_return_rate.go b/pkg/strategy/irr/neg_return_rate.go index f16adecbe..af2b48b57 100644 --- a/pkg/strategy/irr/neg_return_rate.go +++ b/pkg/strategy/irr/neg_return_rate.go @@ -74,7 +74,7 @@ func (inc *NRR) PushK(k types.KLine) { return } - inc.Update(indicator.KLineOpenPriceMapper(k), indicator.KLineClosePriceMapper(k)) + inc.Update(types.KLineOpenPriceMapper(k), types.KLineClosePriceMapper(k)) inc.EndTime = k.EndTime.Time() inc.EmitUpdate(inc.Last(0)) } diff --git a/pkg/strategy/rsicross/strategy.go b/pkg/strategy/rsicross/strategy.go index 5710e418e..dafab6ae7 100644 --- a/pkg/strategy/rsicross/strategy.go +++ b/pkg/strategy/rsicross/strategy.go @@ -9,7 +9,7 @@ import ( "github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/fixedpoint" - "github.com/c9s/bbgo/pkg/indicator" + "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/strategy/common" "github.com/c9s/bbgo/pkg/types" ) @@ -52,10 +52,10 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se fastRsi := session.Indicators(s.Symbol).RSI(types.IntervalWindow{Interval: s.Interval, Window: s.FastWindow}) slowRsi := session.Indicators(s.Symbol).RSI(types.IntervalWindow{Interval: s.Interval, Window: s.SlowWindow}) - rsiCross := indicator.Cross(fastRsi, slowRsi) + rsiCross := indicatorv2.Cross(fastRsi, slowRsi) rsiCross.OnUpdate(func(v float64) { - switch indicator.CrossType(v) { - case indicator.CrossOver: + switch indicatorv2.CrossType(v) { + case indicatorv2.CrossOver: opts := s.OpenPositionOptions opts.Long = true @@ -69,7 +69,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se logErr(err, "unable to open position") } - case indicator.CrossUnder: + case indicatorv2.CrossUnder: if err := s.OrderExecutor.ClosePosition(ctx, fixedpoint.One); err != nil { logErr(err, "failed to close position") } diff --git a/pkg/strategy/scmaker/intensity.go b/pkg/strategy/scmaker/intensity.go index b4f3cb383..047224123 100644 --- a/pkg/strategy/scmaker/intensity.go +++ b/pkg/strategy/scmaker/intensity.go @@ -2,24 +2,24 @@ package scmaker import ( "github.com/c9s/bbgo/pkg/fixedpoint" - "github.com/c9s/bbgo/pkg/indicator" + indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/types" ) type IntensityStream struct { - *indicator.Float64Series + *types.Float64Series - Buy, Sell *indicator.RMAStream + Buy, Sell *indicatorv2.RMAStream window int } -func Intensity(source indicator.KLineSubscription, window int) *IntensityStream { +func Intensity(source indicatorv2.KLineSubscription, window int) *IntensityStream { s := &IntensityStream{ - Float64Series: indicator.NewFloat64Series(), + Float64Series: types.NewFloat64Series(), window: window, - Buy: indicator.RMA2(indicator.NewFloat64Series(), window, false), - Sell: indicator.RMA2(indicator.NewFloat64Series(), window, false), + Buy: indicatorv2.RMA2(types.NewFloat64Series(), window, false), + Sell: indicatorv2.RMA2(types.NewFloat64Series(), window, false), } threshold := fixedpoint.NewFromFloat(100.0) diff --git a/pkg/strategy/scmaker/strategy.go b/pkg/strategy/scmaker/strategy.go index 6dc1dc659..7356ed84f 100644 --- a/pkg/strategy/scmaker/strategy.go +++ b/pkg/strategy/scmaker/strategy.go @@ -10,7 +10,7 @@ import ( "github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/fixedpoint" - "github.com/c9s/bbgo/pkg/indicator" + indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/risk/riskcontrol" "github.com/c9s/bbgo/pkg/strategy/common" "github.com/c9s/bbgo/pkg/types" @@ -73,8 +73,8 @@ type Strategy struct { liquidityScale bbgo.Scale // indicators - ewma *indicator.EWMAStream - boll *indicator.BOLLStream + ewma *indicatorv2.EWMAStream + boll *indicatorv2.BOLLStream intensity *IntensityStream positionRiskControl *riskcontrol.PositionRiskControl @@ -172,7 +172,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se return nil } -func (s *Strategy) preloadKLines(inc *indicator.KLineStream, session *bbgo.ExchangeSession, symbol string, interval types.Interval) { +func (s *Strategy) preloadKLines(inc *indicatorv2.KLineStream, session *bbgo.ExchangeSession, symbol string, interval types.Interval) { if store, ok := session.MarketDataStore(symbol); ok { if kLinesData, ok := store.KLinesOfInterval(interval); ok { for _, k := range *kLinesData { @@ -183,23 +183,23 @@ func (s *Strategy) preloadKLines(inc *indicator.KLineStream, session *bbgo.Excha } func (s *Strategy) initializeMidPriceEMA(session *bbgo.ExchangeSession) { - kLines := indicator.KLines(session.MarketDataStream, s.Symbol, s.MidPriceEMA.Interval) - s.ewma = indicator.EWMA2(indicator.ClosePrices(kLines), s.MidPriceEMA.Window) + kLines := indicatorv2.KLines(session.MarketDataStream, s.Symbol, s.MidPriceEMA.Interval) + s.ewma = indicatorv2.EWMA2(indicatorv2.ClosePrices(kLines), s.MidPriceEMA.Window) s.preloadKLines(kLines, session, s.Symbol, s.MidPriceEMA.Interval) } func (s *Strategy) initializeIntensityIndicator(session *bbgo.ExchangeSession) { - kLines := indicator.KLines(session.MarketDataStream, s.Symbol, s.StrengthInterval) + kLines := indicatorv2.KLines(session.MarketDataStream, s.Symbol, s.StrengthInterval) s.intensity = Intensity(kLines, 10) s.preloadKLines(kLines, session, s.Symbol, s.StrengthInterval) } func (s *Strategy) initializePriceRangeBollinger(session *bbgo.ExchangeSession) { - kLines := indicator.KLines(session.MarketDataStream, s.Symbol, s.PriceRangeBollinger.Interval) - closePrices := indicator.ClosePrices(kLines) - s.boll = indicator.BOLL2(closePrices, s.PriceRangeBollinger.Window, s.PriceRangeBollinger.K) + kLines := indicatorv2.KLines(session.MarketDataStream, s.Symbol, s.PriceRangeBollinger.Interval) + closePrices := indicatorv2.ClosePrices(kLines) + s.boll = indicatorv2.BOLL(closePrices, s.PriceRangeBollinger.Window, s.PriceRangeBollinger.K) s.preloadKLines(kLines, session, s.Symbol, s.PriceRangeBollinger.Interval) } diff --git a/pkg/indicator/float64updater.go b/pkg/types/float64updater.go similarity index 86% rename from pkg/indicator/float64updater.go rename to pkg/types/float64updater.go index a9743538e..ba3b5e03f 100644 --- a/pkg/indicator/float64updater.go +++ b/pkg/types/float64updater.go @@ -1,4 +1,4 @@ -package indicator +package types //go:generate callbackgen -type Float64Updater type Float64Updater struct { diff --git a/pkg/types/float64updater_callbacks.go b/pkg/types/float64updater_callbacks.go new file mode 100644 index 000000000..28b2b6f78 --- /dev/null +++ b/pkg/types/float64updater_callbacks.go @@ -0,0 +1,15 @@ +// Code generated by "callbackgen -type Float64Updater"; DO NOT EDIT. + +package types + +import () + +func (F *Float64Updater) OnUpdate(cb func(v float64)) { + F.updateCallbacks = append(F.updateCallbacks, cb) +} + +func (F *Float64Updater) EmitUpdate(v float64) { + for _, cb := range F.updateCallbacks { + cb(v) + } +} diff --git a/pkg/types/kline.go b/pkg/types/kline.go index a17e1e94e..e405ba94b 100644 --- a/pkg/types/kline.go +++ b/pkg/types/kline.go @@ -645,3 +645,41 @@ func KLineWith(symbol string, interval Interval, callback KLineCallback) KLineCa callback(k) } } + +type KLineValueMapper func(k KLine) float64 + +func KLineOpenPriceMapper(k KLine) float64 { + return k.Open.Float64() +} + +func KLineClosePriceMapper(k KLine) float64 { + return k.Close.Float64() +} + +func KLineTypicalPriceMapper(k KLine) float64 { + return (k.High.Float64() + k.Low.Float64() + k.Close.Float64()) / 3. +} + +func KLinePriceVolumeMapper(k KLine) float64 { + return k.Close.Mul(k.Volume).Float64() +} + +func KLineVolumeMapper(k KLine) float64 { + return k.Volume.Float64() +} + +func MapKLinePrice(kLines []KLine, f KLineValueMapper) (prices []float64) { + for _, k := range kLines { + prices = append(prices, f(k)) + } + + return prices +} + +func KLineLowPriceMapper(k KLine) float64 { + return k.Low.Float64() +} + +func KLineHighPriceMapper(k KLine) float64 { + return k.High.Float64() +} diff --git a/pkg/indicator/float64series.go b/pkg/types/series_float64.go similarity index 73% rename from pkg/indicator/float64series.go rename to pkg/types/series_float64.go index 8035acb96..6dd6704aa 100644 --- a/pkg/indicator/float64series.go +++ b/pkg/types/series_float64.go @@ -1,25 +1,24 @@ -package indicator +package types import ( "github.com/c9s/bbgo/pkg/datatype/floats" - "github.com/c9s/bbgo/pkg/types" ) type Float64Series struct { - types.SeriesBase + SeriesBase Float64Updater - slice floats.Slice + Slice floats.Slice } func NewFloat64Series(v ...float64) *Float64Series { s := &Float64Series{} - s.slice = v - s.SeriesBase.Series = s.slice + s.Slice = v + s.SeriesBase.Series = s.Slice return s } func (f *Float64Series) Last(i int) float64 { - return f.slice.Last(i) + return f.Slice.Last(i) } func (f *Float64Series) Index(i int) float64 { @@ -27,15 +26,11 @@ func (f *Float64Series) Index(i int) float64 { } func (f *Float64Series) Length() int { - return len(f.slice) -} - -func (f *Float64Series) Slice() floats.Slice { - return f.slice + return len(f.Slice) } func (f *Float64Series) PushAndEmit(x float64) { - f.slice.Push(x) + f.Slice.Push(x) f.EmitUpdate(x) } @@ -71,3 +66,22 @@ func (f *Float64Series) Bind(source Float64Source, target Float64Calculator) { f.Subscribe(source, c) } } + +type Float64Calculator interface { + Calculate(x float64) float64 + PushAndEmit(x float64) +} + +type Float64Source interface { + Series + OnUpdate(f func(v float64)) +} + +type Float64Subscription interface { + Series + AddSubscriber(f func(v float64)) +} + +type Float64Truncator interface { + Truncate() +}