mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
Merge pull request #773 from c9s/fix/backtest-taker-order
fix: fix backtest taker order execution
This commit is contained in:
commit
ee59fc447d
|
@ -126,15 +126,19 @@ func (m *SimplePriceMatching) CancelOrder(o types.Order) (types.Order, error) {
|
|||
|
||||
// PlaceOrder returns the created order object, executed trade (if any) and error
|
||||
func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *types.Trade, error) {
|
||||
if o.Type == types.OrderTypeMarket {
|
||||
if m.LastPrice.IsZero() {
|
||||
panic("unexpected error: for market order, the last price can not be zero")
|
||||
}
|
||||
}
|
||||
|
||||
isTaker := o.Type == types.OrderTypeMarket || isLimitTakerOrder(o, m.LastPrice)
|
||||
|
||||
// price for checking account balance, default price
|
||||
price := o.Price
|
||||
|
||||
switch o.Type {
|
||||
case types.OrderTypeMarket:
|
||||
if m.LastPrice.IsZero() {
|
||||
panic("unexpected: last price can not be zero")
|
||||
}
|
||||
|
||||
price = m.LastPrice
|
||||
case types.OrderTypeLimit, types.OrderTypeStopLimit, types.OrderTypeLimitMaker:
|
||||
price = o.Price
|
||||
|
@ -167,7 +171,7 @@ func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *ty
|
|||
orderID := incOrderID()
|
||||
order := m.newOrder(o, orderID)
|
||||
|
||||
if o.Type == types.OrderTypeMarket {
|
||||
if isTaker {
|
||||
// emit the order update for Status:New
|
||||
m.EmitOrderUpdate(order)
|
||||
|
||||
|
@ -175,13 +179,12 @@ func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *ty
|
|||
var order2 = order
|
||||
|
||||
// emit trade before we publish order
|
||||
trade := m.newTradeFromOrder(&order2, false)
|
||||
trade := m.newTradeFromOrder(&order2, false, m.LastPrice)
|
||||
m.executeTrade(trade)
|
||||
|
||||
// update the order status
|
||||
order2.Status = types.OrderStatusFilled
|
||||
order2.ExecutedQuantity = order2.Quantity
|
||||
order2.Price = price
|
||||
order2.IsWorking = false
|
||||
|
||||
// let the exchange emit the "FILLED" order update (we need the closed order)
|
||||
|
@ -240,26 +243,21 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
|
|||
m.EmitBalanceUpdate(m.Account.Balances())
|
||||
}
|
||||
|
||||
func (m *SimplePriceMatching) newTradeFromOrder(order *types.Order, isMaker bool) types.Trade {
|
||||
func (m *SimplePriceMatching) getFeeRate(isMaker bool) (feeRate fixedpoint.Value) {
|
||||
// BINANCE uses 0.1% for both maker and taker
|
||||
// MAX uses 0.050% for maker and 0.15% for taker
|
||||
var feeRate fixedpoint.Value
|
||||
if isMaker {
|
||||
feeRate = m.Account.MakerFeeRate
|
||||
} else {
|
||||
feeRate = m.Account.TakerFeeRate
|
||||
}
|
||||
return feeRate
|
||||
}
|
||||
|
||||
price := order.Price
|
||||
switch order.Type {
|
||||
case types.OrderTypeMarket, types.OrderTypeStopMarket:
|
||||
if m.LastPrice.IsZero() {
|
||||
panic("unexpected: last price can not be zero")
|
||||
}
|
||||
|
||||
price = m.LastPrice
|
||||
}
|
||||
|
||||
func (m *SimplePriceMatching) newTradeFromOrder(order *types.Order, isMaker bool, price fixedpoint.Value) types.Trade {
|
||||
// BINANCE uses 0.1% for both maker and taker
|
||||
// MAX uses 0.050% for maker and 0.15% for taker
|
||||
var feeRate = m.getFeeRate(isMaker)
|
||||
var quoteQuantity = order.Quantity.Mul(price)
|
||||
var fee fixedpoint.Value
|
||||
var feeCurrency string
|
||||
|
@ -268,17 +266,7 @@ func (m *SimplePriceMatching) newTradeFromOrder(order *types.Order, isMaker bool
|
|||
feeCurrency = FeeToken
|
||||
fee = quoteQuantity.Mul(feeRate)
|
||||
} else {
|
||||
switch order.Side {
|
||||
|
||||
case types.SideTypeBuy:
|
||||
fee = order.Quantity.Mul(feeRate)
|
||||
feeCurrency = m.Market.BaseCurrency
|
||||
|
||||
case types.SideTypeSell:
|
||||
fee = quoteQuantity.Mul(feeRate)
|
||||
feeCurrency = m.Market.QuoteCurrency
|
||||
|
||||
}
|
||||
fee, feeCurrency = calculateNativeOrderFee(order, m.Market, feeRate)
|
||||
}
|
||||
|
||||
// update order time
|
||||
|
@ -288,7 +276,7 @@ func (m *SimplePriceMatching) newTradeFromOrder(order *types.Order, isMaker bool
|
|||
return types.Trade{
|
||||
ID: id,
|
||||
OrderID: order.OrderID,
|
||||
Exchange: "backtest",
|
||||
Exchange: types.ExchangeBacktest,
|
||||
Price: price,
|
||||
Quantity: order.Quantity,
|
||||
QuoteQuantity: quoteQuantity,
|
||||
|
@ -302,8 +290,8 @@ func (m *SimplePriceMatching) newTradeFromOrder(order *types.Order, isMaker bool
|
|||
}
|
||||
}
|
||||
|
||||
// BuyToPrice means price go up and the limit sell should be triggered
|
||||
func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders []types.Order, trades []types.Trade) {
|
||||
// buyToPrice means price go up and the limit sell should be triggered
|
||||
func (m *SimplePriceMatching) buyToPrice(price fixedpoint.Value) (closedOrders []types.Order, trades []types.Trade) {
|
||||
klineMatchingLogger.Debugf("kline buy to price %s", price.String())
|
||||
|
||||
var bidOrders []types.Order
|
||||
|
@ -418,7 +406,7 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
|
|||
|
||||
for i := range closedOrders {
|
||||
o := closedOrders[i]
|
||||
trade := m.newTradeFromOrder(&o, true)
|
||||
trade := m.newTradeFromOrder(&o, true, o.Price)
|
||||
m.executeTrade(trade)
|
||||
closedOrders[i] = o
|
||||
|
||||
|
@ -432,9 +420,9 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
|
|||
return closedOrders, trades
|
||||
}
|
||||
|
||||
// SellToPrice simulates the price trend in down direction.
|
||||
// sellToPrice simulates the price trend in down direction.
|
||||
// When price goes down, buy orders should be executed, and the stop orders should be triggered.
|
||||
func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders []types.Order, trades []types.Trade) {
|
||||
func (m *SimplePriceMatching) sellToPrice(price fixedpoint.Value) (closedOrders []types.Order, trades []types.Trade) {
|
||||
klineMatchingLogger.Debugf("kline sell to price %s", price.String())
|
||||
|
||||
// in this section we handle --- the price goes lower, and we trigger the stop sell
|
||||
|
@ -488,8 +476,10 @@ func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders
|
|||
switch o.Type {
|
||||
|
||||
case types.OrderTypeStopMarket:
|
||||
// should we trigger the order
|
||||
if o.StopPrice.Compare(price) < 0 {
|
||||
// price goes down and if the stop price is still lower than the current price
|
||||
// or the stop price is not touched
|
||||
// then we should skip this order
|
||||
if price.Compare(o.StopPrice) > 0 {
|
||||
bidOrders = append(bidOrders, o)
|
||||
break
|
||||
}
|
||||
|
@ -501,9 +491,10 @@ func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders
|
|||
closedOrders = append(closedOrders, o)
|
||||
|
||||
case types.OrderTypeStopLimit:
|
||||
// if the price is lower than the stop order price
|
||||
// we should trigger the stop order
|
||||
if o.StopPrice.Compare(price) < 0 {
|
||||
// price goes down and if the stop price is still lower than the current price
|
||||
// or the stop price is not touched
|
||||
// then we should skip this order
|
||||
if price.Compare(o.StopPrice) > 0 {
|
||||
bidOrders = append(bidOrders, o)
|
||||
break
|
||||
}
|
||||
|
@ -539,7 +530,7 @@ func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders
|
|||
|
||||
for i := range closedOrders {
|
||||
o := closedOrders[i]
|
||||
trade := m.newTradeFromOrder(&o, true)
|
||||
trade := m.newTradeFromOrder(&o, true, o.Price)
|
||||
m.executeTrade(trade)
|
||||
closedOrders[i] = o
|
||||
|
||||
|
@ -579,40 +570,40 @@ func (m *SimplePriceMatching) processKLine(kline types.KLine) {
|
|||
m.LastPrice = kline.Open
|
||||
} else {
|
||||
if m.LastPrice.Compare(kline.Open) > 0 {
|
||||
m.SellToPrice(kline.Open)
|
||||
m.sellToPrice(kline.Open)
|
||||
} else {
|
||||
m.BuyToPrice(kline.Open)
|
||||
m.buyToPrice(kline.Open)
|
||||
}
|
||||
}
|
||||
|
||||
switch kline.Direction() {
|
||||
case types.DirectionDown:
|
||||
if kline.High.Compare(kline.Open) >= 0 {
|
||||
m.BuyToPrice(kline.High)
|
||||
m.buyToPrice(kline.High)
|
||||
}
|
||||
|
||||
// if low is lower than close, sell to low first, and then buy up to close
|
||||
if kline.Low.Compare(kline.Close) < 0 {
|
||||
m.SellToPrice(kline.Low)
|
||||
m.BuyToPrice(kline.Close)
|
||||
m.sellToPrice(kline.Low)
|
||||
m.buyToPrice(kline.Close)
|
||||
} else {
|
||||
m.SellToPrice(kline.Close)
|
||||
m.sellToPrice(kline.Close)
|
||||
}
|
||||
|
||||
case types.DirectionUp:
|
||||
if kline.Low.Compare(kline.Open) <= 0 {
|
||||
m.SellToPrice(kline.Low)
|
||||
m.sellToPrice(kline.Low)
|
||||
}
|
||||
|
||||
if kline.High.Compare(kline.Close) > 0 {
|
||||
m.BuyToPrice(kline.High)
|
||||
m.SellToPrice(kline.Close)
|
||||
m.buyToPrice(kline.High)
|
||||
m.sellToPrice(kline.Close)
|
||||
} else {
|
||||
m.BuyToPrice(kline.Close)
|
||||
m.buyToPrice(kline.Close)
|
||||
}
|
||||
default: // no trade up or down
|
||||
if m.LastPrice.IsZero() {
|
||||
m.BuyToPrice(kline.Close)
|
||||
m.buyToPrice(kline.Close)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -631,3 +622,28 @@ func (m *SimplePriceMatching) newOrder(o types.SubmitOrder, orderID uint64) type
|
|||
UpdateTime: types.Time(m.CurrentTime),
|
||||
}
|
||||
}
|
||||
|
||||
func calculateNativeOrderFee(order *types.Order, market types.Market, feeRate fixedpoint.Value) (fee fixedpoint.Value, feeCurrency string) {
|
||||
switch order.Side {
|
||||
|
||||
case types.SideTypeBuy:
|
||||
fee = order.Quantity.Mul(feeRate)
|
||||
feeCurrency = market.BaseCurrency
|
||||
|
||||
case types.SideTypeSell:
|
||||
quoteQuantity := order.Quantity.Mul(order.Price)
|
||||
fee = quoteQuantity.Mul(feeRate)
|
||||
feeCurrency = market.QuoteCurrency
|
||||
|
||||
}
|
||||
return fee, feeCurrency
|
||||
}
|
||||
|
||||
func isLimitTakerOrder(o types.SubmitOrder, currentPrice fixedpoint.Value) bool {
|
||||
if currentPrice.IsZero() {
|
||||
return false
|
||||
}
|
||||
|
||||
return o.Type == types.OrderTypeLimit && ((o.Side == types.SideTypeBuy && o.Price.Compare(currentPrice) >= 0) ||
|
||||
(o.Side == types.SideTypeSell && o.Price.Compare(currentPrice) <= 0))
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ func TestSimplePriceMatching_orderUpdate(t *testing.T) {
|
|||
Market: market,
|
||||
CurrentTime: t1,
|
||||
closedOrders: make(map[uint64]types.Order),
|
||||
LastPrice: fixedpoint.NewFromFloat(25000),
|
||||
}
|
||||
|
||||
orderUpdateCnt := 0
|
||||
|
@ -66,11 +67,12 @@ func TestSimplePriceMatching_orderUpdate(t *testing.T) {
|
|||
}
|
||||
})
|
||||
|
||||
// maker order
|
||||
_, _, err := engine.PlaceOrder(newLimitOrder("BTCUSDT", types.SideTypeBuy, 24000.0, 0.1))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, orderUpdateCnt) // should got new status
|
||||
assert.Equal(t, 1, orderUpdateNewStatusCnt) // should got new status
|
||||
assert.Equal(t, 0, orderUpdateFilledStatusCnt) // should got new status
|
||||
assert.Equal(t, 1, orderUpdateCnt) // should get new status
|
||||
assert.Equal(t, 1, orderUpdateNewStatusCnt) // should get new status
|
||||
assert.Equal(t, 0, orderUpdateFilledStatusCnt) // should get new status
|
||||
assert.Equal(t, types.OrderStatusNew, lastOrder.Status)
|
||||
assert.Equal(t, fixedpoint.NewFromFloat(0.0), lastOrder.ExecutedQuantity)
|
||||
|
||||
|
@ -89,23 +91,8 @@ func TestSimplePriceMatching_orderUpdate(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSimplePriceMatching_processKLine(t *testing.T) {
|
||||
account := &types.Account{
|
||||
MakerFeeRate: fixedpoint.NewFromFloat(0.075 * 0.01),
|
||||
TakerFeeRate: fixedpoint.NewFromFloat(0.075 * 0.01),
|
||||
}
|
||||
account.UpdateBalances(types.BalanceMap{
|
||||
"USDT": {Currency: "USDT", Available: fixedpoint.NewFromFloat(10000.0)},
|
||||
})
|
||||
market := types.Market{
|
||||
Symbol: "BTCUSDT",
|
||||
PricePrecision: 8,
|
||||
VolumePrecision: 8,
|
||||
QuoteCurrency: "USDT",
|
||||
BaseCurrency: "BTC",
|
||||
MinNotional: fixedpoint.MustNewFromString("0.001"),
|
||||
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
||||
MinQuantity: fixedpoint.MustNewFromString("0.001"),
|
||||
}
|
||||
account := getTestAccount()
|
||||
market := getTestMarket()
|
||||
|
||||
t1 := time.Date(2021, 7, 1, 0, 0, 0, 0, time.UTC)
|
||||
engine := &SimplePriceMatching{
|
||||
|
@ -113,6 +100,7 @@ func TestSimplePriceMatching_processKLine(t *testing.T) {
|
|||
Market: market,
|
||||
CurrentTime: t1,
|
||||
closedOrders: make(map[uint64]types.Order),
|
||||
LastPrice: fixedpoint.NewFromFloat(30000.0),
|
||||
}
|
||||
|
||||
for i := 0; i <= 5; i++ {
|
||||
|
@ -124,7 +112,7 @@ func TestSimplePriceMatching_processKLine(t *testing.T) {
|
|||
t2 := t1.Add(time.Minute)
|
||||
|
||||
// should match 25000, 24000
|
||||
k := newKLine("BTCUSDT", types.Interval1m, t2, 26000, 27000, 23000, 25000)
|
||||
k := newKLine("BTCUSDT", types.Interval1m, t2, 30000, 27000, 23000, 25000)
|
||||
assert.Equal(t, t2.Add(time.Minute-time.Millisecond), k.EndTime.Time())
|
||||
|
||||
engine.processKLine(k)
|
||||
|
@ -151,6 +139,8 @@ func newKLine(symbol string, interval types.Interval, startTime time.Time, o, h,
|
|||
}
|
||||
}
|
||||
|
||||
// getTestMarket returns the BTCUSDT market information
|
||||
// for tests, we always use BTCUSDT
|
||||
func getTestMarket() types.Market {
|
||||
market := types.Market{
|
||||
Symbol: "BTCUSDT",
|
||||
|
@ -209,13 +199,13 @@ func TestSimplePriceMatching_StopLimitOrderBuy(t *testing.T) {
|
|||
assert.Equal(t, 2, len(engine.bidOrders))
|
||||
assert.Equal(t, 1, len(engine.askOrders))
|
||||
|
||||
closedOrders, trades := engine.BuyToPrice(fixedpoint.NewFromFloat(20000.0))
|
||||
closedOrders, trades := engine.buyToPrice(fixedpoint.NewFromFloat(20000.0))
|
||||
assert.Len(t, closedOrders, 0, "price change far from the price should not trigger the stop buy")
|
||||
assert.Len(t, trades, 0, "price change far from the price should not trigger the stop buy")
|
||||
assert.Equal(t, 2, len(engine.bidOrders), "bid orders should be the same")
|
||||
assert.Equal(t, 1, len(engine.askOrders), "ask orders should be the same")
|
||||
|
||||
closedOrders, trades = engine.BuyToPrice(fixedpoint.NewFromFloat(21001.0))
|
||||
closedOrders, trades = engine.buyToPrice(fixedpoint.NewFromFloat(21001.0))
|
||||
assert.Len(t, closedOrders, 1, "should trigger the stop buy order")
|
||||
assert.Len(t, trades, 1, "should have stop order trade executed")
|
||||
|
||||
|
@ -241,7 +231,7 @@ func TestSimplePriceMatching_StopLimitOrderBuy(t *testing.T) {
|
|||
assert.NotNil(t, createdOrder, "place stop order should not trigger the stop buy")
|
||||
assert.Len(t, engine.bidOrders, 2)
|
||||
|
||||
closedOrders, trades = engine.SellToPrice(fixedpoint.NewFromFloat(20500.0))
|
||||
closedOrders, trades = engine.sellToPrice(fixedpoint.NewFromFloat(20500.0))
|
||||
assert.Len(t, closedOrders, 1, "should trigger the stop buy order")
|
||||
assert.Len(t, trades, 1, "should have stop order trade executed")
|
||||
assert.Len(t, engine.bidOrders, 1, "should left one bid order")
|
||||
|
@ -279,13 +269,13 @@ func TestSimplePriceMatching_StopLimitOrderSell(t *testing.T) {
|
|||
assert.Equal(t, 1, len(engine.bidOrders))
|
||||
assert.Equal(t, 2, len(engine.askOrders))
|
||||
|
||||
closedOrders, trades := engine.SellToPrice(fixedpoint.NewFromFloat(21500.0))
|
||||
closedOrders, trades := engine.sellToPrice(fixedpoint.NewFromFloat(21500.0))
|
||||
assert.Len(t, closedOrders, 0, "price change far from the price should not trigger the stop buy")
|
||||
assert.Len(t, trades, 0, "price change far from the price should not trigger the stop buy")
|
||||
assert.Equal(t, 1, len(engine.bidOrders))
|
||||
assert.Equal(t, 2, len(engine.askOrders))
|
||||
|
||||
closedOrders, trades = engine.SellToPrice(fixedpoint.NewFromFloat(20990.0))
|
||||
closedOrders, trades = engine.sellToPrice(fixedpoint.NewFromFloat(20990.0))
|
||||
assert.Len(t, closedOrders, 1, "should trigger the stop sell order")
|
||||
assert.Len(t, trades, 1, "should have stop order trade executed")
|
||||
assert.Equal(t, 1, len(engine.bidOrders))
|
||||
|
@ -313,7 +303,7 @@ func TestSimplePriceMatching_StopLimitOrderSell(t *testing.T) {
|
|||
assert.Nil(t, trade, "place stop order should not trigger the stop sell")
|
||||
assert.NotNil(t, createdOrder, "place stop order should not trigger the stop sell")
|
||||
|
||||
closedOrders, trades = engine.BuyToPrice(fixedpoint.NewFromFloat(21000.0))
|
||||
closedOrders, trades = engine.buyToPrice(fixedpoint.NewFromFloat(21000.0))
|
||||
if assert.Len(t, closedOrders, 1, "should trigger the stop sell order") {
|
||||
assert.Len(t, trades, 1, "should have stop order trade executed")
|
||||
assert.Equal(t, types.SideTypeSell, closedOrders[0].Side)
|
||||
|
@ -348,11 +338,11 @@ func TestSimplePriceMatching_StopMarketOrderSell(t *testing.T) {
|
|||
assert.Nil(t, trade, "place stop order should not trigger the stop sell")
|
||||
assert.NotNil(t, createdOrder, "place stop order should not trigger the stop sell")
|
||||
|
||||
closedOrders, trades := engine.SellToPrice(fixedpoint.NewFromFloat(21500.0))
|
||||
closedOrders, trades := engine.sellToPrice(fixedpoint.NewFromFloat(21500.0))
|
||||
assert.Len(t, closedOrders, 0, "price change far from the price should not trigger the stop buy")
|
||||
assert.Len(t, trades, 0, "price change far from the price should not trigger the stop buy")
|
||||
|
||||
closedOrders, trades = engine.SellToPrice(fixedpoint.NewFromFloat(20990.0))
|
||||
closedOrders, trades = engine.sellToPrice(fixedpoint.NewFromFloat(20990.0))
|
||||
assert.Len(t, closedOrders, 1, "should trigger the stop sell order")
|
||||
assert.Len(t, trades, 1, "should have stop order trade executed")
|
||||
|
||||
|
@ -384,11 +374,11 @@ func TestSimplePriceMatching_PlaceLimitOrder(t *testing.T) {
|
|||
assert.Len(t, engine.bidOrders, 5)
|
||||
assert.Len(t, engine.askOrders, 5)
|
||||
|
||||
closedOrders, trades := engine.SellToPrice(fixedpoint.NewFromFloat(8100.0))
|
||||
closedOrders, trades := engine.sellToPrice(fixedpoint.NewFromFloat(8100.0))
|
||||
assert.Len(t, closedOrders, 0)
|
||||
assert.Len(t, trades, 0)
|
||||
|
||||
closedOrders, trades = engine.SellToPrice(fixedpoint.NewFromFloat(8000.0))
|
||||
closedOrders, trades = engine.sellToPrice(fixedpoint.NewFromFloat(8000.0))
|
||||
assert.Len(t, closedOrders, 1)
|
||||
assert.Len(t, trades, 1)
|
||||
for _, trade := range trades {
|
||||
|
@ -399,15 +389,15 @@ func TestSimplePriceMatching_PlaceLimitOrder(t *testing.T) {
|
|||
assert.Equal(t, types.SideTypeBuy, o.Side)
|
||||
}
|
||||
|
||||
closedOrders, trades = engine.SellToPrice(fixedpoint.NewFromFloat(7000.0))
|
||||
closedOrders, trades = engine.sellToPrice(fixedpoint.NewFromFloat(7000.0))
|
||||
assert.Len(t, closedOrders, 4)
|
||||
assert.Len(t, trades, 4)
|
||||
|
||||
closedOrders, trades = engine.BuyToPrice(fixedpoint.NewFromFloat(8900.0))
|
||||
closedOrders, trades = engine.buyToPrice(fixedpoint.NewFromFloat(8900.0))
|
||||
assert.Len(t, closedOrders, 0)
|
||||
assert.Len(t, trades, 0)
|
||||
|
||||
closedOrders, trades = engine.BuyToPrice(fixedpoint.NewFromFloat(9000.0))
|
||||
closedOrders, trades = engine.buyToPrice(fixedpoint.NewFromFloat(9000.0))
|
||||
assert.Len(t, closedOrders, 1)
|
||||
assert.Len(t, trades, 1)
|
||||
for _, o := range closedOrders {
|
||||
|
@ -417,7 +407,76 @@ func TestSimplePriceMatching_PlaceLimitOrder(t *testing.T) {
|
|||
assert.Equal(t, types.SideTypeSell, trade.Side)
|
||||
}
|
||||
|
||||
closedOrders, trades = engine.BuyToPrice(fixedpoint.NewFromFloat(9500.0))
|
||||
closedOrders, trades = engine.buyToPrice(fixedpoint.NewFromFloat(9500.0))
|
||||
assert.Len(t, closedOrders, 4)
|
||||
assert.Len(t, trades, 4)
|
||||
}
|
||||
|
||||
func Test_calculateNativeOrderFee(t *testing.T) {
|
||||
market := getTestMarket()
|
||||
|
||||
t.Run("sellOrder", func(t *testing.T) {
|
||||
order := types.Order{
|
||||
SubmitOrder: types.SubmitOrder{
|
||||
Symbol: market.Symbol,
|
||||
Side: types.SideTypeSell,
|
||||
Type: types.OrderTypeLimit,
|
||||
Quantity: fixedpoint.NewFromFloat(0.1),
|
||||
Price: fixedpoint.NewFromFloat(20000.0),
|
||||
TimeInForce: types.TimeInForceGTC,
|
||||
},
|
||||
}
|
||||
feeRate := fixedpoint.MustNewFromString("0.075%")
|
||||
fee, feeCurrency := calculateNativeOrderFee(&order, market, feeRate)
|
||||
assert.Equal(t, "1.5", fee.String())
|
||||
assert.Equal(t, "USDT", feeCurrency)
|
||||
})
|
||||
|
||||
t.Run("buyOrder", func(t *testing.T) {
|
||||
order := types.Order{
|
||||
SubmitOrder: types.SubmitOrder{
|
||||
Symbol: market.Symbol,
|
||||
Side: types.SideTypeBuy,
|
||||
Type: types.OrderTypeLimit,
|
||||
Quantity: fixedpoint.NewFromFloat(0.1),
|
||||
Price: fixedpoint.NewFromFloat(20000.0),
|
||||
TimeInForce: types.TimeInForceGTC,
|
||||
},
|
||||
}
|
||||
|
||||
feeRate := fixedpoint.MustNewFromString("0.075%")
|
||||
fee, feeCurrency := calculateNativeOrderFee(&order, market, feeRate)
|
||||
assert.Equal(t, "0.000075", fee.String())
|
||||
assert.Equal(t, "BTC", feeCurrency)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSimplePriceMatching_LimitTakerOrder(t *testing.T) {
|
||||
account := getTestAccount()
|
||||
market := getTestMarket()
|
||||
engine := &SimplePriceMatching{
|
||||
Account: account,
|
||||
Market: market,
|
||||
closedOrders: make(map[uint64]types.Order),
|
||||
LastPrice: fixedpoint.NewFromFloat(20000.0),
|
||||
}
|
||||
|
||||
closedOrder, trade, err := engine.PlaceOrder(newLimitOrder("BTCUSDT", types.SideTypeBuy, 21000.0, 1.0))
|
||||
assert.NoError(t, err)
|
||||
if assert.NotNil(t, closedOrder) {
|
||||
if assert.NotNil(t, trade) {
|
||||
assert.Equal(t, "20000", trade.Price.String())
|
||||
assert.False(t, trade.IsMaker, "should be taker")
|
||||
}
|
||||
}
|
||||
|
||||
closedOrder, trade, err = engine.PlaceOrder(newLimitOrder("BTCUSDT", types.SideTypeSell, 19000.0, 1.0))
|
||||
assert.NoError(t, err)
|
||||
if assert.NotNil(t, closedOrder) {
|
||||
assert.Equal(t, "19000", closedOrder.Price.String())
|
||||
if assert.NotNil(t, trade) {
|
||||
assert.Equal(t, "20000", trade.Price.String())
|
||||
assert.False(t, trade.IsMaker, "should be taker")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,9 +116,13 @@ type SubmitOrder struct {
|
|||
Side SideType `json:"side" db:"side"`
|
||||
Type OrderType `json:"orderType" db:"order_type"`
|
||||
|
||||
Quantity fixedpoint.Value `json:"quantity" db:"quantity"`
|
||||
Price fixedpoint.Value `json:"price" db:"price"`
|
||||
StopPrice fixedpoint.Value `json:"stopPrice,omitempty" db:"stop_price"`
|
||||
Quantity fixedpoint.Value `json:"quantity" db:"quantity"`
|
||||
Price fixedpoint.Value `json:"price" db:"price"`
|
||||
|
||||
// AveragePrice is only used in back-test currently
|
||||
AveragePrice fixedpoint.Value `json:"averagePrice"`
|
||||
|
||||
StopPrice fixedpoint.Value `json:"stopPrice,omitempty" db:"stop_price"`
|
||||
|
||||
Market Market `json:"-" db:"-"`
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user