Merge pull request #1119 from c9s/feature/grids/recover-from-trades

FEATURE: make PinOrderMap's key from string to Pin
This commit is contained in:
kbearXD 2023-03-17 10:45:09 +08:00 committed by GitHub
commit 294c09b9e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 55 deletions

View File

@ -4,11 +4,12 @@ import (
"fmt"
"strings"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/types"
)
// PinOrderMap store the pin-order's relation, we will change key from string to fixedpoint.Value when FormatString fixed
type PinOrderMap map[string]types.Order
type PinOrderMap map[fixedpoint.Value]types.Order
// AscendingOrders get the orders from pin order map and sort it in asc order
func (m PinOrderMap) AscendingOrders() []types.Order {
@ -41,7 +42,7 @@ func (m PinOrderMap) String() string {
sb.WriteString("================== PIN ORDER MAP ==================\n")
for pin, order := range m {
sb.WriteString(fmt.Sprintf("%s -> %s\n", pin, order.String()))
sb.WriteString(fmt.Sprintf("%+v -> %s\n", pin, order.String()))
}
sb.WriteString("================== END OF PIN ORDER MAP ==================\n")
return sb.String()

View File

@ -1465,11 +1465,11 @@ func (s *Strategy) verifyFilledGrid(pins []Pin, pinOrders PinOrderMap, filledOrd
s.debugOrders("filled orders", filledOrders)
for _, filledOrder := range filledOrders {
price := s.Market.FormatPrice(filledOrder.Price)
price := filledOrder.Price
if o, exist := pinOrders[price]; !exist {
return fmt.Errorf("the price (%s) is not in pins", price)
return fmt.Errorf("the price (%+v) is not in pins", price)
} else if o.OrderID != 0 {
return fmt.Errorf("there is already an order at this price (%s)", price)
return fmt.Errorf("there is already an order at this price (%+v)", price)
} else {
pinOrders[price] = filledOrder
}
@ -1479,10 +1479,9 @@ func (s *Strategy) verifyFilledGrid(pins []Pin, pinOrders PinOrderMap, filledOrd
side := types.SideTypeBuy
for _, pin := range pins {
price := s.Market.FormatPrice(fixedpoint.Value(pin))
order, exist := pinOrders[price]
order, exist := pinOrders[fixedpoint.Value(pin)]
if !exist {
return fmt.Errorf("there is no order at price (%s)", price)
return fmt.Errorf("there is no order at price (%+v)", pin)
}
// if there is order with OrderID = 0, means we hit the empty pin
@ -1516,13 +1515,12 @@ func (s *Strategy) buildPinOrderMap(pins []Pin, openOrders []types.Order) (PinOr
pinOrderMap := make(PinOrderMap)
for _, pin := range pins {
priceStr := s.Market.FormatPrice(fixedpoint.Value(pin))
pinOrderMap[priceStr] = types.Order{}
pinOrderMap[fixedpoint.Value(pin)] = types.Order{}
}
for _, openOrder := range openOrders {
priceStr := s.Market.FormatPrice(openOrder.Price)
v, exist := pinOrderMap[priceStr]
pin := openOrder.Price
v, exist := pinOrderMap[pin]
if !exist {
return nil, fmt.Errorf("the price of the order (id: %d) is not in pins", openOrder.OrderID)
}
@ -1531,7 +1529,7 @@ func (s *Strategy) buildPinOrderMap(pins []Pin, openOrders []types.Order) (PinOr
return nil, fmt.Errorf("there are duplicated open orders at the same pin")
}
pinOrderMap[priceStr] = openOrder
pinOrderMap[pin] = openOrder
}
return pinOrderMap, nil
@ -1590,8 +1588,8 @@ func (s *Strategy) buildFilledPinOrderMapFromTrades(ctx context.Context, history
}
// checked the trade's order is filled order
priceStr := s.Market.FormatPrice(order.Price)
v, exist := pinOrdersOpen[priceStr]
pin := order.Price
v, exist := pinOrdersOpen[pin]
if !exist {
return nil, fmt.Errorf("the price of the order with the same GroupID is not in pins")
}
@ -1602,13 +1600,13 @@ func (s *Strategy) buildFilledPinOrderMapFromTrades(ctx context.Context, history
}
// check the order's creation time
if pinOrder, exist := pinOrdersFilled[priceStr]; exist && pinOrder.CreationTime.Time().After(order.CreationTime.Time()) {
if pinOrder, exist := pinOrdersFilled[pin]; exist && pinOrder.CreationTime.Time().After(order.CreationTime.Time()) {
// do not replace the pin order if the order's creation time is not after pin order's creation time
// this situation should not happen actually, because the trades is already sorted.
s.logger.Infof("pinOrder's creation time (%s) should not be after order's creation time (%s)", pinOrder.CreationTime, order.CreationTime)
continue
}
pinOrdersFilled[priceStr] = *order
pinOrdersFilled[pin] = *order
// wait 100 ms to avoid rate limit
time.Sleep(100 * time.Millisecond)

View File

@ -1100,7 +1100,7 @@ func Test_buildPinOrderMap(t *testing.T) {
assert.Len(m, 11)
for pin, order := range m {
if pin == s.FormatPrice(openOrders[0].Price) {
if pin == openOrders[0].Price {
assert.Equal(openOrders[0].OrderID, order.OrderID)
} else {
assert.Equal(uint64(0), order.OrderID)
@ -1184,19 +1184,19 @@ func Test_getOrdersFromPinOrderMapInAscOrder(t *testing.T) {
assert := assert.New(t)
now := time.Now()
pinOrderMap := PinOrderMap{
"1000": types.Order{
number("1000"): types.Order{
OrderID: 1,
CreationTime: types.Time(now.Add(1 * time.Hour)),
UpdateTime: types.Time(now.Add(5 * time.Hour)),
},
"1100": types.Order{},
"1200": types.Order{},
"1300": types.Order{
number("1100"): types.Order{},
number("1200"): types.Order{},
number("1300"): types.Order{
OrderID: 3,
CreationTime: types.Time(now.Add(3 * time.Hour)),
UpdateTime: types.Time(now.Add(6 * time.Hour)),
},
"1400": types.Order{
number("1400"): types.Order{
OrderID: 2,
CreationTime: types.Time(now.Add(2 * time.Hour)),
UpdateTime: types.Time(now.Add(4 * time.Hour)),
@ -1220,20 +1220,20 @@ func Test_verifyFilledGrid(t *testing.T) {
t.Run("valid grid with buy/sell orders", func(t *testing.T) {
pinOrderMap := PinOrderMap{
"100.00": types.Order{
number("100.00"): types.Order{
OrderID: 1,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
},
},
"200.00": types.Order{},
"300.00": types.Order{
number("200.00"): types.Order{},
number("300.00"): types.Order{
OrderID: 3,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
},
},
"400.00": types.Order{
number("400.00"): types.Order{
OrderID: 4,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
@ -1245,45 +1245,45 @@ func Test_verifyFilledGrid(t *testing.T) {
})
t.Run("valid grid with only buy orders", func(t *testing.T) {
pinOrderMap := PinOrderMap{
"100.00": types.Order{
number("100.00"): types.Order{
OrderID: 1,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
},
},
"200.00": types.Order{
number("200.00"): types.Order{
OrderID: 2,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
},
},
"300.00": types.Order{
number("300.00"): types.Order{
OrderID: 3,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
},
},
"400.00": types.Order{},
number("400.00"): types.Order{},
}
assert.NoError(s.verifyFilledGrid(s.grid.Pins, pinOrderMap, nil))
})
t.Run("valid grid with only sell orders", func(t *testing.T) {
pinOrderMap := PinOrderMap{
"100.00": types.Order{},
"200.00": types.Order{
number("100.00"): types.Order{},
number("200.00"): types.Order{
OrderID: 2,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
},
},
"300.00": types.Order{
number("300.00"): types.Order{
OrderID: 3,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
},
},
"400.00": types.Order{
number("400.00"): types.Order{
OrderID: 4,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
@ -1295,15 +1295,15 @@ func Test_verifyFilledGrid(t *testing.T) {
})
t.Run("invalid grid with multiple empty pins", func(t *testing.T) {
pinOrderMap := PinOrderMap{
"100.00": types.Order{
number("100.00"): types.Order{
OrderID: 1,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
},
},
"200.00": types.Order{},
"300.00": types.Order{},
"400.00": types.Order{
number("200.00"): types.Order{},
number("300.00"): types.Order{},
number("400.00"): types.Order{
OrderID: 4,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
@ -1315,25 +1315,25 @@ func Test_verifyFilledGrid(t *testing.T) {
})
t.Run("invalid grid without empty pin", func(t *testing.T) {
pinOrderMap := PinOrderMap{
"100.00": types.Order{
number("100.00"): types.Order{
OrderID: 1,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
},
},
"200.00": types.Order{
number("200.00"): types.Order{
OrderID: 2,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
},
},
"300.00": types.Order{
number("300.00"): types.Order{
OrderID: 3,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
},
},
"400.00": types.Order{
number("400.00"): types.Order{
OrderID: 4,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
@ -1345,20 +1345,20 @@ func Test_verifyFilledGrid(t *testing.T) {
})
t.Run("invalid grid with Buy-empty-Sell-Buy order", func(t *testing.T) {
pinOrderMap := PinOrderMap{
"100.00": types.Order{
number("100.00"): types.Order{
OrderID: 1,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
},
},
"200.00": types.Order{},
"300.00": types.Order{
number("200.00"): types.Order{},
number("300.00"): types.Order{
OrderID: 3,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
},
},
"400.00": types.Order{
number("400.00"): types.Order{
OrderID: 4,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
@ -1370,45 +1370,45 @@ func Test_verifyFilledGrid(t *testing.T) {
})
t.Run("invalid grid with Sell-empty order", func(t *testing.T) {
pinOrderMap := PinOrderMap{
"100.00": types.Order{
number("100.00"): types.Order{
OrderID: 1,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
},
},
"200.00": types.Order{
number("200.00"): types.Order{
OrderID: 2,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
},
},
"300.00": types.Order{
number("300.00"): types.Order{
OrderID: 3,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeSell,
},
},
"400.00": types.Order{},
number("400.00"): types.Order{},
}
assert.Error(s.verifyFilledGrid(s.grid.Pins, pinOrderMap, nil))
})
t.Run("invalid grid with empty-Buy order", func(t *testing.T) {
pinOrderMap := PinOrderMap{
"100.00": types.Order{},
"200.00": types.Order{
number("100.00"): types.Order{},
number("200.00"): types.Order{
OrderID: 2,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
},
},
"300.00": types.Order{
number("300.00"): types.Order{
OrderID: 3,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,
},
},
"400.00": types.Order{
number("400.00"): types.Order{
OrderID: 4,
SubmitOrder: types.SubmitOrder{
Side: types.SideTypeBuy,