Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: [grid2] round down quoteQuantity/baseQuantity after the fee reduction #1086

Merged
merged 2 commits into from
Mar 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 29 additions & 23 deletions pkg/strategy/grid2/strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,25 +401,14 @@ func (s *Strategy) processFilledOrder(o types.Order) {
// will be used for calculating quantity
orderExecutedQuoteAmount := o.Quantity.Mul(executedPrice)

// collect trades
feeQuantityReduction := fixedpoint.Zero
feeCurrency := ""
feePrec := 2

// feeQuantityReduction calculation is used to reduce the order quantity
// collect trades for fee
// fee 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
// 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",
o.OrderID, o.Side,
feeQuantityReduction.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)
fee.String(), feeCurrency)

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

newQuantity = fixedpoint.Max(orderExecutedQuoteAmount.Div(newPrice), s.Market.MinQuantity)
// 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 %s %s order quote quantity %s to %s by quote precision %d", s.Symbol, newSide, origQuoteAmount.String(), orderExecutedQuoteAmount.String(), s.Market.PricePrecision)

newQuantity = orderExecutedQuoteAmount.Div(newPrice)

origQuantity := newQuantity
newQuantity = newQuantity.Round(s.Market.VolumePrecision, fixedpoint.Down)
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)

newQuantity = fixedpoint.Max(newQuantity, s.Market.MinQuantity)
} else if s.QuantityOrAmount.Quantity.Sign() > 0 {
newQuantity = s.QuantityOrAmount.Quantity
}
Expand All @@ -449,10 +449,6 @@ func (s *Strategy) processFilledOrder(o types.Order) {
profit = s.calculateProfit(o, newPrice, newQuantity)

case types.SideTypeBuy:
if feeCurrency == s.Market.BaseCurrency {
newQuantity = newQuantity.Sub(feeQuantityReduction)
}

newSide = types.SideTypeSell
if !s.ProfitSpread.IsZero() {
newPrice = newPrice.Add(s.ProfitSpread)
Expand All @@ -462,9 +458,19 @@ func (s *Strategy) processFilledOrder(o types.Order) {
}
}

if feeCurrency == s.Market.BaseCurrency {
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(feeQuantityReduction), s.Market.MinQuantity)
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{
Expand Down Expand Up @@ -1869,4 +1875,4 @@ func roundUpMarketQuantity(market types.Market, v fixedpoint.Value, c string) (f
}

return v.Round(prec, fixedpoint.Up), prec
}
}
4 changes: 2 additions & 2 deletions pkg/strategy/grid2/strategy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ func TestStrategy_handleOrderFilled(t *testing.T) {
Type: types.OrderTypeLimit,
Side: types.SideTypeBuy,
Price: number(11_000.0),
Quantity: number(0.09999999),
Quantity: number(0.09999909),
TimeInForce: types.TimeInForceGTC,
Market: s.Market,
Tag: orderTag,
Expand Down Expand Up @@ -797,7 +797,7 @@ func TestStrategy_handleOrderFilled(t *testing.T) {
Symbol: "BTCUSDT",
Type: types.OrderTypeLimit,
Price: number(12_000.0),
Quantity: number(0.09998999),
Quantity: number(0.09999),
Side: types.SideTypeSell,
TimeInForce: types.TimeInForceGTC,
Market: s.Market,
Expand Down