bbgo/pkg/risk/riskcontrol/circuit_break.go

68 lines
1.7 KiB
Go

package riskcontrol
import (
"time"
log "github.com/sirupsen/logrus"
"git.qtrade.icu/lychiyu/bbgo/pkg/fixedpoint"
indicatorv2 "git.qtrade.icu/lychiyu/bbgo/pkg/indicator/v2"
"git.qtrade.icu/lychiyu/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
}