backtest: fix limit taker lock issue

This commit is contained in:
c9s 2022-08-18 15:09:46 +08:00
parent d7dbfd7613
commit 6c4d5041ba
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
2 changed files with 69 additions and 0 deletions

View File

@ -178,6 +178,8 @@ func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *ty
if isTaker {
if order.Type == types.OrderTypeMarket {
order.Price = m.LastPrice
} else if order.Type == types.OrderTypeLimit {
order.AveragePrice = m.LastPrice
}
// emit the order update for Status:New
@ -197,6 +199,33 @@ func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *ty
m.EmitOrderUpdate(order2)
// unlock the rest balances for limit taker
if order.Type == types.OrderTypeLimit {
if order.AveragePrice.IsZero() {
return nil, nil, fmt.Errorf("the average price of the given limit taker order can not be zero")
}
switch o.Side {
case types.SideTypeBuy:
// limit buy taker, the order price is higher than the current best ask price
// the executed price is lower than the given price, so we will use less quote currency to buy the base asset.
amount := order.Price.Sub(order.AveragePrice).Mul(order.Quantity)
if amount.Sign() > 0 {
if err := m.Account.UnlockBalance(m.Market.QuoteCurrency, amount); err != nil {
return nil, nil, err
}
}
case types.SideTypeSell:
// limit sell taker, the order price is lower than the current best bid price
// the executed price is higher than the given price, so we will get more quote currency back
amount := order.AveragePrice.Sub(order.Price).Mul(order.Quantity)
if amount.Sign() > 0 {
m.Account.AddBalance(m.Market.QuoteCurrency, amount)
}
}
}
// let the exchange emit the "FILLED" order update (we need the closed order)
// m.EmitOrderUpdate(order2)
return &order2, &trade, nil

View File

@ -208,6 +208,46 @@ func getTestAccount() *types.Account {
return account
}
func TestSimplePriceMatching_LimitBuyTakerOrder(t *testing.T) {
account := getTestAccount()
market := getTestMarket()
engine := &SimplePriceMatching{
Account: account,
Market: market,
closedOrders: make(map[uint64]types.Order),
LastPrice: fixedpoint.NewFromFloat(19000.0),
}
takerOrder := types.SubmitOrder{
Symbol: market.Symbol,
Side: types.SideTypeBuy,
Type: types.OrderTypeLimit,
Quantity: fixedpoint.NewFromFloat(0.1),
Price: fixedpoint.NewFromFloat(20000.0),
TimeInForce: types.TimeInForceGTC,
}
createdOrder, trade, err := engine.PlaceOrder(takerOrder)
assert.NoError(t, err)
t.Logf("created order: %+v", createdOrder)
t.Logf("executed trade: %+v", trade)
assert.Equal(t, "19000", trade.Price.String())
assert.Equal(t, "19000", createdOrder.AveragePrice.String())
assert.Equal(t, "20000", createdOrder.Price.String())
usdt, ok := account.Balance("USDT")
assert.True(t, ok)
assert.True(t, usdt.Locked.IsZero())
btc, ok := account.Balance("BTC")
assert.True(t, ok)
assert.True(t, btc.Locked.IsZero())
assert.Equal(t, fixedpoint.NewFromFloat(100.0).Add(createdOrder.Quantity).String(), btc.Available.String())
usedQuoteAmount := createdOrder.AveragePrice.Mul(createdOrder.Quantity)
assert.Equal(t, usdt.Available.String(), fixedpoint.NewFromFloat(1000000.0).Sub(usedQuoteAmount).String())
}
func TestSimplePriceMatching_StopLimitOrderBuy(t *testing.T) {
account := getTestAccount()
market := getTestMarket()