strategy/linregmaker: works w/o dynamic qty

This commit is contained in:
Andy Cheng 2022-11-23 16:53:08 +08:00
parent e776c9e5ea
commit cc124d4264
4 changed files with 27 additions and 62 deletions

View File

@ -110,7 +110,7 @@ exchangeStrategies:
domain: [ -1, 1 ] domain: [ -1, 1 ]
# when in down band, holds 1.0 by maximum # when in down band, holds 1.0 by maximum
# when in up band, holds 0.05 by maximum # when in up band, holds 0.05 by maximum
range: [ 10.0, 1.0 ] range: [ 1.0, 1.0 ]
# quantity is the base order quantity for your buy/sell order. # quantity is the base order quantity for your buy/sell order.
quantity: 0.1 quantity: 0.1

View File

@ -1,9 +0,0 @@
package dynamicmetric
import "github.com/c9s/bbgo/pkg/types"
// BollingerSetting is for Bollinger Band settings
type BollingerSetting struct {
types.IntervalWindow
BandWidth float64 `json:"bandWidth"`
}

View File

@ -16,18 +16,6 @@ type DynamicSpread struct {
// WeightedBollWidthRatioSpread calculates spreads based on two Bollinger Bands // WeightedBollWidthRatioSpread calculates spreads based on two Bollinger Bands
WeightedBollWidthRatioSpread *DynamicSpreadBollWidthRatio `json:"weightedBollWidth"` WeightedBollWidthRatioSpread *DynamicSpreadBollWidthRatio `json:"weightedBollWidth"`
// deprecated
Enabled *bool `json:"enabled"`
// deprecated
types.IntervalWindow
// deprecated. AskSpreadScale is used to define the ask spread range with the given percentage.
AskSpreadScale *bbgo.PercentageScale `json:"askSpreadScale"`
// deprecated. BidSpreadScale is used to define the bid spread range with the given percentage.
BidSpreadScale *bbgo.PercentageScale `json:"bidSpreadScale"`
} }
// Initialize dynamic spread // Initialize dynamic spread
@ -37,14 +25,6 @@ func (ds *DynamicSpread) Initialize(symbol string, session *bbgo.ExchangeSession
ds.AmpSpread.initialize(symbol, session) ds.AmpSpread.initialize(symbol, session)
case ds.WeightedBollWidthRatioSpread != nil: case ds.WeightedBollWidthRatioSpread != nil:
ds.WeightedBollWidthRatioSpread.initialize(symbol, session) ds.WeightedBollWidthRatioSpread.initialize(symbol, session)
case ds.Enabled != nil && *ds.Enabled:
// backward compatibility
ds.AmpSpread = &DynamicSpreadAmp{
IntervalWindow: ds.IntervalWindow,
AskSpreadScale: ds.AskSpreadScale,
BidSpreadScale: ds.BidSpreadScale,
}
ds.AmpSpread.initialize(symbol, session)
} }
} }
@ -171,8 +151,8 @@ type DynamicSpreadBollWidthRatio struct {
// A positive number. The greater factor, the sharper weighting function. Default set to 1.0 . // A positive number. The greater factor, the sharper weighting function. Default set to 1.0 .
Sensitivity float64 `json:"sensitivity"` Sensitivity float64 `json:"sensitivity"`
DefaultBollinger *BollingerSetting `json:"defaultBollinger"` DefaultBollinger types.IntervalWindowBandWidth `json:"defaultBollinger"`
NeutralBollinger *BollingerSetting `json:"neutralBollinger"` NeutralBollinger types.IntervalWindowBandWidth `json:"neutralBollinger"`
neutralBoll *indicator.BOLL neutralBoll *indicator.BOLL
defaultBoll *indicator.BOLL defaultBoll *indicator.BOLL
@ -232,7 +212,7 @@ func (ds *DynamicSpreadBollWidthRatio) getWeightedBBWidthRatio(positiveSigmoid b
// - To bid spread, the weighting density function d_weight(x) is sigmoid((default_BB_mid - x) / (w / alpha)) // - To bid spread, the weighting density function d_weight(x) is sigmoid((default_BB_mid - x) / (w / alpha))
// - The higher sensitivity factor alpha, the sharper weighting function. // - The higher sensitivity factor alpha, the sharper weighting function.
// //
// Then calculate the weighted band width ratio by taking integral of d_weight(x) from neutral_BB_lower to neutral_BB_upper: // Then calculate the weighted bandwidth ratio by taking integral of d_weight(x) from neutral_BB_lower to neutral_BB_upper:
// infinite integral of ask spread sigmoid weighting density function F(x) = (w / alpha) * ln(exp(x / (w / alpha)) + exp(default_BB_mid / (w / alpha))) // infinite integral of ask spread sigmoid weighting density function F(x) = (w / alpha) * ln(exp(x / (w / alpha)) + exp(default_BB_mid / (w / alpha)))
// infinite integral of bid spread sigmoid weighting density function F(x) = x - (w / alpha) * ln(exp(x / (w / alpha)) + exp(default_BB_mid / (w / alpha))) // infinite integral of bid spread sigmoid weighting density function F(x) = x - (w / alpha) * ln(exp(x / (w / alpha)) + exp(default_BB_mid / (w / alpha)))
// Note that we've rescaled the sigmoid function to fit default BB, // Note that we've rescaled the sigmoid function to fit default BB,

View File

@ -25,17 +25,12 @@ var two = fixedpoint.NewFromInt(2)
var log = logrus.WithField("strategy", ID) var log = logrus.WithField("strategy", ID)
//TODO: Logic for backtest //TODO: Logic for backtest
// TODO: Dynamic exposure should work on both side
func init() { func init() {
bbgo.RegisterStrategy(ID, &Strategy{}) bbgo.RegisterStrategy(ID, &Strategy{})
} }
// TODO: Remove BollingerSetting and bollsetting.go
type BollingerSetting struct {
types.IntervalWindow
BandWidth float64 `json:"bandWidth"`
}
type Strategy struct { type Strategy struct {
Environment *bbgo.Environment Environment *bbgo.Environment
StandardIndicatorSet *bbgo.StandardIndicatorSet StandardIndicatorSet *bbgo.StandardIndicatorSet
@ -77,7 +72,10 @@ type Strategy struct {
// NeutralBollinger is the smaller range of the bollinger band // NeutralBollinger is the smaller range of the bollinger band
// If price is in this band, it usually means the price is oscillating. // 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 // If price goes out of this band, we tend to not place sell orders or buy orders
NeutralBollinger *BollingerSetting `json:"neutralBollinger"` NeutralBollinger types.IntervalWindowBandWidth `json:"neutralBollinger"`
// neutralBoll is the neutral price section for TradeInBand
neutralBoll *indicator.BOLL
// TradeInBand // TradeInBand
// When this is on, places orders only when the current price is in the bollinger band. // When this is on, places orders only when the current price is in the bollinger band.
@ -113,7 +111,7 @@ type Strategy struct {
DynamicExposure dynamicmetric.DynamicExposure `json:"dynamicExposure"` DynamicExposure dynamicmetric.DynamicExposure `json:"dynamicExposure"`
bbgo.QuantityOrAmount bbgo.QuantityOrAmount
// TODO: Should work w/o dynamic qty
// DynamicQuantityIncrease calculates the increase position order quantity dynamically // DynamicQuantityIncrease calculates the increase position order quantity dynamically
DynamicQuantityIncrease dynamicmetric.DynamicQuantitySet `json:"dynamicQuantityIncrease"` DynamicQuantityIncrease dynamicmetric.DynamicQuantitySet `json:"dynamicQuantityIncrease"`
@ -134,9 +132,6 @@ type Strategy struct {
groupID uint32 groupID uint32
// neutralBoll is the neutral price section
neutralBoll *indicator.BOLL
// StrategyController // StrategyController
bbgo.StrategyController bbgo.StrategyController
} }
@ -176,7 +171,7 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
} }
// Subscribe for BBs // Subscribe for BBs
if s.NeutralBollinger != nil && s.NeutralBollinger.Interval != "" { if s.NeutralBollinger.Interval != "" {
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{ session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{
Interval: s.NeutralBollinger.Interval, Interval: s.NeutralBollinger.Interval,
}) })
@ -276,39 +271,38 @@ func (s *Strategy) getOrderPrices(midPrice fixedpoint.Value) (askPrice fixedpoin
func (s *Strategy) getOrderQuantities(askPrice fixedpoint.Value, bidPrice fixedpoint.Value) (sellQuantity fixedpoint.Value, buyQuantity fixedpoint.Value) { func (s *Strategy) getOrderQuantities(askPrice fixedpoint.Value, bidPrice fixedpoint.Value) (sellQuantity fixedpoint.Value, buyQuantity fixedpoint.Value) {
// TODO: spot, margin, and futures // TODO: spot, margin, and futures
// Default
sellQuantity = s.QuantityOrAmount.CalculateQuantity(askPrice)
buyQuantity = s.QuantityOrAmount.CalculateQuantity(bidPrice)
// Dynamic qty // Dynamic qty
switch { switch {
case s.mainTrendCurrent == types.DirectionUp: case s.mainTrendCurrent == types.DirectionUp:
var err error
if len(s.DynamicQuantityIncrease) > 0 { if len(s.DynamicQuantityIncrease) > 0 {
buyQuantity, err = s.DynamicQuantityIncrease.GetQuantity() qty, err := s.DynamicQuantityIncrease.GetQuantity()
if err != nil { if err == nil {
buyQuantity = s.QuantityOrAmount.CalculateQuantity(bidPrice) buyQuantity = qty
} }
} }
if len(s.DynamicQuantityDecrease) > 0 { if len(s.DynamicQuantityDecrease) > 0 {
sellQuantity, err = s.DynamicQuantityDecrease.GetQuantity() qty, err := s.DynamicQuantityDecrease.GetQuantity()
if err != nil { if err == nil {
sellQuantity = s.QuantityOrAmount.CalculateQuantity(askPrice) sellQuantity = qty
} }
} }
case s.mainTrendCurrent == types.DirectionDown: case s.mainTrendCurrent == types.DirectionDown:
var err error
if len(s.DynamicQuantityIncrease) > 0 { if len(s.DynamicQuantityIncrease) > 0 {
sellQuantity, err = s.DynamicQuantityIncrease.GetQuantity() qty, err := s.DynamicQuantityIncrease.GetQuantity()
if err != nil { if err == nil {
sellQuantity = s.QuantityOrAmount.CalculateQuantity(bidPrice) sellQuantity = qty
} }
} }
if len(s.DynamicQuantityDecrease) > 0 { if len(s.DynamicQuantityDecrease) > 0 {
buyQuantity, err = s.DynamicQuantityDecrease.GetQuantity() qty, err := s.DynamicQuantityDecrease.GetQuantity()
if err != nil { if err == nil {
buyQuantity = s.QuantityOrAmount.CalculateQuantity(askPrice) buyQuantity = qty
} }
} }
default:
sellQuantity = s.QuantityOrAmount.CalculateQuantity(askPrice)
buyQuantity = s.QuantityOrAmount.CalculateQuantity(bidPrice)
} }
// Faster position decrease // Faster position decrease