Skip to content

Commit f26568e

Browse files
authored
Merge pull request #1086 from c9s/fix/grid2/quote-fee-rounding
FIX: [grid2] round down quoteQuantity/baseQuantity after the fee reduction
2 parents b04492a + 756a3bb commit f26568e

File tree

2 files changed

+31
-25
lines changed

2 files changed

+31
-25
lines changed

pkg/strategy/grid2/strategy.go

+29-23
Original file line numberDiff line numberDiff line change
@@ -401,25 +401,14 @@ func (s *Strategy) processFilledOrder(o types.Order) {
401401
// will be used for calculating quantity
402402
orderExecutedQuoteAmount := o.Quantity.Mul(executedPrice)
403403

404-
// collect trades
405-
feeQuantityReduction := fixedpoint.Zero
406-
feeCurrency := ""
407-
feePrec := 2
408-
409-
// feeQuantityReduction calculation is used to reduce the order quantity
404+
// collect trades for fee
405+
// fee calculation is used to reduce the order quantity
410406
// because when 1.0 BTC buy order is filled without FEE token, then we will actually get 1.0 * (1 - feeRate) BTC
411407
// if we don't reduce the sell quantity, than we might fail to place the sell order
412-
feeQuantityReduction, feeCurrency = s.aggregateOrderFee(o)
408+
fee, feeCurrency := s.aggregateOrderFee(o)
413409
s.logger.Infof("GRID ORDER #%d %s FEE: %s %s",
414410
o.OrderID, o.Side,
415-
feeQuantityReduction.String(), feeCurrency)
416-
417-
feeQuantityReduction, feePrec = roundUpMarketQuantity(s.Market, feeQuantityReduction, feeCurrency)
418-
s.logger.Infof("GRID ORDER #%d %s FEE (rounding precision %d): %s %s",
419-
o.OrderID, o.Side,
420-
feePrec,
421-
feeQuantityReduction.String(),
422-
feeCurrency)
411+
fee.String(), feeCurrency)
423412

424413
switch o.Side {
425414
case types.SideTypeSell:
@@ -437,10 +426,21 @@ func (s *Strategy) processFilledOrder(o types.Order) {
437426
if s.Compound || s.EarnBase {
438427
// if it's not using the platform fee currency, reduce the quote quantity for the buy order
439428
if feeCurrency == s.Market.QuoteCurrency {
440-
orderExecutedQuoteAmount = orderExecutedQuoteAmount.Sub(feeQuantityReduction)
429+
orderExecutedQuoteAmount = orderExecutedQuoteAmount.Sub(fee)
441430
}
442431

443-
newQuantity = fixedpoint.Max(orderExecutedQuoteAmount.Div(newPrice), s.Market.MinQuantity)
432+
// for quote amount, always round down with price precision to prevent the quote currency fund locking rounding issue
433+
origQuoteAmount := orderExecutedQuoteAmount
434+
orderExecutedQuoteAmount = orderExecutedQuoteAmount.Round(s.Market.PricePrecision, fixedpoint.Down)
435+
s.logger.Infof("round down %s %s order quote quantity %s to %s by quote precision %d", s.Symbol, newSide, origQuoteAmount.String(), orderExecutedQuoteAmount.String(), s.Market.PricePrecision)
436+
437+
newQuantity = orderExecutedQuoteAmount.Div(newPrice)
438+
439+
origQuantity := newQuantity
440+
newQuantity = newQuantity.Round(s.Market.VolumePrecision, fixedpoint.Down)
441+
s.logger.Infof("round down %s %s order base quantity %s to %s by base precision %d", s.Symbol, newSide, origQuantity.String(), newQuantity.String(), s.Market.VolumePrecision)
442+
443+
newQuantity = fixedpoint.Max(newQuantity, s.Market.MinQuantity)
444444
} else if s.QuantityOrAmount.Quantity.Sign() > 0 {
445445
newQuantity = s.QuantityOrAmount.Quantity
446446
}
@@ -449,10 +449,6 @@ func (s *Strategy) processFilledOrder(o types.Order) {
449449
profit = s.calculateProfit(o, newPrice, newQuantity)
450450

451451
case types.SideTypeBuy:
452-
if feeCurrency == s.Market.BaseCurrency {
453-
newQuantity = newQuantity.Sub(feeQuantityReduction)
454-
}
455-
456452
newSide = types.SideTypeSell
457453
if !s.ProfitSpread.IsZero() {
458454
newPrice = newPrice.Add(s.ProfitSpread)
@@ -462,9 +458,19 @@ func (s *Strategy) processFilledOrder(o types.Order) {
462458
}
463459
}
464460

461+
if feeCurrency == s.Market.BaseCurrency {
462+
newQuantity = newQuantity.Sub(fee)
463+
}
464+
465+
// if EarnBase is enabled, we should sell less to get the same quote amount back
465466
if s.EarnBase {
466-
newQuantity = fixedpoint.Max(orderExecutedQuoteAmount.Div(newPrice).Sub(feeQuantityReduction), s.Market.MinQuantity)
467+
newQuantity = fixedpoint.Max(orderExecutedQuoteAmount.Div(newPrice).Sub(fee), s.Market.MinQuantity)
467468
}
469+
470+
// always round down the base quantity for placing sell order to avoid the base currency fund locking issue
471+
origQuantity := newQuantity
472+
newQuantity = newQuantity.Round(s.Market.VolumePrecision, fixedpoint.Down)
473+
s.logger.Infof("round down sell order quantity %s to %s by base quantity precision %d", origQuantity.String(), newQuantity.String(), s.Market.VolumePrecision)
468474
}
469475

470476
orderForm := types.SubmitOrder{
@@ -1869,4 +1875,4 @@ func roundUpMarketQuantity(market types.Market, v fixedpoint.Value, c string) (f
18691875
}
18701876

18711877
return v.Round(prec, fixedpoint.Up), prec
1872-
}
1878+
}

pkg/strategy/grid2/strategy_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -706,7 +706,7 @@ func TestStrategy_handleOrderFilled(t *testing.T) {
706706
Type: types.OrderTypeLimit,
707707
Side: types.SideTypeBuy,
708708
Price: number(11_000.0),
709-
Quantity: number(0.09999999),
709+
Quantity: number(0.09999909),
710710
TimeInForce: types.TimeInForceGTC,
711711
Market: s.Market,
712712
Tag: orderTag,
@@ -797,7 +797,7 @@ func TestStrategy_handleOrderFilled(t *testing.T) {
797797
Symbol: "BTCUSDT",
798798
Type: types.OrderTypeLimit,
799799
Price: number(12_000.0),
800-
Quantity: number(0.09998999),
800+
Quantity: number(0.09999),
801801
Side: types.SideTypeSell,
802802
TimeInForce: types.TimeInForceGTC,
803803
Market: s.Market,

0 commit comments

Comments
 (0)