88 lines
2.4 KiB
Go
88 lines
2.4 KiB
Go
|
package xmaker
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
|
||
|
"github.com/prometheus/client_golang/prometheus"
|
||
|
|
||
|
"git.qtrade.icu/lychiyu/bbgo/pkg/bbgo"
|
||
|
"git.qtrade.icu/lychiyu/bbgo/pkg/fixedpoint"
|
||
|
"git.qtrade.icu/lychiyu/bbgo/pkg/indicator/v2"
|
||
|
"git.qtrade.icu/lychiyu/bbgo/pkg/types"
|
||
|
)
|
||
|
|
||
|
var bollingerBandSignalMetrics = prometheus.NewGaugeVec(
|
||
|
prometheus.GaugeOpts{
|
||
|
Name: "xmaker_bollinger_band_signal",
|
||
|
Help: "",
|
||
|
}, []string{"symbol"})
|
||
|
|
||
|
func init() {
|
||
|
prometheus.MustRegister(bollingerBandSignalMetrics)
|
||
|
}
|
||
|
|
||
|
type BollingerBandTrendSignal struct {
|
||
|
types.IntervalWindow
|
||
|
MinBandWidth float64 `json:"minBandWidth"`
|
||
|
MaxBandWidth float64 `json:"maxBandWidth"`
|
||
|
|
||
|
indicator *indicatorv2.BOLLStream
|
||
|
symbol string
|
||
|
lastK *types.KLine
|
||
|
}
|
||
|
|
||
|
func (s *BollingerBandTrendSignal) Bind(ctx context.Context, session *bbgo.ExchangeSession, symbol string) error {
|
||
|
if s.MaxBandWidth == 0.0 {
|
||
|
s.MaxBandWidth = 2.0
|
||
|
}
|
||
|
|
||
|
if s.MinBandWidth == 0.0 {
|
||
|
s.MinBandWidth = 1.0
|
||
|
}
|
||
|
|
||
|
s.symbol = symbol
|
||
|
s.indicator = session.Indicators(symbol).BOLL(s.IntervalWindow, s.MinBandWidth)
|
||
|
|
||
|
session.MarketDataStream.OnKLineClosed(types.KLineWith(s.symbol, s.IntervalWindow.Interval, func(kline types.KLine) {
|
||
|
s.lastK = &kline
|
||
|
}))
|
||
|
|
||
|
bollingerBandSignalMetrics.WithLabelValues(s.symbol).Set(0.0)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *BollingerBandTrendSignal) CalculateSignal(ctx context.Context) (float64, error) {
|
||
|
if s.lastK == nil {
|
||
|
return 0, nil
|
||
|
}
|
||
|
|
||
|
closePrice := s.lastK.Close
|
||
|
|
||
|
// when bid price is lower than the down band, then it's in the downtrend
|
||
|
// when ask price is higher than the up band, then it's in the uptrend
|
||
|
lastDownBand := fixedpoint.NewFromFloat(s.indicator.DownBand.Last(0))
|
||
|
lastUpBand := fixedpoint.NewFromFloat(s.indicator.UpBand.Last(0))
|
||
|
|
||
|
maxBandWidth := s.indicator.StdDev.Last(0) * s.MaxBandWidth
|
||
|
|
||
|
signal := 0.0
|
||
|
|
||
|
// if the price is inside the band, do not vote
|
||
|
if closePrice.Compare(lastDownBand) > 0 && closePrice.Compare(lastUpBand) < 0 {
|
||
|
signal = 0.0
|
||
|
} else if closePrice.Compare(lastDownBand) < 0 {
|
||
|
signal = lastDownBand.Sub(closePrice).Float64() / maxBandWidth * -2.0
|
||
|
} else if closePrice.Compare(lastUpBand) > 0 {
|
||
|
signal = closePrice.Sub(lastUpBand).Float64() / maxBandWidth * 2.0
|
||
|
}
|
||
|
|
||
|
log.Infof("[BollingerBandTrendSignal] %f up/down = %f/%f, close price = %f",
|
||
|
signal,
|
||
|
lastUpBand.Float64(),
|
||
|
lastDownBand.Float64(),
|
||
|
closePrice.Float64())
|
||
|
|
||
|
bollingerBandSignalMetrics.WithLabelValues(s.symbol).Set(signal)
|
||
|
return signal, nil
|
||
|
}
|