xmaker: add signalTrendSideMargin support

This commit is contained in:
c9s 2024-11-20 16:42:19 +08:00
parent f1f5f1ff3a
commit 6e38210af8
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54

View File

@ -109,6 +109,12 @@ func init() {
bbgo.RegisterStrategy(ID, &Strategy{}) bbgo.RegisterStrategy(ID, &Strategy{})
} }
type SignalMargin struct {
Enabled bool `json:"enabled"`
Scale *bbgo.SlideRule `json:"scale,omitempty"`
Threshold float64 `json:"threshold,omitempty"`
}
type Strategy struct { type Strategy struct {
Environment *bbgo.Environment Environment *bbgo.Environment
@ -128,12 +134,18 @@ type Strategy struct {
EnableSignalMargin bool `json:"enableSignalMargin"` EnableSignalMargin bool `json:"enableSignalMargin"`
SignalConfigList []SignalConfig `json:"signals"` SignalConfigList []SignalConfig `json:"signals"`
SignalReverseSideMarginScale *bbgo.SlideRule `json:"signalReverseSideMarginScale,omitempty"`
SignalTrendSideMarginScale *bbgo.SlideRule `json:"signalTrendSideMarginScale,omitempty"`
SignalReverseSideMargin *SignalMargin `json:"signalReverseSideMargin,omitempty"`
SignalTrendSideMargin *SignalMargin `json:"signalTrendSideMargin,omitempty"`
// Margin is the default margin for the quote
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"`
// MinMargin is the minimum margin protection for signal margin
MinMargin *fixedpoint.Value `json:"minMargin"`
UseDepthPrice bool `json:"useDepthPrice"` UseDepthPrice bool `json:"useDepthPrice"`
DepthQuantity fixedpoint.Value `json:"depthQuantity"` DepthQuantity fixedpoint.Value `json:"depthQuantity"`
SourceDepthLevel types.Depth `json:"sourceDepthLevel"` SourceDepthLevel types.Depth `json:"sourceDepthLevel"`
@ -180,7 +192,7 @@ type Strategy struct {
RecoverTradeScanPeriod types.Duration `json:"recoverTradeScanPeriod"` RecoverTradeScanPeriod types.Duration `json:"recoverTradeScanPeriod"`
MaxQuoteUsageRatio fixedpoint.Value `json:"maxQuoteUsageRatio"` MaxQuoteQuotaRatio fixedpoint.Value `json:"maxQuoteQuotaRatio,omitempty"`
NumLayers int `json:"numLayers"` NumLayers int `json:"numLayers"`
@ -321,8 +333,8 @@ func (s *Strategy) Initialize() error {
"symbol": s.Symbol, "symbol": s.Symbol,
} }
if s.SignalReverseSideMarginScale != nil { if s.SignalReverseSideMargin != nil && s.SignalReverseSideMargin.Scale != nil {
scale, err := s.SignalReverseSideMarginScale.Scale() scale, err := s.SignalReverseSideMargin.Scale.Scale()
if err != nil { if err != nil {
return err return err
} }
@ -332,8 +344,8 @@ func (s *Strategy) Initialize() error {
} }
} }
if s.SignalTrendSideMarginScale != nil { if s.SignalTrendSideMargin != nil && s.SignalTrendSideMargin.Scale != nil {
scale, err := s.SignalTrendSideMarginScale.Scale() scale, err := s.SignalTrendSideMargin.Scale.Scale()
if err != nil { if err != nil {
return err return err
} }
@ -403,32 +415,65 @@ func (s *Strategy) applySignalMargin(ctx context.Context, quote *Quote) error {
return nil return nil
} }
scale, err := s.SignalReverseSideMarginScale.Scale() signalAbs := math.Abs(signal)
var trendSideMarginDiscount, reverseSideMargin float64
var trendSideMarginDiscountFp, reverseSideMarginFp fixedpoint.Value
if s.SignalTrendSideMargin != nil && s.SignalTrendSideMargin.Enabled {
trendSideMarginScale, err := s.SignalTrendSideMargin.Scale.Scale()
if err != nil { if err != nil {
return err return err
} }
margin := scale.Call(math.Abs(signal)) if signalAbs > s.SignalTrendSideMargin.Threshold {
// trendSideMarginDiscount is the discount for the trend side margin
trendSideMarginDiscount = trendSideMarginScale.Call(math.Abs(signal))
trendSideMarginDiscountFp = fixedpoint.NewFromFloat(trendSideMarginDiscount)
s.logger.Infof("signal margin: %f", margin) if signal > 0.0 {
quote.BidMargin = quote.BidMargin.Sub(trendSideMarginDiscountFp)
} else if signal < 0.0 {
quote.AskMargin = quote.AskMargin.Sub(trendSideMarginDiscountFp)
}
}
}
marginFp := fixedpoint.NewFromFloat(margin) if s.SignalReverseSideMargin != nil && s.SignalReverseSideMargin.Enabled {
reverseSideMarginScale, err := s.SignalReverseSideMargin.Scale.Scale()
if err != nil {
return err
}
if signalAbs > s.SignalReverseSideMargin.Threshold {
reverseSideMargin = reverseSideMarginScale.Call(math.Abs(signal))
reverseSideMarginFp = fixedpoint.NewFromFloat(reverseSideMargin)
if signal < 0.0 { if signal < 0.0 {
quote.BidMargin = quote.BidMargin.Add(marginFp) quote.BidMargin = quote.BidMargin.Add(reverseSideMarginFp)
if signal <= -2.0 {
// quote.BidMargin = fixedpoint.Zero
}
s.logger.Infof("adjusted bid margin: %f", quote.BidMargin.Float64())
} else if signal > 0.0 { } else if signal > 0.0 {
quote.AskMargin = quote.AskMargin.Add(marginFp) quote.AskMargin = quote.AskMargin.Add(reverseSideMarginFp)
if signal >= 2.0 { }
// quote.AskMargin = fixedpoint.Zero }
} }
s.logger.Infof("adjusted ask margin: %f", quote.AskMargin.Float64()) s.logger.Infof("signal margin params: signal = %f, reverseSideMargin = %f, trendSideMarginDiscount = %f", signal, reverseSideMargin, trendSideMarginDiscount)
s.logger.Infof("calculated signal margin: signal = %f, askMargin = %s, bidMargin = %s",
signal,
quote.AskMargin,
quote.BidMargin,
)
if s.MinMargin != nil {
quote.AskMargin = fixedpoint.Max(*s.MinMargin, quote.AskMargin)
quote.BidMargin = fixedpoint.Max(*s.MinMargin, quote.BidMargin)
} }
s.logger.Infof("final signal margin: signal = %f, askMargin = %s, bidMargin = %s",
signal,
quote.AskMargin,
quote.BidMargin,
)
return nil return nil
} }
@ -722,8 +767,8 @@ func (s *Strategy) updateQuote(ctx context.Context) error {
if b, ok := makerBalances[s.makerMarket.QuoteCurrency]; ok { if b, ok := makerBalances[s.makerMarket.QuoteCurrency]; ok {
if b.Available.Compare(s.makerMarket.MinNotional) > 0 { if b.Available.Compare(s.makerMarket.MinNotional) > 0 {
if s.MaxQuoteUsageRatio.Sign() > 0 { if s.MaxQuoteQuotaRatio.Sign() > 0 {
quoteAvailable := b.Available.Mul(s.MaxQuoteUsageRatio) quoteAvailable := b.Available.Mul(s.MaxQuoteQuotaRatio)
makerQuota.QuoteAsset.Add(quoteAvailable) makerQuota.QuoteAsset.Add(quoteAvailable)
} else { } else {
// use all quote balances as much as possible // use all quote balances as much as possible
@ -1396,6 +1441,7 @@ func (s *Strategy) Defaults() error {
if s.BollBandMarginFactor.IsZero() { if s.BollBandMarginFactor.IsZero() {
s.BollBandMarginFactor = fixedpoint.One s.BollBandMarginFactor = fixedpoint.One
} }
if s.BollBandMargin.IsZero() { if s.BollBandMargin.IsZero() {
s.BollBandMargin = fixedpoint.NewFromFloat(0.001) s.BollBandMargin = fixedpoint.NewFromFloat(0.001)
} }
@ -1444,8 +1490,8 @@ func (s *Strategy) Defaults() error {
} }
if s.EnableSignalMargin { if s.EnableSignalMargin {
if s.SignalReverseSideMarginScale == nil { if s.SignalReverseSideMargin.Scale == nil {
s.SignalReverseSideMarginScale = &bbgo.SlideRule{ s.SignalReverseSideMargin.Scale = &bbgo.SlideRule{
ExpScale: &bbgo.ExponentialScale{ ExpScale: &bbgo.ExponentialScale{
Domain: [2]float64{0, 2.0}, Domain: [2]float64{0, 2.0},
Range: [2]float64{0.00010, 0.00500}, Range: [2]float64{0.00010, 0.00500},
@ -1454,14 +1500,18 @@ func (s *Strategy) Defaults() error {
} }
} }
if s.SignalTrendSideMarginScale == nil { if s.SignalTrendSideMargin.Scale == nil {
s.SignalTrendSideMarginScale = &bbgo.SlideRule{ s.SignalTrendSideMargin.Scale = &bbgo.SlideRule{
ExpScale: &bbgo.ExponentialScale{ ExpScale: &bbgo.ExponentialScale{
Domain: [2]float64{0, 2.0}, Domain: [2]float64{0, 2.0},
Range: [2]float64{0.00010, 0.00500}, Range: [2]float64{0.00010, 0.00500},
}, },
} }
} }
if s.SignalTrendSideMargin.Threshold == 0.0 {
s.SignalTrendSideMargin.Threshold = 1.0
}
} }
// circuitBreakerAlertLimiter is for CircuitBreaker alerts // circuitBreakerAlertLimiter is for CircuitBreaker alerts
@ -1815,15 +1865,15 @@ func (s *Strategy) CrossRun(
if s.EnableSignalMargin { if s.EnableSignalMargin {
s.logger.Infof("signal margin is enabled") s.logger.Infof("signal margin is enabled")
if s.SignalReverseSideMarginScale == nil { if s.SignalReverseSideMargin == nil || s.SignalReverseSideMargin.Scale == nil {
return errors.New("signalReverseSideMarginScale can not be nil when signal margin is enabled") return errors.New("signalReverseSideMarginScale can not be nil when signal margin is enabled")
} }
if s.SignalTrendSideMarginScale == nil { if s.SignalTrendSideMargin == nil || s.SignalTrendSideMargin.Scale == nil {
return errors.New("signalTrendSideMarginScale can not be nil when signal margin is enabled") return errors.New("signalTrendSideMarginScale can not be nil when signal margin is enabled")
} }
scale, err := s.SignalReverseSideMarginScale.Scale() scale, err := s.SignalReverseSideMargin.Scale.Scale()
if err != nil { if err != nil {
return err return err
} }