Merge pull request #1164 from c9s/fix/grid2/trade-fee

FIX: [grid2] apply defensive programming on the order quantity
This commit is contained in:
Yo-An Lin 2023-04-28 16:26:20 +08:00 committed by GitHub
commit 9db62ed0fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -344,13 +344,32 @@ func (s *Strategy) calculateProfit(o types.Order, buyPrice, buyQuantity fixedpoi
func (s *Strategy) verifyOrderTrades(o types.Order, trades []types.Trade) bool {
tq := aggregateTradesQuantity(trades)
if tq.Compare(o.Quantity) != 0 {
s.logger.Warnf("order trades missing. expected: %s got: %s",
o.Quantity.String(),
tq.String())
return false
// on MAX: if order.status == filled, it does not mean order.executedQuantity == order.quantity
// order.executedQuantity can be less than order.quantity
// so here we use executed quantity to check if the total trade quantity matches to order.executedQuantity
executedQuantity := o.ExecutedQuantity
if executedQuantity.IsZero() {
// fall back to the original quantity if the executed quantity is zero
executedQuantity = o.Quantity
}
// early return here if it matches
c := tq.Compare(executedQuantity)
if c == 0 {
return true
}
if c < 0 {
s.logger.Warnf("order trades missing. expected: %s got: %s",
executedQuantity.String(),
tq.String())
return false
} else if c > 0 {
s.logger.Errorf("aggregated trade quantity %s > order executed quantity %s, something is wrong, please check", tq.String(), executedQuantity.String())
return true
}
// shouldn't reach here
return true
}
@ -360,7 +379,7 @@ func (s *Strategy) aggregateOrderFee(o types.Order) (fixedpoint.Value, string) {
// try to get the received trades (websocket trades)
orderTrades := s.historicalTrades.GetOrderTrades(o)
if len(orderTrades) > 0 {
s.logger.Infof("found filled order trades: %+v", orderTrades)
s.logger.Infof("GRID: found filled order trades: %+v", orderTrades)
}
feeCurrency := s.Market.BaseCurrency
@ -384,7 +403,7 @@ func (s *Strategy) aggregateOrderFee(o types.Order) (fixedpoint.Value, string) {
return fixedpoint.Zero, feeCurrency
}
s.logger.Warnf("missing order trades or missing trade fee, pulling order trades from API")
s.logger.Warnf("GRID: missing #%d order trades or missing trade fee, pulling order trades from API", o.OrderID)
// if orderQueryService is supported, use it to query the trades of the filled order
apiOrderTrades, err := s.orderQueryService.QueryOrderTrades(context.Background(), types.OrderQuery{
@ -392,13 +411,19 @@ func (s *Strategy) aggregateOrderFee(o types.Order) (fixedpoint.Value, string) {
OrderID: strconv.FormatUint(o.OrderID, 10),
})
if err != nil {
s.logger.WithError(err).Errorf("query order trades error")
s.logger.WithError(err).Errorf("query #%d order trades error", o.OrderID)
} else {
s.logger.Infof("fetched api trades: %+v", apiOrderTrades)
s.logger.Infof("GRID: fetched api #%d order trades: %+v", o.OrderID, apiOrderTrades)
orderTrades = apiOrderTrades
}
}
// still try to aggregate the trades quantity if we can:
fees := collectTradeFee(orderTrades)
if fee, ok := fees[feeCurrency]; ok {
return fee, feeCurrency
}
return fixedpoint.Zero, feeCurrency
}
@ -408,9 +433,20 @@ func (s *Strategy) processFilledOrder(o types.Order) {
// check order fee
newSide := types.SideTypeSell
newPrice := o.Price
newQuantity := o.Quantity
executedQuantity := o.ExecutedQuantity
// A safeguard check, fallback to the original quantity
if executedQuantity.IsZero() {
executedQuantity = o.Quantity
}
newQuantity := executedQuantity
executedPrice := o.Price
if o.ExecutedQuantity.Compare(o.Quantity) != 0 {
s.logger.Warnf("order #%d is filled, but order executed quantity %s != order quantity %s, something is wrong", o.OrderID, o.ExecutedQuantity, o.Quantity)
}
/*
if o.AveragePrice.Sign() > 0 {
executedPrice = o.AveragePrice
@ -418,7 +454,7 @@ func (s *Strategy) processFilledOrder(o types.Order) {
*/
// will be used for calculating quantity
orderExecutedQuoteAmount := o.Quantity.Mul(executedPrice)
orderExecutedQuoteAmount := executedQuantity.Mul(executedPrice)
// collect trades for fee
// fee calculation is used to reduce the order quantity