package riskcontrol import ( "time" log "github.com/sirupsen/logrus" "github.com/c9s/bbgo/pkg/fixedpoint" indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/types" ) type CircuitBreakRiskControl struct { // Since price could be fluctuated large, // use an EWMA to smooth it in running time price *indicatorv2.EWMAStream position *types.Position profitStats *types.ProfitStats lossThreshold fixedpoint.Value haltedDuration time.Duration haltedAt time.Time } func NewCircuitBreakRiskControl( position *types.Position, price *indicatorv2.EWMAStream, lossThreshold fixedpoint.Value, profitStats *types.ProfitStats, haltedDuration time.Duration, ) *CircuitBreakRiskControl { return &CircuitBreakRiskControl{ price: price, position: position, profitStats: profitStats, lossThreshold: lossThreshold, haltedDuration: haltedDuration, } } func (c *CircuitBreakRiskControl) IsOverHaltedDuration() bool { return time.Since(c.haltedAt) >= c.haltedDuration } // IsHalted returns whether we reached the circuit break condition set for this day? func (c *CircuitBreakRiskControl) IsHalted(t time.Time) bool { if c.profitStats.IsOver24Hours() { c.profitStats.ResetToday(t) } // if we are not over the halted duration, we don't need to check the condition if !c.IsOverHaltedDuration() { return false } var unrealized = c.position.UnrealizedProfit(fixedpoint.NewFromFloat(c.price.Last(0))) log.Infof("[CircuitBreakRiskControl] realized PnL = %f, unrealized PnL = %f\n", c.profitStats.TodayPnL.Float64(), unrealized.Float64()) isHalted := unrealized.Add(c.profitStats.TodayPnL).Compare(c.lossThreshold) <= 0 if isHalted { c.haltedAt = t } return isHalted }