mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
xmaker: add signal providers
This commit is contained in:
parent
d9fb9ff3e0
commit
9ebab4f4f7
|
@ -38,6 +38,12 @@ var askMarginMetrics = prometheus.NewGaugeVec(
|
||||||
Help: "the current ask margin (dynamic)",
|
Help: "the current ask margin (dynamic)",
|
||||||
}, []string{"strategy_type", "strategy_id", "exchange", "symbol"})
|
}, []string{"strategy_type", "strategy_id", "exchange", "symbol"})
|
||||||
|
|
||||||
|
var finalSignalMetrics = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "xmaker_final_signal",
|
||||||
|
Help: "",
|
||||||
|
}, []string{"strategy_type", "strategy_id", "exchange", "symbol"})
|
||||||
|
|
||||||
var configNumOfLayersMetrics = prometheus.NewGaugeVec(
|
var configNumOfLayersMetrics = prometheus.NewGaugeVec(
|
||||||
prometheus.GaugeOpts{
|
prometheus.GaugeOpts{
|
||||||
Name: "xmaker_config_num_of_layers",
|
Name: "xmaker_config_num_of_layers",
|
||||||
|
@ -70,6 +76,7 @@ func init() {
|
||||||
makerBestAskPriceMetrics,
|
makerBestAskPriceMetrics,
|
||||||
bidMarginMetrics,
|
bidMarginMetrics,
|
||||||
askMarginMetrics,
|
askMarginMetrics,
|
||||||
|
finalSignalMetrics,
|
||||||
configNumOfLayersMetrics,
|
configNumOfLayersMetrics,
|
||||||
configMaxExposureMetrics,
|
configMaxExposureMetrics,
|
||||||
configBidMarginMetrics,
|
configBidMarginMetrics,
|
||||||
|
|
87
pkg/strategy/xmaker/signal_boll.go
Normal file
87
pkg/strategy/xmaker/signal_boll.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package xmaker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/indicator/v2"
|
||||||
|
"github.com/c9s/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))
|
||||||
|
|
||||||
|
log.Infof("bollinger band: up/down = %f/%f, close price = %f",
|
||||||
|
lastUpBand.Float64(),
|
||||||
|
lastDownBand.Float64(),
|
||||||
|
closePrice.Float64())
|
||||||
|
|
||||||
|
// if the price is inside the band, do not vote
|
||||||
|
if closePrice.Compare(lastDownBand) > 0 && closePrice.Compare(lastUpBand) < 0 {
|
||||||
|
return 0.0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
maxBandWidth := s.indicator.StdDev.Last(0) * s.MaxBandWidth
|
||||||
|
|
||||||
|
signal := 0.0
|
||||||
|
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("bollinger signal: %f", signal)
|
||||||
|
return signal, nil
|
||||||
|
}
|
70
pkg/strategy/xmaker/signal_book.go
Normal file
70
pkg/strategy/xmaker/signal_book.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package xmaker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var orderBookSignalMetrics = prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: "xmaker_order_book_signal",
|
||||||
|
Help: "",
|
||||||
|
}, []string{"symbol"})
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
prometheus.MustRegister(orderBookSignalMetrics)
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderBookBestPriceVolumeSignal struct {
|
||||||
|
RatioThreshold fixedpoint.Value `json:"ratioThreshold"`
|
||||||
|
MinVolume fixedpoint.Value `json:"minVolume"`
|
||||||
|
|
||||||
|
symbol string
|
||||||
|
book *types.StreamOrderBook
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderBookBestPriceVolumeSignal) Bind(ctx context.Context, session *bbgo.ExchangeSession, symbol string) error {
|
||||||
|
if s.book == nil {
|
||||||
|
return errors.New("s.book can not be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.symbol = symbol
|
||||||
|
orderBookSignalMetrics.WithLabelValues(s.symbol).Set(0.0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *OrderBookBestPriceVolumeSignal) CalculateSignal(ctx context.Context) (float64, error) {
|
||||||
|
bid, ask, ok := s.book.BestBidAndAsk()
|
||||||
|
if !ok {
|
||||||
|
return 0.0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if bid.Volume.Compare(s.MinVolume) < 0 && ask.Volume.Compare(s.MinVolume) < 0 {
|
||||||
|
return 0.0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("OrderBookBestPriceVolumeSignal: bid/ask = %f/%f", bid.Volume.Float64(), ask.Volume.Float64())
|
||||||
|
|
||||||
|
// TODO: may use scale to define this
|
||||||
|
sumVol := bid.Volume.Add(ask.Volume)
|
||||||
|
bidRatio := bid.Volume.Div(sumVol)
|
||||||
|
askRatio := ask.Volume.Div(sumVol)
|
||||||
|
denominator := fixedpoint.One.Sub(s.RatioThreshold)
|
||||||
|
signal := 0.0
|
||||||
|
if bidRatio.Compare(s.RatioThreshold) >= 0 {
|
||||||
|
numerator := bidRatio.Sub(s.RatioThreshold)
|
||||||
|
signal = numerator.Div(denominator).Float64()
|
||||||
|
} else if askRatio.Compare(s.RatioThreshold) >= 0 {
|
||||||
|
numerator := askRatio.Sub(s.RatioThreshold)
|
||||||
|
signal = -numerator.Div(denominator).Float64()
|
||||||
|
}
|
||||||
|
|
||||||
|
orderBookSignalMetrics.WithLabelValues(s.symbol).Set(signal)
|
||||||
|
return signal, nil
|
||||||
|
}
|
|
@ -31,6 +31,41 @@ const ID = "xmaker"
|
||||||
|
|
||||||
var log = logrus.WithField("strategy", ID)
|
var log = logrus.WithField("strategy", ID)
|
||||||
|
|
||||||
|
type Quote struct {
|
||||||
|
BestBidPrice, BestAskPrice fixedpoint.Value
|
||||||
|
|
||||||
|
BidMargin, AskMargin fixedpoint.Value
|
||||||
|
|
||||||
|
// BidLayerPips is the price pips between each layer
|
||||||
|
BidLayerPips, AskLayerPips fixedpoint.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionBinder interface {
|
||||||
|
Bind(ctx context.Context, session *bbgo.ExchangeSession, symbol string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignalNumber float64
|
||||||
|
|
||||||
|
const (
|
||||||
|
SignalNumberMaxLong = 2.0
|
||||||
|
SignalNumberMaxShort = -2.0
|
||||||
|
)
|
||||||
|
|
||||||
|
type SignalProvider interface {
|
||||||
|
CalculateSignal(ctx context.Context) (float64, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type KLineShapeSignal struct {
|
||||||
|
FullBodyThreshold float64 `json:"fullBodyThreshold"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignalConfig struct {
|
||||||
|
Weight float64 `json:"weight"`
|
||||||
|
BollingerBandTrendSignal *BollingerBandTrendSignal `json:"bollingerBandTrend,omitempty"`
|
||||||
|
OrderBookBestPriceSignal *OrderBookBestPriceVolumeSignal `json:"orderBookBestPrice,omitempty"`
|
||||||
|
KLineShapeSignal *KLineShapeSignal `json:"klineShape,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
bbgo.RegisterStrategy(ID, &Strategy{})
|
bbgo.RegisterStrategy(ID, &Strategy{})
|
||||||
}
|
}
|
||||||
|
@ -50,6 +85,8 @@ type Strategy struct {
|
||||||
HedgeInterval types.Duration `json:"hedgeInterval"`
|
HedgeInterval types.Duration `json:"hedgeInterval"`
|
||||||
OrderCancelWaitTime types.Duration `json:"orderCancelWaitTime"`
|
OrderCancelWaitTime types.Duration `json:"orderCancelWaitTime"`
|
||||||
|
|
||||||
|
SignalConfigList []SignalConfig `json:"signals"`
|
||||||
|
|
||||||
Margin fixedpoint.Value `json:"margin"`
|
Margin fixedpoint.Value `json:"margin"`
|
||||||
BidMargin fixedpoint.Value `json:"bidMargin"`
|
BidMargin fixedpoint.Value `json:"bidMargin"`
|
||||||
AskMargin fixedpoint.Value `json:"askMargin"`
|
AskMargin fixedpoint.Value `json:"askMargin"`
|
||||||
|
@ -138,6 +175,8 @@ type Strategy struct {
|
||||||
circuitBreakerAlertLimiter *rate.Limiter
|
circuitBreakerAlertLimiter *rate.Limiter
|
||||||
|
|
||||||
logger logrus.FieldLogger
|
logger logrus.FieldLogger
|
||||||
|
|
||||||
|
metricsLabels prometheus.Labels
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) ID() string {
|
func (s *Strategy) ID() string {
|
||||||
|
@ -192,18 +231,16 @@ func (s *Strategy) Initialize() error {
|
||||||
"strategy": ID,
|
"strategy": ID,
|
||||||
"strategy_id": s.InstanceID(),
|
"strategy_id": s.InstanceID(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
s.metricsLabels = prometheus.Labels{
|
||||||
|
"strategy_type": ID,
|
||||||
|
"strategy_id": s.InstanceID(),
|
||||||
|
"exchange": s.MakerExchange,
|
||||||
|
"symbol": s.Symbol,
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Quote struct {
|
|
||||||
BestBidPrice, BestAskPrice fixedpoint.Value
|
|
||||||
|
|
||||||
BidMargin, AskMargin fixedpoint.Value
|
|
||||||
|
|
||||||
// BidLayerPips is the price pips between each layer
|
|
||||||
BidLayerPips, AskLayerPips fixedpoint.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// getBollingerTrend returns -1 when the price is in the downtrend, 1 when the price is in the uptrend, 0 when the price is in the band
|
// getBollingerTrend returns -1 when the price is in the downtrend, 1 when the price is in the uptrend, 0 when the price is in the band
|
||||||
func (s *Strategy) getBollingerTrend(quote *Quote) int {
|
func (s *Strategy) getBollingerTrend(quote *Quote) int {
|
||||||
// when bid price is lower than the down band, then it's in the downtrend
|
// when bid price is lower than the down band, then it's in the downtrend
|
||||||
|
@ -211,12 +248,6 @@ func (s *Strategy) getBollingerTrend(quote *Quote) int {
|
||||||
lastDownBand := fixedpoint.NewFromFloat(s.boll.DownBand.Last(0))
|
lastDownBand := fixedpoint.NewFromFloat(s.boll.DownBand.Last(0))
|
||||||
lastUpBand := fixedpoint.NewFromFloat(s.boll.UpBand.Last(0))
|
lastUpBand := fixedpoint.NewFromFloat(s.boll.UpBand.Last(0))
|
||||||
|
|
||||||
s.logger.Infof("bollinger band: up/down = %f/%f, bid/ask = %f/%f",
|
|
||||||
lastUpBand.Float64(),
|
|
||||||
lastDownBand.Float64(),
|
|
||||||
quote.BestBidPrice.Float64(),
|
|
||||||
quote.BestAskPrice.Float64())
|
|
||||||
|
|
||||||
if quote.BestAskPrice.Compare(lastDownBand) < 0 {
|
if quote.BestAskPrice.Compare(lastDownBand) < 0 {
|
||||||
return -1
|
return -1
|
||||||
} else if quote.BestBidPrice.Compare(lastUpBand) > 0 {
|
} else if quote.BestBidPrice.Compare(lastUpBand) > 0 {
|
||||||
|
@ -282,6 +313,43 @@ func (s *Strategy) applyBollingerMargin(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Strategy) calculateSignal(ctx context.Context) (float64, error) {
|
||||||
|
sum := 0.0
|
||||||
|
voters := 0.0
|
||||||
|
for _, signal := range s.SignalConfigList {
|
||||||
|
if signal.OrderBookBestPriceSignal != nil {
|
||||||
|
sig, err := signal.OrderBookBestPriceSignal.CalculateSignal(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if signal.Weight > 0.0 {
|
||||||
|
sum += sig * signal.Weight
|
||||||
|
voters += signal.Weight
|
||||||
|
} else {
|
||||||
|
sum += sig
|
||||||
|
voters++
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if signal.BollingerBandTrendSignal != nil {
|
||||||
|
sig, err := signal.BollingerBandTrendSignal.CalculateSignal(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if signal.Weight > 0.0 {
|
||||||
|
sum += sig * signal.Weight
|
||||||
|
voters += signal.Weight
|
||||||
|
} else {
|
||||||
|
sum += sig
|
||||||
|
voters++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum / voters, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Strategy) updateQuote(ctx context.Context) {
|
func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
if err := s.activeMakerOrders.GracefulCancel(ctx, s.makerSession.Exchange); err != nil {
|
if err := s.activeMakerOrders.GracefulCancel(ctx, s.makerSession.Exchange); err != nil {
|
||||||
s.logger.Warnf("there are some %s orders not canceled, skipping placing maker orders", s.Symbol)
|
s.logger.Warnf("there are some %s orders not canceled, skipping placing maker orders", s.Symbol)
|
||||||
|
@ -293,6 +361,15 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signal, err := s.calculateSignal(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Infof("Final signal: %f", signal)
|
||||||
|
|
||||||
|
finalSignalMetrics.With(s.metricsLabels).Set(signal)
|
||||||
|
|
||||||
if s.CircuitBreaker != nil {
|
if s.CircuitBreaker != nil {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if reason, halted := s.CircuitBreaker.IsHalted(now); halted {
|
if reason, halted := s.CircuitBreaker.IsHalted(now); halted {
|
||||||
|
@ -500,13 +577,6 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
labels := prometheus.Labels{
|
|
||||||
"strategy_type": ID,
|
|
||||||
"strategy_id": s.InstanceID(),
|
|
||||||
"exchange": s.MakerExchange,
|
|
||||||
"symbol": s.Symbol,
|
|
||||||
}
|
|
||||||
|
|
||||||
bidExposureInUsd := fixedpoint.Zero
|
bidExposureInUsd := fixedpoint.Zero
|
||||||
askExposureInUsd := fixedpoint.Zero
|
askExposureInUsd := fixedpoint.Zero
|
||||||
bidPrice := quote.BestBidPrice
|
bidPrice := quote.BestBidPrice
|
||||||
|
@ -520,8 +590,8 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bidMarginMetrics.With(labels).Set(quote.BidMargin.Float64())
|
bidMarginMetrics.With(s.metricsLabels).Set(quote.BidMargin.Float64())
|
||||||
askMarginMetrics.With(labels).Set(quote.AskMargin.Float64())
|
askMarginMetrics.With(s.metricsLabels).Set(quote.AskMargin.Float64())
|
||||||
|
|
||||||
for i := 0; i < s.NumLayers; i++ {
|
for i := 0; i < s.NumLayers; i++ {
|
||||||
// for maker bid orders
|
// for maker bid orders
|
||||||
|
@ -566,7 +636,7 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
s.logger.Infof("maker best bid price %f", bidPrice.Float64())
|
s.logger.Infof("maker best bid price %f", bidPrice.Float64())
|
||||||
makerBestBidPriceMetrics.With(labels).Set(bidPrice.Float64())
|
makerBestBidPriceMetrics.With(s.metricsLabels).Set(bidPrice.Float64())
|
||||||
}
|
}
|
||||||
|
|
||||||
if makerQuota.QuoteAsset.Lock(bidQuantity.Mul(bidPrice)) && hedgeQuota.BaseAsset.Lock(bidQuantity) {
|
if makerQuota.QuoteAsset.Lock(bidQuantity.Mul(bidPrice)) && hedgeQuota.BaseAsset.Lock(bidQuantity) {
|
||||||
|
@ -634,7 +704,7 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
s.logger.Infof("maker best ask price %f", askPrice.Float64())
|
s.logger.Infof("maker best ask price %f", askPrice.Float64())
|
||||||
makerBestAskPriceMetrics.With(labels).Set(askPrice.Float64())
|
makerBestAskPriceMetrics.With(s.metricsLabels).Set(askPrice.Float64())
|
||||||
}
|
}
|
||||||
|
|
||||||
if makerQuota.BaseAsset.Lock(askQuantity) && hedgeQuota.QuoteAsset.Lock(askQuantity.Mul(askPrice)) {
|
if makerQuota.BaseAsset.Lock(askQuantity) && hedgeQuota.QuoteAsset.Lock(askQuantity.Mul(askPrice)) {
|
||||||
|
@ -687,8 +757,8 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
log.WithError(err).Errorf("unable to place maker orders: %+v", formattedOrders)
|
log.WithError(err).Errorf("unable to place maker orders: %+v", formattedOrders)
|
||||||
}
|
}
|
||||||
|
|
||||||
openOrderBidExposureInUsdMetrics.With(labels).Set(bidExposureInUsd.Float64())
|
openOrderBidExposureInUsdMetrics.With(s.metricsLabels).Set(bidExposureInUsd.Float64())
|
||||||
openOrderAskExposureInUsdMetrics.With(labels).Set(askExposureInUsd.Float64())
|
openOrderAskExposureInUsdMetrics.With(s.metricsLabels).Set(askExposureInUsd.Float64())
|
||||||
|
|
||||||
_ = errIdx
|
_ = errIdx
|
||||||
_ = createdOrders
|
_ = createdOrders
|
||||||
|
@ -1039,7 +1109,6 @@ func (s *Strategy) hedgeWorker(ctx context.Context) {
|
||||||
func (s *Strategy) CrossRun(
|
func (s *Strategy) CrossRun(
|
||||||
ctx context.Context, orderExecutionRouter bbgo.OrderExecutionRouter, sessions map[string]*bbgo.ExchangeSession,
|
ctx context.Context, orderExecutionRouter bbgo.OrderExecutionRouter, sessions map[string]*bbgo.ExchangeSession,
|
||||||
) error {
|
) error {
|
||||||
|
|
||||||
instanceID := s.InstanceID()
|
instanceID := s.InstanceID()
|
||||||
|
|
||||||
// configure sessions
|
// configure sessions
|
||||||
|
@ -1125,6 +1194,14 @@ func (s *Strategy) CrossRun(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.sourceSession.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, types.Interval1m, func(k types.KLine) {
|
||||||
|
s.priceSolver.Update(k.Symbol, k.Close)
|
||||||
|
feeToken := s.sourceSession.Exchange.PlatformFeeCurrency()
|
||||||
|
if feePrice, ok := s.priceSolver.ResolvePrice(feeToken, "USDT"); ok {
|
||||||
|
s.Position.SetFeeAverageCost(feeToken, feePrice)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
if s.ProfitFixerConfig != nil {
|
if s.ProfitFixerConfig != nil {
|
||||||
bbgo.Notify("Fixing %s profitStats and position...", s.Symbol)
|
bbgo.Notify("Fixing %s profitStats and position...", s.Symbol)
|
||||||
|
|
||||||
|
@ -1169,6 +1246,25 @@ func (s *Strategy) CrossRun(
|
||||||
s.book = types.NewStreamBook(s.Symbol, s.sourceSession.ExchangeName)
|
s.book = types.NewStreamBook(s.Symbol, s.sourceSession.ExchangeName)
|
||||||
s.book.BindStream(s.sourceSession.MarketDataStream)
|
s.book.BindStream(s.sourceSession.MarketDataStream)
|
||||||
|
|
||||||
|
for _, signalConfig := range s.SignalConfigList {
|
||||||
|
var sigAny any
|
||||||
|
switch {
|
||||||
|
case signalConfig.OrderBookBestPriceSignal != nil:
|
||||||
|
sig := signalConfig.OrderBookBestPriceSignal
|
||||||
|
sig.book = s.book
|
||||||
|
sigAny = sig
|
||||||
|
|
||||||
|
case signalConfig.BollingerBandTrendSignal != nil:
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if sigAny != nil {
|
||||||
|
if binder, ok := sigAny.(SessionBinder); ok {
|
||||||
|
binder.Bind(ctx, s.sourceSession, s.Symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.activeMakerOrders = bbgo.NewActiveOrderBook(s.Symbol)
|
s.activeMakerOrders = bbgo.NewActiveOrderBook(s.Symbol)
|
||||||
s.activeMakerOrders.BindStream(s.makerSession.UserDataStream)
|
s.activeMakerOrders.BindStream(s.makerSession.UserDataStream)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user