Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: [circuit breaker] separate metrics label by symbol and add panic handler #1816

Merged
merged 2 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 40 additions & 13 deletions pkg/risk/circuitbreaker/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,53 +13,55 @@ import (
log "github.com/sirupsen/logrus"
)

var metricsLabels = []string{"strategy", "strategyInstance", "symbol"}

var consecutiveTotalLossMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_circuit_breaker_consecutive_total_loss",
Help: "",
}, []string{"strategy", "strategyInstance"})
}, metricsLabels)

var consecutiveLossTimesCounterMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_circuit_breaker_consecutive_loss_times",
Help: "",
}, []string{"strategy", "strategyInstance"})
}, metricsLabels)

var haltCounterMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_circuit_breaker_halt_counter",
Help: "",
}, []string{"strategy", "strategyInstance"})
}, metricsLabels)

var haltMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_circuit_breaker_halt",
Help: "",
}, []string{"strategy", "strategyInstance"})
}, metricsLabels)

var totalProfitMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_circuit_breaker_total_profit",
Help: "",
}, []string{"strategy", "strategyInstance"})
}, metricsLabels)

var profitWinCounterMetrics = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "bbgo_circuit_breaker_profit_win_counter",
Help: "profit winning counter",
}, []string{"strategy", "strategyInstance"})
}, metricsLabels)

var profitLossCounterMetrics = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "bbgo_circuit_breaker_profit_loss_counter",
Help: "profit los counter",
}, []string{"strategy", "strategyInstance"})
Help: "profit loss counter",
}, metricsLabels)

var winningRatioMetrics = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "bbgo_circuit_breaker_winning_ratio",
Help: "winning ratio",
}, []string{"strategy", "strategyInstance"})
}, metricsLabels)

func init() {
prometheus.MustRegister(
Expand All @@ -74,6 +76,7 @@ func init() {
)
}

//go:generate callbackgen -type BasicCircuitBreaker
type BasicCircuitBreaker struct {
Enabled bool `json:"enabled"`

Expand All @@ -94,7 +97,9 @@ type BasicCircuitBreaker struct {

HaltDuration types.Duration `json:"haltDuration"`

strategyID, strategyInstance string
strategyID, strategyInstance, symbol string

panicCallbacks []func()

haltCounter int
haltReason string
Expand All @@ -117,7 +122,7 @@ type BasicCircuitBreaker struct {
metricsLabels prometheus.Labels
}

func NewBasicCircuitBreaker(strategyID, strategyInstance string) *BasicCircuitBreaker {
func NewBasicCircuitBreaker(strategyID, strategyInstance, symbol string) *BasicCircuitBreaker {
b := &BasicCircuitBreaker{
Enabled: true,
MaximumConsecutiveLossTimes: 8,
Expand All @@ -127,19 +132,39 @@ func NewBasicCircuitBreaker(strategyID, strategyInstance string) *BasicCircuitBr
HaltDuration: types.Duration(1 * time.Hour),
strategyID: strategyID,
strategyInstance: strategyInstance,
metricsLabels: prometheus.Labels{"strategy": strategyID, "strategyInstance": strategyInstance},
symbol: symbol,
metricsLabels: prometheus.Labels{
"strategy": strategyID,
"strategyInstance": strategyInstance,
"symbol": symbol,
},
}

b.updateMetrics()
return b
}

func (b *BasicCircuitBreaker) SetMetricsInfo(strategyID, strategyInstance, symbol string) {
b.strategyID = strategyID
b.strategyInstance = strategyInstance
b.symbol = symbol
b.metricsLabels = prometheus.Labels{
"strategy": b.strategyID,
"strategyInstance": b.strategyInstance,
"symbol": b.symbol,
}
}

func (b *BasicCircuitBreaker) getMetricsLabels() prometheus.Labels {
if b.metricsLabels != nil {
return b.metricsLabels
}

return prometheus.Labels{"strategy": b.strategyID, "strategyInstance": b.strategyInstance}
return prometheus.Labels{
"strategy": b.strategyID,
"strategyInstance": b.strategyInstance,
"symbol": b.symbol,
}
}

func (b *BasicCircuitBreaker) updateMetrics() {
Expand Down Expand Up @@ -263,6 +288,8 @@ func (b *BasicCircuitBreaker) halt(now time.Time, reason string) {
defer b.updateMetrics()

if b.MaximumHaltTimesExceededPanic && b.haltCounter > b.MaximumHaltTimes {
b.EmitPanic()

panic(fmt.Errorf("total %d halt times > maximumHaltTimesExceededPanic %d", b.haltCounter, b.MaximumHaltTimes))
}
}
15 changes: 15 additions & 0 deletions pkg/risk/circuitbreaker/basiccircuitbreaker_callbacks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pkg/strategy/common/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ func (s *Strategy) Initialize(

if !s.CircuitBreakLossThreshold.IsZero() {
log.Infof("circuitBreakLossThreshold is configured, setting up CircuitBreakRiskControl...")
s.circuitBreakRiskControl = circuitbreaker.NewBasicCircuitBreaker(strategyID, instanceID)
s.circuitBreakRiskControl = circuitbreaker.NewBasicCircuitBreaker(strategyID, instanceID, market.Symbol)

s.OrderExecutor.TradeCollector().OnProfit(func(trade types.Trade, profit *types.Profit) {
if profit != nil && s.circuitBreakRiskControl != nil {
s.circuitBreakRiskControl.RecordProfit(profit.Profit, trade.Time.Time())
Expand Down
4 changes: 3 additions & 1 deletion pkg/strategy/xmaker/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,9 @@ func (s *Strategy) Defaults() error {
}

if s.CircuitBreaker == nil {
s.CircuitBreaker = circuitbreaker.NewBasicCircuitBreaker(ID, s.InstanceID())
s.CircuitBreaker = circuitbreaker.NewBasicCircuitBreaker(ID, s.InstanceID(), s.Symbol)
} else {
s.CircuitBreaker.SetMetricsInfo(ID, s.InstanceID(), s.Symbol)
}

// circuitBreakerAlertLimiter is for CircuitBreaker alerts
Expand Down
Loading