mirror of
https://github.com/c9s/bbgo.git
synced 2024-09-20 08:11:08 +00:00
Merge pull request #1463 from c9s/c9s/bollmaker-ema-crosssignal
This commit is contained in:
commit
c250fec2dc
|
@ -17,7 +17,7 @@ backtest:
|
|||
# see here for more details
|
||||
# https://www.investopedia.com/terms/m/maximum-drawdown-mdd.asp
|
||||
startTime: "2022-05-01"
|
||||
endTime: "2022-08-14"
|
||||
endTime: "2023-11-01"
|
||||
sessions:
|
||||
- binance
|
||||
symbols:
|
||||
|
@ -200,6 +200,15 @@ exchangeStrategies:
|
|||
# buyBelowNeutralSMA: when this set, it will only place buy order when the current price is below the SMA line.
|
||||
buyBelowNeutralSMA: true
|
||||
|
||||
# emaCross is used for turning buy on/off
|
||||
# when short term EMA cross fast term EMA, turn on buy,
|
||||
# otherwise, turn off buy
|
||||
emaCross:
|
||||
enabled: false
|
||||
interval: 1h
|
||||
fastWindow: 3
|
||||
slowWindow: 12
|
||||
|
||||
exits:
|
||||
|
||||
# roiTakeProfit is used to force taking profit by percentage of the position ROI (currently the price change)
|
||||
|
|
|
@ -6,12 +6,11 @@ import (
|
|||
"math"
|
||||
"sync"
|
||||
|
||||
indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
|
@ -46,6 +45,16 @@ type BollingerSetting struct {
|
|||
BandWidth float64 `json:"bandWidth"`
|
||||
}
|
||||
|
||||
type EMACrossSetting struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Interval types.Interval `json:"interval"`
|
||||
FastWindow int `json:"fastWindow"`
|
||||
SlowWindow int `json:"slowWindow"`
|
||||
|
||||
fastEMA, slowEMA *indicatorv2.EWMAStream
|
||||
cross *indicatorv2.CrossStream
|
||||
}
|
||||
|
||||
type Strategy struct {
|
||||
Environment *bbgo.Environment
|
||||
StandardIndicatorSet *bbgo.StandardIndicatorSet
|
||||
|
@ -121,6 +130,9 @@ type Strategy struct {
|
|||
// BuyBelowNeutralSMA if true, the market maker will only place buy order when the current price is below the neutral band SMA.
|
||||
BuyBelowNeutralSMA bool `json:"buyBelowNeutralSMA"`
|
||||
|
||||
// EMACrossSetting is used for defining ema cross signal to turn on/off buy
|
||||
EMACrossSetting *EMACrossSetting `json:"emaCross"`
|
||||
|
||||
// NeutralBollinger is the smaller range of the bollinger band
|
||||
// If price is in this band, it usually means the price is oscillating.
|
||||
// If price goes out of this band, we tend to not place sell orders or buy orders
|
||||
|
@ -150,25 +162,24 @@ type Strategy struct {
|
|||
ShadowProtection bool `json:"shadowProtection"`
|
||||
ShadowProtectionRatio fixedpoint.Value `json:"shadowProtectionRatio"`
|
||||
|
||||
session *bbgo.ExchangeSession
|
||||
book *types.StreamOrderBook
|
||||
|
||||
ExitMethods bbgo.ExitMethodSet `json:"exits"`
|
||||
|
||||
// persistence fields
|
||||
Position *types.Position `json:"position,omitempty" persistence:"position"`
|
||||
ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"`
|
||||
|
||||
session *bbgo.ExchangeSession
|
||||
book *types.StreamOrderBook
|
||||
orderExecutor *bbgo.GeneralOrderExecutor
|
||||
|
||||
groupID uint32
|
||||
|
||||
// defaultBoll is the BOLLINGER indicator we used for predicting the price.
|
||||
defaultBoll *indicatorv2.BOLLStream
|
||||
|
||||
// neutralBoll is the neutral price section
|
||||
neutralBoll *indicatorv2.BOLLStream
|
||||
|
||||
shouldBuy bool
|
||||
|
||||
// StrategyController
|
||||
bbgo.StrategyController
|
||||
}
|
||||
|
@ -202,6 +213,10 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
|||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.TrendEMA.Interval})
|
||||
}
|
||||
|
||||
if s.EMACrossSetting != nil && s.EMACrossSetting.Enabled {
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.EMACrossSetting.Interval})
|
||||
}
|
||||
|
||||
s.ExitMethods.SetAndSubscribe(session, s)
|
||||
}
|
||||
|
||||
|
@ -274,8 +289,8 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k
|
|||
Quantity: sellQuantity,
|
||||
Price: askPrice,
|
||||
Market: s.Market,
|
||||
GroupID: s.groupID,
|
||||
}
|
||||
|
||||
buyOrder := types.SubmitOrder{
|
||||
Symbol: s.Symbol,
|
||||
Side: types.SideTypeBuy,
|
||||
|
@ -283,7 +298,6 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k
|
|||
Quantity: buyQuantity,
|
||||
Price: bidPrice,
|
||||
Market: s.Market,
|
||||
GroupID: s.groupID,
|
||||
}
|
||||
|
||||
var submitOrders []types.SubmitOrder
|
||||
|
@ -435,14 +449,20 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k
|
|||
}
|
||||
}
|
||||
|
||||
if !s.shouldBuy {
|
||||
log.Infof("shouldBuy is turned off, skip placing buy order")
|
||||
canBuy = false
|
||||
}
|
||||
|
||||
if canSell {
|
||||
submitOrders = append(submitOrders, sellOrder)
|
||||
}
|
||||
|
||||
if canBuy {
|
||||
submitOrders = append(submitOrders, buyOrder)
|
||||
}
|
||||
|
||||
// condition for lower the average cost
|
||||
// condition for lowering the average cost
|
||||
/*
|
||||
if midPrice < s.Position.AverageCost.MulFloat64(1.0-s.MinProfitSpread.Float64()) && canBuy {
|
||||
submitOrders = append(submitOrders, buyOrder)
|
||||
|
@ -478,9 +498,26 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
// StrategyController
|
||||
s.Status = types.StrategyStatusRunning
|
||||
|
||||
s.shouldBuy = true
|
||||
s.neutralBoll = session.Indicators(s.Symbol).BOLL(s.NeutralBollinger.IntervalWindow, s.NeutralBollinger.BandWidth)
|
||||
s.defaultBoll = session.Indicators(s.Symbol).BOLL(s.DefaultBollinger.IntervalWindow, s.DefaultBollinger.BandWidth)
|
||||
|
||||
if s.EMACrossSetting != nil && s.EMACrossSetting.Enabled {
|
||||
s.EMACrossSetting.fastEMA = session.Indicators(s.Symbol).EWMA(types.IntervalWindow{Interval: s.Interval, Window: s.EMACrossSetting.FastWindow})
|
||||
s.EMACrossSetting.slowEMA = session.Indicators(s.Symbol).EWMA(types.IntervalWindow{Interval: s.Interval, Window: s.EMACrossSetting.SlowWindow})
|
||||
s.EMACrossSetting.cross = indicatorv2.Cross(s.EMACrossSetting.fastEMA, s.EMACrossSetting.slowEMA)
|
||||
s.EMACrossSetting.cross.OnUpdate(func(v float64) {
|
||||
switch indicatorv2.CrossType(v) {
|
||||
case indicatorv2.CrossOver:
|
||||
s.shouldBuy = true
|
||||
case indicatorv2.CrossUnder:
|
||||
s.shouldBuy = false
|
||||
// TODO: can partially close position when necessary
|
||||
// s.orderExecutor.ClosePosition(ctx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Setup dynamic spread
|
||||
if s.DynamicSpread.IsEnabled() {
|
||||
if s.DynamicSpread.Interval == "" {
|
||||
|
@ -511,7 +548,6 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
|
||||
// calculate group id for orders
|
||||
instanceID := s.InstanceID()
|
||||
s.groupID = util.FNV32(instanceID)
|
||||
|
||||
// If position is nil, we need to allocate a new position for calculation
|
||||
if s.Position == nil {
|
||||
|
@ -563,7 +599,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
})
|
||||
|
||||
session.UserDataStream.OnStart(func() {
|
||||
if s.UseTickerPrice {
|
||||
if !bbgo.IsBackTesting && s.UseTickerPrice {
|
||||
ticker, err := s.session.Exchange.QueryTicker(ctx, s.Symbol)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
Loading…
Reference in New Issue
Block a user