add price risk control

This commit is contained in:
narumi 2023-09-28 17:03:41 +08:00
parent 2058ce808b
commit 53309616c3
2 changed files with 103 additions and 0 deletions

View File

@ -0,0 +1,40 @@
package riskcontrol
import (
"fmt"
"github.com/c9s/bbgo/pkg/fixedpoint"
indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2"
"github.com/c9s/bbgo/pkg/types"
)
type PriceRiskControl struct {
referencePrice *indicatorv2.EWMAStream
lossThreshold fixedpoint.Value
}
func NewPriceRiskControl(refPrice *indicatorv2.EWMAStream, threshold fixedpoint.Value) *PriceRiskControl {
return &PriceRiskControl{
referencePrice: refPrice,
lossThreshold: threshold,
}
}
func (r *PriceRiskControl) IsSafe(side types.SideType, price fixedpoint.Value, quantity fixedpoint.Value) bool {
refPrice := fixedpoint.NewFromFloat(r.referencePrice.Last(0))
// calculate profit
var profit fixedpoint.Value
if side == types.SideTypeBuy {
profit = refPrice.Sub(price).Mul(quantity)
} else if side == types.SideTypeSell {
profit = price.Sub(refPrice).Mul(quantity)
}
return profit.Compare(r.lossThreshold) > 0
}
func (r *PriceRiskControl) IsSafeLimitOrder(o types.SubmitOrder) (bool, error) {
if !(o.Type == types.OrderTypeLimit || o.Type == types.OrderTypeLimitMaker) {
return false, fmt.Errorf("order type is not limit order")
}
return r.IsSafe(o.Side, o.Price, o.Quantity), nil
}

View File

@ -0,0 +1,63 @@
package riskcontrol
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/c9s/bbgo/pkg/fixedpoint"
indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2"
"github.com/c9s/bbgo/pkg/types"
)
func Test_IsSafe(t *testing.T) {
refPrice := 30000.00
lossThreshold := fixedpoint.NewFromFloat(-100)
window := types.IntervalWindow{Window: 30, Interval: types.Interval1m}
refPriceEWMA := indicatorv2.EWMA2(nil, window.Window)
refPriceEWMA.PushAndEmit(refPrice)
cases := []struct {
name string
side types.SideType
price fixedpoint.Value
quantity fixedpoint.Value
isSafe bool
}{
{
name: "BuyingHighSafe",
side: types.SideTypeBuy,
price: fixedpoint.NewFromFloat(30040.0),
quantity: fixedpoint.NewFromFloat(1.0),
isSafe: true,
},
{
name: "SellingLowSafe",
side: types.SideTypeSell,
price: fixedpoint.NewFromFloat(29960.0),
quantity: fixedpoint.NewFromFloat(1.0),
isSafe: true,
},
{
name: "BuyingHighLoss",
side: types.SideTypeBuy,
price: fixedpoint.NewFromFloat(30040.0),
quantity: fixedpoint.NewFromFloat(10.0),
isSafe: false,
},
{
name: "SellingLowLoss",
side: types.SideTypeSell,
price: fixedpoint.NewFromFloat(29960.0),
quantity: fixedpoint.NewFromFloat(10.0),
isSafe: false,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var riskControl = NewPriceRiskControl(refPriceEWMA, lossThreshold)
assert.Equal(t, tc.isSafe, riskControl.IsSafe(tc.side, tc.price, tc.quantity))
})
}
}