mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
strategy: DynamicSpreadSettings struct to make it more clean
This commit is contained in:
parent
f452f8eb94
commit
98b794f265
|
@ -49,6 +49,81 @@ type BollingerSetting struct {
|
||||||
BandWidth float64 `json:"bandWidth"`
|
BandWidth float64 `json:"bandWidth"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DynamicSpreadSettings struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
|
||||||
|
// Window is the window of the SMAs of spreads
|
||||||
|
Window int `json:"window"`
|
||||||
|
|
||||||
|
// AskSpreadScale is used to define the ask spread range with the given percentage.
|
||||||
|
AskSpreadScale *bbgo.PercentageScale `json:"askSpreadScale"`
|
||||||
|
|
||||||
|
// BidSpreadScale is used to define the bid spread range with the given percentage.
|
||||||
|
BidSpreadScale *bbgo.PercentageScale `json:"bidSpreadScale"`
|
||||||
|
|
||||||
|
DynamicAskSpread *indicator.SMA
|
||||||
|
DynamicBidSpread *indicator.SMA
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update dynamic spreads
|
||||||
|
func (ds *DynamicSpreadSettings) Update(kline types.KLine) {
|
||||||
|
if !ds.Enabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ampl := (kline.GetHigh().Float64() - kline.GetLow().Float64()) / kline.GetOpen().Float64()
|
||||||
|
|
||||||
|
switch kline.Direction() {
|
||||||
|
case types.DirectionUp:
|
||||||
|
ds.DynamicAskSpread.Update(ampl)
|
||||||
|
ds.DynamicBidSpread.Update(0)
|
||||||
|
case types.DirectionDown:
|
||||||
|
ds.DynamicBidSpread.Update(ampl)
|
||||||
|
ds.DynamicAskSpread.Update(0)
|
||||||
|
default:
|
||||||
|
ds.DynamicAskSpread.Update(0)
|
||||||
|
ds.DynamicBidSpread.Update(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAskSpread returns current ask spread
|
||||||
|
func (ds *DynamicSpreadSettings) GetAskSpread() (askSpread float64, err error) {
|
||||||
|
if !ds.Enabled {
|
||||||
|
return 0, errors.New("dynamic spread is not enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ds.AskSpreadScale != nil && ds.DynamicAskSpread.Length() >= ds.Window {
|
||||||
|
askSpread, err = ds.AskSpreadScale.Scale(ds.DynamicAskSpread.Last())
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Errorf("can not calculate dynamicAskSpread")
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return askSpread, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, errors.New("incomplete dynamic spread settings or not enough data yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBidSpread returns current dynamic bid spread
|
||||||
|
func (ds *DynamicSpreadSettings) GetBidSpread() (bidSpread float64, err error) {
|
||||||
|
if !ds.Enabled {
|
||||||
|
return 0, errors.New("dynamic spread is not enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ds.BidSpreadScale != nil && ds.DynamicBidSpread.Length() >= ds.Window {
|
||||||
|
bidSpread, err = ds.BidSpreadScale.Scale(ds.DynamicBidSpread.Last())
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Errorf("can not calculate dynamicBidSpread")
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bidSpread, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, errors.New("incomplete dynamic spread settings or not enough data yet")
|
||||||
|
}
|
||||||
|
|
||||||
type Strategy struct {
|
type Strategy struct {
|
||||||
*bbgo.Graceful
|
*bbgo.Graceful
|
||||||
*bbgo.Notifiability
|
*bbgo.Notifiability
|
||||||
|
@ -78,19 +153,8 @@ type Strategy struct {
|
||||||
// AskSpread overrides the spread setting, this spread will be used for the sell order
|
// AskSpread overrides the spread setting, this spread will be used for the sell order
|
||||||
AskSpread fixedpoint.Value `json:"askSpread,omitempty"`
|
AskSpread fixedpoint.Value `json:"askSpread,omitempty"`
|
||||||
|
|
||||||
// DynamicSpreadWindow enables the automatic adjustment to bid and ask spread.
|
// DynamicSpread 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
|
DynamicSpread DynamicSpreadSettings `json:"dynamicSpread,omitempty"`
|
||||||
// [DynamicSpreadWindow] K-lines
|
|
||||||
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
|
|
||||||
|
|
||||||
// MinProfitSpread is the minimal order price spread from the current average cost.
|
// MinProfitSpread is the minimal order price spread from the current average cost.
|
||||||
// For long position, you will only place sell order above the price (= average cost * (1 + minProfitSpread))
|
// For long position, you will only place sell order above the price (= average cost * (1 + minProfitSpread))
|
||||||
|
@ -290,75 +354,6 @@ func (s *Strategy) getCurrentAllowedExposurePosition(bandPercentage float64) (fi
|
||||||
return s.MaxExposurePosition, nil
|
return s.MaxExposurePosition, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDynamicSpreadEnabled checks dynamic spread functionality enabled or not
|
|
||||||
func (s *Strategy) IsDynamicSpreadEnabled() bool {
|
|
||||||
if s.DynamicSpreadWindow > 0 && s.DynamicAskSpreadScale != nil && s.DynamicBidSpreadScale != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update dynamic spreads
|
|
||||||
func (s *Strategy) updateDynamicSpread(kline types.KLine) {
|
|
||||||
if !s.IsDynamicSpreadEnabled() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ampl := (kline.GetHigh().Float64() - kline.GetLow().Float64()) / kline.GetOpen().Float64()
|
|
||||||
|
|
||||||
switch kline.Direction() {
|
|
||||||
case types.DirectionUp:
|
|
||||||
s.DynamicAskSpread.Update(ampl)
|
|
||||||
s.DynamicBidSpread.Update(0)
|
|
||||||
case types.DirectionDown:
|
|
||||||
s.DynamicBidSpread.Update(ampl)
|
|
||||||
s.DynamicAskSpread.Update(0)
|
|
||||||
default:
|
|
||||||
s.DynamicAskSpread.Update(0)
|
|
||||||
s.DynamicBidSpread.Update(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current dynamic ask spread
|
|
||||||
func (s *Strategy) getDynamicAskSpread() (dynamicAskSpread float64, err error) {
|
|
||||||
if !s.IsDynamicSpreadEnabled() {
|
|
||||||
return 0, errors.New("dynamic spread is not enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.DynamicAskSpreadScale != nil && 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.IsDynamicSpreadEnabled() {
|
|
||||||
return 0, errors.New("dynamic spread is not enabled")
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.DynamicBidSpreadScale != nil && 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) {
|
func (s *Strategy) placeOrders(ctx context.Context, orderExecutor bbgo.OrderExecutor, midPrice fixedpoint.Value, kline *types.KLine) {
|
||||||
bidSpread := s.Spread
|
bidSpread := s.Spread
|
||||||
if s.BidSpread.Sign() > 0 {
|
if s.BidSpread.Sign() > 0 {
|
||||||
|
@ -591,9 +586,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
s.Status = types.StrategyStatusRunning
|
s.Status = types.StrategyStatusRunning
|
||||||
|
|
||||||
// Setup dynamic spread
|
// Setup dynamic spread
|
||||||
if s.IsDynamicSpreadEnabled() {
|
if s.DynamicSpread.Enabled {
|
||||||
s.DynamicBidSpread = &indicator.SMA{IntervalWindow: types.IntervalWindow{s.Interval, s.DynamicSpreadWindow}}
|
s.DynamicSpread.DynamicBidSpread = &indicator.SMA{IntervalWindow: types.IntervalWindow{s.Interval, s.DynamicSpread.Window}}
|
||||||
s.DynamicAskSpread = &indicator.SMA{IntervalWindow: types.IntervalWindow{s.Interval, s.DynamicSpreadWindow}}
|
s.DynamicSpread.DynamicAskSpread = &indicator.SMA{IntervalWindow: types.IntervalWindow{s.Interval, s.DynamicSpread.Window}}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.OnSuspend(func() {
|
s.OnSuspend(func() {
|
||||||
|
@ -747,14 +742,14 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update spreads with dynamic spread
|
// Update spreads with dynamic spread
|
||||||
if s.IsDynamicSpreadEnabled() {
|
if s.DynamicSpread.Enabled {
|
||||||
s.updateDynamicSpread(kline)
|
s.DynamicSpread.Update(kline)
|
||||||
dynamicBidSpread, err := s.getDynamicBidSpread()
|
dynamicBidSpread, err := s.DynamicSpread.GetBidSpread()
|
||||||
if err == nil && dynamicBidSpread > 0 {
|
if err == nil && dynamicBidSpread > 0 {
|
||||||
s.BidSpread = fixedpoint.NewFromFloat(dynamicBidSpread)
|
s.BidSpread = fixedpoint.NewFromFloat(dynamicBidSpread)
|
||||||
log.Infof("new bid spread: %v", s.BidSpread.Percentage())
|
log.Infof("new bid spread: %v", s.BidSpread.Percentage())
|
||||||
}
|
}
|
||||||
dynamicAskSpread, err := s.getDynamicAskSpread()
|
dynamicAskSpread, err := s.DynamicSpread.GetAskSpread()
|
||||||
if err == nil && dynamicAskSpread > 0 {
|
if err == nil && dynamicAskSpread > 0 {
|
||||||
s.AskSpread = fixedpoint.NewFromFloat(dynamicAskSpread)
|
s.AskSpread = fixedpoint.NewFromFloat(dynamicAskSpread)
|
||||||
log.Infof("new ask spread: %v", s.AskSpread.Percentage())
|
log.Infof("new ask spread: %v", s.AskSpread.Percentage())
|
||||||
|
|
Loading…
Reference in New Issue
Block a user