grid2: round down quoteQuantity/baseQuantity after the fee reduction

This commit is contained in:
c9s 2023-03-07 13:36:33 +08:00
parent b04492a5a7
commit 62eed9605d
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
2 changed files with 26 additions and 25 deletions

View File

@ -401,25 +401,14 @@ func (s *Strategy) processFilledOrder(o types.Order) {
// will be used for calculating quantity // will be used for calculating quantity
orderExecutedQuoteAmount := o.Quantity.Mul(executedPrice) orderExecutedQuoteAmount := o.Quantity.Mul(executedPrice)
// collect trades // collect trades for fee
feeQuantityReduction := fixedpoint.Zero // fee calculation is used to reduce the order quantity
feeCurrency := ""
feePrec := 2
// feeQuantityReduction calculation is used to reduce the order quantity
// because when 1.0 BTC buy order is filled without FEE token, then we will actually get 1.0 * (1 - feeRate) BTC // because when 1.0 BTC buy order is filled without FEE token, then we will actually get 1.0 * (1 - feeRate) BTC
// if we don't reduce the sell quantity, than we might fail to place the sell order // if we don't reduce the sell quantity, than we might fail to place the sell order
feeQuantityReduction, feeCurrency = s.aggregateOrderFee(o) fee, feeCurrency := s.aggregateOrderFee(o)
s.logger.Infof("GRID ORDER #%d %s FEE: %s %s", s.logger.Infof("GRID ORDER #%d %s FEE: %s %s",
o.OrderID, o.Side, o.OrderID, o.Side,
feeQuantityReduction.String(), feeCurrency) fee.String(), feeCurrency)
feeQuantityReduction, feePrec = roundUpMarketQuantity(s.Market, feeQuantityReduction, feeCurrency)
s.logger.Infof("GRID ORDER #%d %s FEE (rounding precision %d): %s %s",
o.OrderID, o.Side,
feePrec,
feeQuantityReduction.String(),
feeCurrency)
switch o.Side { switch o.Side {
case types.SideTypeSell: case types.SideTypeSell:
@ -437,9 +426,15 @@ func (s *Strategy) processFilledOrder(o types.Order) {
if s.Compound || s.EarnBase { if s.Compound || s.EarnBase {
// if it's not using the platform fee currency, reduce the quote quantity for the buy order // if it's not using the platform fee currency, reduce the quote quantity for the buy order
if feeCurrency == s.Market.QuoteCurrency { if feeCurrency == s.Market.QuoteCurrency {
orderExecutedQuoteAmount = orderExecutedQuoteAmount.Sub(feeQuantityReduction) orderExecutedQuoteAmount = orderExecutedQuoteAmount.Sub(fee)
} }
// for quote amount, always round down with price precision to prevent the quote currency fund locking rounding issue
origQuoteAmount := orderExecutedQuoteAmount
orderExecutedQuoteAmount = orderExecutedQuoteAmount.Round(s.Market.PricePrecision, fixedpoint.Down)
s.logger.Infof("round down buy order quote quantity %s to %s by quote quantity precision %d", origQuoteAmount.String(), orderExecutedQuoteAmount.String(), s.Market.PricePrecision)
newQuantity = fixedpoint.Max(orderExecutedQuoteAmount.Div(newPrice), s.Market.MinQuantity) newQuantity = fixedpoint.Max(orderExecutedQuoteAmount.Div(newPrice), s.Market.MinQuantity)
} else if s.QuantityOrAmount.Quantity.Sign() > 0 { } else if s.QuantityOrAmount.Quantity.Sign() > 0 {
newQuantity = s.QuantityOrAmount.Quantity newQuantity = s.QuantityOrAmount.Quantity
@ -449,10 +444,6 @@ func (s *Strategy) processFilledOrder(o types.Order) {
profit = s.calculateProfit(o, newPrice, newQuantity) profit = s.calculateProfit(o, newPrice, newQuantity)
case types.SideTypeBuy: case types.SideTypeBuy:
if feeCurrency == s.Market.BaseCurrency {
newQuantity = newQuantity.Sub(feeQuantityReduction)
}
newSide = types.SideTypeSell newSide = types.SideTypeSell
if !s.ProfitSpread.IsZero() { if !s.ProfitSpread.IsZero() {
newPrice = newPrice.Add(s.ProfitSpread) newPrice = newPrice.Add(s.ProfitSpread)
@ -462,9 +453,19 @@ func (s *Strategy) processFilledOrder(o types.Order) {
} }
} }
if s.EarnBase { if feeCurrency == s.Market.BaseCurrency {
newQuantity = fixedpoint.Max(orderExecutedQuoteAmount.Div(newPrice).Sub(feeQuantityReduction), s.Market.MinQuantity) newQuantity = newQuantity.Sub(fee)
} }
// if EarnBase is enabled, we should sell less to get the same quote amount back
if s.EarnBase {
newQuantity = fixedpoint.Max(orderExecutedQuoteAmount.Div(newPrice).Sub(fee), s.Market.MinQuantity)
}
// always round down the base quantity for placing sell order to avoid the base currency fund locking issue
origQuantity := newQuantity
newQuantity = newQuantity.Round(s.Market.VolumePrecision, fixedpoint.Down)
s.logger.Infof("round down sell order quantity %s to %s by base quantity precision %d", origQuantity.String(), newQuantity.String(), s.Market.VolumePrecision)
} }
orderForm := types.SubmitOrder{ orderForm := types.SubmitOrder{
@ -1869,4 +1870,4 @@ func roundUpMarketQuantity(market types.Market, v fixedpoint.Value, c string) (f
} }
return v.Round(prec, fixedpoint.Up), prec return v.Round(prec, fixedpoint.Up), prec
} }

View File

@ -706,7 +706,7 @@ func TestStrategy_handleOrderFilled(t *testing.T) {
Type: types.OrderTypeLimit, Type: types.OrderTypeLimit,
Side: types.SideTypeBuy, Side: types.SideTypeBuy,
Price: number(11_000.0), Price: number(11_000.0),
Quantity: number(0.09999999), Quantity: number(0.09999909),
TimeInForce: types.TimeInForceGTC, TimeInForce: types.TimeInForceGTC,
Market: s.Market, Market: s.Market,
Tag: orderTag, Tag: orderTag,
@ -797,7 +797,7 @@ func TestStrategy_handleOrderFilled(t *testing.T) {
Symbol: "BTCUSDT", Symbol: "BTCUSDT",
Type: types.OrderTypeLimit, Type: types.OrderTypeLimit,
Price: number(12_000.0), Price: number(12_000.0),
Quantity: number(0.09998999), Quantity: number(0.09999),
Side: types.SideTypeSell, Side: types.SideTypeSell,
TimeInForce: types.TimeInForceGTC, TimeInForce: types.TimeInForceGTC,
Market: s.Market, Market: s.Market,