mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 08:45:16 +00:00
strategy: use scale for dynamic spread
This commit is contained in:
parent
7d3181f3fd
commit
b41cef4bd7
|
@ -58,15 +58,29 @@ exchangeStrategies:
|
|||
|
||||
# EXPERIMENTAL
|
||||
# Dynamic spread is an experimental feature. Use at your own risk!
|
||||
# DynamicSpreadWindow enables the automatic adjustment to bid and ask spread.
|
||||
#
|
||||
# dynamicSpreadWindow enables the automatic adjustment to bid and ask spread.
|
||||
# When DynamicSpreadWindow is set and larger than 0, the spreads are calculated based on the SMA of amplitude of
|
||||
# previous [DynamicSpreadWindow] K-lines. Max and min spreads limit the maximum and minimum of spreads. Set them
|
||||
# to 0 to disable the limitation.
|
||||
# previous [DynamicSpreadWindow] K-lines.
|
||||
# dynamicSpreadWindow: 1
|
||||
# minAskSpread: 0.05%
|
||||
# minBidSpread: 0.05%
|
||||
# maxAskSpread: 1
|
||||
# maxBidSpread: 1
|
||||
# dynamicAskSpreadScale:
|
||||
# byPercentage:
|
||||
# # exp means we want to use exponential scale, you can replace "exp" with "linear" for linear scale
|
||||
# exp:
|
||||
# # amplitude from low to high
|
||||
# domain: [ 0.001, 0.01 ]
|
||||
# # when amplitude is low, minimum ask is spread 0.05%
|
||||
# # when amplitude is high, maximum ask is spread 0.2%
|
||||
# range: [ 0.0005, 0.002 ]
|
||||
# dynamicBidSpreadScale:
|
||||
# byPercentage:
|
||||
# # exp means we want to use exponential scale, you can replace "exp" with "linear" for linear scale
|
||||
# exp:
|
||||
# # amplitude from low to high
|
||||
# domain: [ 0.001, 0.01 ]
|
||||
# # when amplitude is low, minimum bid is spread 0.05%
|
||||
# # when amplitude is high, maximum bid is spread 0.2%
|
||||
# range: [ 0.0005, 0.002 ]
|
||||
|
||||
# maxExposurePosition is the maximum position you can hold
|
||||
# +10 means you can hold 10 ETH long position by maximum
|
||||
|
|
|
@ -81,11 +81,13 @@ type Strategy struct {
|
|||
// DynamicSpreadWindow enables the automatic adjustment to bid and ask spread.
|
||||
// When DynamicSpreadWindow is set and is larger than 0, the spreads are calculated based on the SMA of amplitude of
|
||||
// [DynamicSpreadWindow] K-lines
|
||||
DynamicSpreadWindow int `json:"dynamicSpreadWindow,omitempty"`
|
||||
MinAskSpread fixedpoint.Value `json:"minAskSpread"`
|
||||
MinBidSpread fixedpoint.Value `json:"minBidSpread"`
|
||||
MaxAskSpread fixedpoint.Value `json:"maxAskSpread"`
|
||||
MaxBidSpread fixedpoint.Value `json:"maxBidSpread"`
|
||||
DynamicSpreadWindow int `json:"dynamicSpreadWindow,omitempty"`
|
||||
|
||||
// DynamicAskSpreadScale is used to define the ask spread range with the given percentage.
|
||||
DynamicAskSpreadScale *bbgo.PercentageScale `json:"dynamicAskSpreadScale"`
|
||||
|
||||
// DynamicBidSpreadScale is used to define the bid spread range with the given percentage.
|
||||
DynamicBidSpreadScale *bbgo.PercentageScale `json:"dynamicBidSpreadScale"`
|
||||
|
||||
DynamicAskSpread *indicator.SMA
|
||||
DynamicBidSpread *indicator.SMA
|
||||
|
@ -288,6 +290,53 @@ func (s *Strategy) getCurrentAllowedExposurePosition(bandPercentage float64) (fi
|
|||
return s.MaxExposurePosition, nil
|
||||
}
|
||||
|
||||
// Update dynamic spreads
|
||||
func (s *Strategy) updateDynamicSpread(kline types.KLine) {
|
||||
if s.DynamicSpreadWindow > 0 {
|
||||
high := kline.GetHigh().Float64()
|
||||
open := kline.GetOpen().Float64()
|
||||
low := kline.GetLow().Float64()
|
||||
|
||||
if kline.Direction() == types.DirectionUp {
|
||||
s.DynamicAskSpread.Update((high - open) / open)
|
||||
}
|
||||
if kline.Direction() == types.DirectionDown {
|
||||
s.DynamicBidSpread.Update((open - low) / open)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get current dynamic ask spread
|
||||
func (s *Strategy) getDynamicAskSpread() (dynamicAskSpread float64, err error) {
|
||||
if s.DynamicAskSpreadScale != nil && s.DynamicSpreadWindow > 0 && s.DynamicAskSpread.Length() >= s.DynamicSpreadWindow {
|
||||
dynamicAskSpread, err = s.DynamicAskSpreadScale.Scale(s.DynamicAskSpread.Last())
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("can not calculate dynamicAskSpread")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return dynamicAskSpread, nil
|
||||
}
|
||||
log.Infof("dynamicAskSpread: %v", dynamicAskSpread)
|
||||
|
||||
return 0, errors.New("incomplete dynamic spread settings or not enough data yet")
|
||||
}
|
||||
|
||||
// Get current dynamic bid spread
|
||||
func (s *Strategy) getDynamicBidSpread() (dynamicBidSpread float64, err error) {
|
||||
if s.DynamicBidSpreadScale != nil && s.DynamicSpreadWindow > 0 && s.DynamicBidSpread.Length() >= s.DynamicSpreadWindow {
|
||||
dynamicBidSpread, err = s.DynamicBidSpreadScale.Scale(s.DynamicBidSpread.Last())
|
||||
if err != nil {
|
||||
log.WithError(err).Errorf("can not calculate dynamicBidSpread")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return dynamicBidSpread, nil
|
||||
}
|
||||
|
||||
return 0, errors.New("incomplete dynamic spread settings or not enough data yet")
|
||||
}
|
||||
|
||||
func (s *Strategy) placeOrders(ctx context.Context, orderExecutor bbgo.OrderExecutor, midPrice fixedpoint.Value, kline *types.KLine) {
|
||||
bidSpread := s.Spread
|
||||
if s.BidSpread.Sign() > 0 {
|
||||
|
@ -676,35 +725,15 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
}
|
||||
|
||||
// Update spreads
|
||||
high := kline.GetHigh().Float64()
|
||||
open := kline.GetOpen().Float64()
|
||||
low := kline.GetLow().Float64()
|
||||
if s.DynamicSpreadWindow > 0 && kline.Direction() == types.DirectionUp {
|
||||
s.DynamicAskSpread.Update((high - open) / open)
|
||||
}
|
||||
if s.DynamicSpreadWindow > 0 && kline.Direction() == types.DirectionDown {
|
||||
s.DynamicBidSpread.Update((open - low) / open)
|
||||
}
|
||||
if s.DynamicSpreadWindow > 0 && s.DynamicBidSpread.Length() >= s.DynamicSpreadWindow {
|
||||
dynamicBidSpread := fixedpoint.NewFromFloat(s.DynamicBidSpread.Last())
|
||||
if !s.MaxBidSpread.IsZero() && dynamicBidSpread.Compare(s.MaxBidSpread) > 0 {
|
||||
s.BidSpread = s.MaxBidSpread
|
||||
} else if !s.MinBidSpread.IsZero() && dynamicBidSpread.Compare(s.MinBidSpread) < 0 {
|
||||
s.BidSpread = s.MinBidSpread
|
||||
} else {
|
||||
s.BidSpread = dynamicBidSpread
|
||||
}
|
||||
s.updateDynamicSpread(kline)
|
||||
dynamicBidSpread, err := s.getDynamicBidSpread()
|
||||
if err == nil && dynamicBidSpread > 0 {
|
||||
s.BidSpread = fixedpoint.NewFromFloat(dynamicBidSpread)
|
||||
log.Infof("new bid spread: %v", s.BidSpread.Percentage())
|
||||
}
|
||||
if s.DynamicSpreadWindow > 0 && s.DynamicAskSpread.Length() >= s.DynamicSpreadWindow {
|
||||
dynamicAskSpread := fixedpoint.NewFromFloat(s.DynamicAskSpread.Last())
|
||||
if !s.MaxAskSpread.IsZero() && dynamicAskSpread.Compare(s.MaxAskSpread) > 0 {
|
||||
s.AskSpread = s.MaxAskSpread
|
||||
} else if !s.MinAskSpread.IsZero() && dynamicAskSpread.Compare(s.MinAskSpread) < 0 {
|
||||
s.AskSpread = s.MinAskSpread
|
||||
} else {
|
||||
s.AskSpread = dynamicAskSpread
|
||||
}
|
||||
dynamicAskSpread, err := s.getDynamicAskSpread()
|
||||
if err == nil && dynamicAskSpread > 0 {
|
||||
s.AskSpread = fixedpoint.NewFromFloat(dynamicAskSpread)
|
||||
log.Infof("new ask spread: %v", s.AskSpread.Percentage())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user