93 lines
2.6 KiB
Markdown
93 lines
2.6 KiB
Markdown
|
# Risk Control
|
||
|
------------
|
||
|
|
||
|
### 1. Introduction
|
||
|
|
||
|
Two types of risk controls for strategies is created:
|
||
|
- Position-limit Risk Control (pkg/risk/riskcontrol/position.go)
|
||
|
- Circuit-break Risk Control (pkg/risk/riskcontrol/circuit_break.go)
|
||
|
|
||
|
### 2. Position-Limit Risk Control
|
||
|
|
||
|
Initialization:
|
||
|
|
||
|
```go
|
||
|
s.positionRiskControl = riskcontrol.NewPositionRiskControl(s.PositionHardLimit, s.MaxPositionQuantity, s.orderExecutor.TradeCollector())
|
||
|
|
||
|
s.positionRiskControl.OnReleasePosition(func(quantity fixedpoint.Value, side types.SideType) {
|
||
|
createdOrders, err := s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
||
|
Symbol: s.Symbol,
|
||
|
Market: s.Market,
|
||
|
Side: side,
|
||
|
Type: types.OrderTypeMarket,
|
||
|
Quantity: quantity,
|
||
|
})
|
||
|
|
||
|
if err != nil {
|
||
|
log.WithError(err).Errorf("failed to submit orders")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
log.Infof("created position release orders: %+v", createdOrders)
|
||
|
})
|
||
|
```
|
||
|
|
||
|
Strategy should provide OnReleasePosition callback, which will be called when position (positive or negative) is over hard limit.
|
||
|
|
||
|
Modify quantity before submitting orders:
|
||
|
|
||
|
```
|
||
|
buyQuantity, sellQuantity := s.positionRiskControl.ModifiedQuantity(s.Position.Base)
|
||
|
```
|
||
|
|
||
|
It calculates buy and sell quantity shrinking by hard limit and position.
|
||
|
|
||
|
### 3. Circuit-Break Risk Control
|
||
|
|
||
|
Initialization
|
||
|
|
||
|
```go
|
||
|
s.circuitBreakRiskControl = riskcontrol.NewCircuitBreakRiskControl(
|
||
|
s.Position,
|
||
|
session.Indicators(s.Symbol).EWMA(s.CircuitBreakEMA),
|
||
|
s.CircuitBreakLossThreshold,
|
||
|
s.ProfitStats,
|
||
|
24*time.Hour)
|
||
|
```
|
||
|
|
||
|
Should pass in position and profit states. Also need an price EWMA to calculate unrealized profit.
|
||
|
|
||
|
|
||
|
Validate parameters:
|
||
|
|
||
|
```
|
||
|
if s.CircuitBreakLossThreshold.Float64() > 0 {
|
||
|
return fmt.Errorf("circuitBreakLossThreshold should be non-positive")
|
||
|
}
|
||
|
return nil
|
||
|
```
|
||
|
|
||
|
Circuit break condition should be non-greater than zero.
|
||
|
|
||
|
Check for circuit break before submitting orders:
|
||
|
```
|
||
|
// Circuit break when accumulated losses are over break condition
|
||
|
if s.circuitBreakRiskControl.IsHalted(kline.EndTime) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
submitOrders, err := s.generateSubmitOrders(ctx)
|
||
|
if err != nil {
|
||
|
log.WithError(err).Error("failed to generate submit orders")
|
||
|
return
|
||
|
}
|
||
|
log.Infof("submit orders: %+v", submitOrders)
|
||
|
|
||
|
if s.DryRun {
|
||
|
log.Infof("dry run, not submitting orders")
|
||
|
return
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Notice that if there are multiple place to submit orders, it is recommended to check in one place in Strategy.Run() and re-use that flag before submitting orders. That can avoid duplicated logs generated from IsHalted().
|