mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 00:05:15 +00:00
backtest: move fee mode functions to fee.go
This commit is contained in:
parent
8cd646668a
commit
10ed706ed6
|
@ -36,10 +36,10 @@ import (
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/cache"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/cache"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/bbgo"
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
"github.com/c9s/bbgo/pkg/service"
|
"github.com/c9s/bbgo/pkg/service"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
@ -138,12 +138,15 @@ func (e *Exchange) addMatchingBook(symbol string, market types.Market) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Exchange) _addMatchingBook(symbol string, market types.Market) {
|
func (e *Exchange) _addMatchingBook(symbol string, market types.Market) {
|
||||||
e.matchingBooks[symbol] = &SimplePriceMatching{
|
matching := &SimplePriceMatching{
|
||||||
CurrentTime: e.currentTime,
|
currentTime: e.currentTime,
|
||||||
Account: e.account,
|
account: e.account,
|
||||||
Market: market,
|
Market: market,
|
||||||
closedOrders: make(map[uint64]types.Order),
|
closedOrders: make(map[uint64]types.Order),
|
||||||
|
feeModeFunction: getFeeModeFunction(e.config.FeeMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.matchingBooks[symbol] = matching
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Exchange) NewStream() types.Stream {
|
func (e *Exchange) NewStream() types.Stream {
|
||||||
|
@ -257,7 +260,7 @@ func (e *Exchange) QueryTicker(ctx context.Context, symbol string) (*types.Ticke
|
||||||
return nil, fmt.Errorf("matching engine is not initialized for symbol %s", symbol)
|
return nil, fmt.Errorf("matching engine is not initialized for symbol %s", symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
kline := matching.LastKLine
|
kline := matching.lastKLine
|
||||||
return &types.Ticker{
|
return &types.Ticker{
|
||||||
Time: kline.EndTime.Time(),
|
Time: kline.EndTime.Time(),
|
||||||
Volume: kline.Volume,
|
Volume: kline.Volume,
|
||||||
|
@ -382,7 +385,7 @@ func (e *Exchange) ConsumeKLine(k types.KLine) {
|
||||||
e.currentTime = kline1m.EndTime.Time()
|
e.currentTime = kline1m.EndTime.Time()
|
||||||
// here we generate trades and order updates
|
// here we generate trades and order updates
|
||||||
matching.processKLine(kline1m)
|
matching.processKLine(kline1m)
|
||||||
matching.NextKLine = &k
|
matching.nextKLine = &k
|
||||||
for _, kline := range matching.klineCache {
|
for _, kline := range matching.klineCache {
|
||||||
e.MarketDataStream.EmitKLineClosed(kline)
|
e.MarketDataStream.EmitKLineClosed(kline)
|
||||||
for _, h := range e.Src.Callbacks {
|
for _, h := range e.Src.Callbacks {
|
||||||
|
|
83
pkg/backtest/fee.go
Normal file
83
pkg/backtest/fee.go
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package backtest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FeeModeFunction func(order *types.Order, market *types.Market, feeRate fixedpoint.Value) (fee fixedpoint.Value, feeCurrency string)
|
||||||
|
|
||||||
|
func feeModeFunctionToken(order *types.Order, _ *types.Market, feeRate fixedpoint.Value) (fee fixedpoint.Value, feeCurrency string) {
|
||||||
|
quoteQuantity := order.Quantity.Mul(order.Price)
|
||||||
|
feeCurrency = FeeToken
|
||||||
|
fee = quoteQuantity.Mul(feeRate)
|
||||||
|
return fee, feeCurrency
|
||||||
|
}
|
||||||
|
|
||||||
|
func feeModeFunctionNative(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 feeModeFunctionQuote(order *types.Order, market *types.Market, feeRate fixedpoint.Value) (fee fixedpoint.Value, feeCurrency string) {
|
||||||
|
feeCurrency = market.QuoteCurrency
|
||||||
|
|
||||||
|
quoteQuantity := order.Quantity.Mul(order.Price)
|
||||||
|
switch order.Side {
|
||||||
|
|
||||||
|
case types.SideTypeBuy:
|
||||||
|
fee = quoteQuantity.Mul(feeRate)
|
||||||
|
|
||||||
|
case types.SideTypeSell:
|
||||||
|
fee = quoteQuantity.Mul(feeRate)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return fee, feeCurrency
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFeeModeFunction(feeMode bbgo.BackTestFeeMode) FeeModeFunction {
|
||||||
|
switch feeMode {
|
||||||
|
|
||||||
|
case bbgo.BackTestFeeModeNative:
|
||||||
|
return feeModeFunctionNative
|
||||||
|
|
||||||
|
case bbgo.BackTestFeeModeQuote:
|
||||||
|
return feeModeFunctionQuote
|
||||||
|
|
||||||
|
case bbgo.BackTestFeeModeToken:
|
||||||
|
return feeModeFunctionToken
|
||||||
|
|
||||||
|
default:
|
||||||
|
return feeModeFunctionNative
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -59,12 +59,14 @@ type SimplePriceMatching struct {
|
||||||
closedOrders map[uint64]types.Order
|
closedOrders map[uint64]types.Order
|
||||||
|
|
||||||
klineCache map[types.Interval]types.KLine
|
klineCache map[types.Interval]types.KLine
|
||||||
LastPrice fixedpoint.Value
|
lastPrice fixedpoint.Value
|
||||||
LastKLine types.KLine
|
lastKLine types.KLine
|
||||||
NextKLine *types.KLine
|
nextKLine *types.KLine
|
||||||
CurrentTime time.Time
|
currentTime time.Time
|
||||||
|
|
||||||
Account *types.Account
|
feeModeFunction FeeModeFunction
|
||||||
|
|
||||||
|
account *types.Account
|
||||||
|
|
||||||
tradeUpdateCallbacks []func(trade types.Trade)
|
tradeUpdateCallbacks []func(trade types.Trade)
|
||||||
orderUpdateCallbacks []func(order types.Order)
|
orderUpdateCallbacks []func(order types.Order)
|
||||||
|
@ -109,38 +111,38 @@ func (m *SimplePriceMatching) CancelOrder(o types.Order) (types.Order, error) {
|
||||||
|
|
||||||
switch o.Side {
|
switch o.Side {
|
||||||
case types.SideTypeBuy:
|
case types.SideTypeBuy:
|
||||||
if err := m.Account.UnlockBalance(m.Market.QuoteCurrency, o.Price.Mul(o.Quantity)); err != nil {
|
if err := m.account.UnlockBalance(m.Market.QuoteCurrency, o.Price.Mul(o.Quantity)); err != nil {
|
||||||
return o, err
|
return o, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case types.SideTypeSell:
|
case types.SideTypeSell:
|
||||||
if err := m.Account.UnlockBalance(m.Market.BaseCurrency, o.Quantity); err != nil {
|
if err := m.account.UnlockBalance(m.Market.BaseCurrency, o.Quantity); err != nil {
|
||||||
return o, err
|
return o, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
o.Status = types.OrderStatusCanceled
|
o.Status = types.OrderStatusCanceled
|
||||||
m.EmitOrderUpdate(o)
|
m.EmitOrderUpdate(o)
|
||||||
m.EmitBalanceUpdate(m.Account.Balances())
|
m.EmitBalanceUpdate(m.account.Balances())
|
||||||
return o, nil
|
return o, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlaceOrder returns the created order object, executed trade (if any) and 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) {
|
func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *types.Trade, error) {
|
||||||
if o.Type == types.OrderTypeMarket {
|
if o.Type == types.OrderTypeMarket {
|
||||||
if m.LastPrice.IsZero() {
|
if m.lastPrice.IsZero() {
|
||||||
panic("unexpected error: for market order, the last price can not be zero")
|
panic("unexpected error: for market order, the last price can not be zero")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isTaker := o.Type == types.OrderTypeMarket || isLimitTakerOrder(o, m.LastPrice)
|
isTaker := o.Type == types.OrderTypeMarket || isLimitTakerOrder(o, m.lastPrice)
|
||||||
|
|
||||||
// price for checking account balance, default price
|
// price for checking account balance, default price
|
||||||
price := o.Price
|
price := o.Price
|
||||||
|
|
||||||
switch o.Type {
|
switch o.Type {
|
||||||
case types.OrderTypeMarket:
|
case types.OrderTypeMarket:
|
||||||
price = m.Market.TruncatePrice(m.LastPrice)
|
price = m.Market.TruncatePrice(m.lastPrice)
|
||||||
|
|
||||||
case types.OrderTypeStopMarket:
|
case types.OrderTypeStopMarket:
|
||||||
// the actual price might be different.
|
// the actual price might be different.
|
||||||
|
@ -165,17 +167,17 @@ func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *ty
|
||||||
|
|
||||||
switch o.Side {
|
switch o.Side {
|
||||||
case types.SideTypeBuy:
|
case types.SideTypeBuy:
|
||||||
if err := m.Account.LockBalance(m.Market.QuoteCurrency, quoteQuantity); err != nil {
|
if err := m.account.LockBalance(m.Market.QuoteCurrency, quoteQuantity); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
case types.SideTypeSell:
|
case types.SideTypeSell:
|
||||||
if err := m.Account.LockBalance(m.Market.BaseCurrency, o.Quantity); err != nil {
|
if err := m.account.LockBalance(m.Market.BaseCurrency, o.Quantity); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.EmitBalanceUpdate(m.Account.Balances())
|
m.EmitBalanceUpdate(m.account.Balances())
|
||||||
|
|
||||||
// start from one
|
// start from one
|
||||||
orderID := incOrderID()
|
orderID := incOrderID()
|
||||||
|
@ -184,19 +186,19 @@ func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *ty
|
||||||
if isTaker {
|
if isTaker {
|
||||||
var price fixedpoint.Value
|
var price fixedpoint.Value
|
||||||
if order.Type == types.OrderTypeMarket {
|
if order.Type == types.OrderTypeMarket {
|
||||||
order.Price = m.Market.TruncatePrice(m.LastPrice)
|
order.Price = m.Market.TruncatePrice(m.lastPrice)
|
||||||
price = order.Price
|
price = order.Price
|
||||||
} else if order.Type == types.OrderTypeLimit {
|
} else if order.Type == types.OrderTypeLimit {
|
||||||
// if limit order's price is with the range of next kline
|
// if limit order's price is with the range of next kline
|
||||||
// we assume it will be traded as a maker trade, and is traded at its original price
|
// we assume it will be traded as a maker trade, and is traded at its original price
|
||||||
// TODO: if it is treated as a maker trade, fee should be specially handled
|
// TODO: if it is treated as a maker trade, fee should be specially handled
|
||||||
// otherwise, set NextKLine.Close(i.e., m.LastPrice) to be the taker traded price
|
// otherwise, set NextKLine.Close(i.e., m.LastPrice) to be the taker traded price
|
||||||
if m.NextKLine != nil && m.NextKLine.High.Compare(order.Price) > 0 && order.Side == types.SideTypeBuy {
|
if m.nextKLine != nil && m.nextKLine.High.Compare(order.Price) > 0 && order.Side == types.SideTypeBuy {
|
||||||
order.AveragePrice = order.Price
|
order.AveragePrice = order.Price
|
||||||
} else if m.NextKLine != nil && m.NextKLine.Low.Compare(order.Price) < 0 && order.Side == types.SideTypeSell {
|
} else if m.nextKLine != nil && m.nextKLine.Low.Compare(order.Price) < 0 && order.Side == types.SideTypeSell {
|
||||||
order.AveragePrice = order.Price
|
order.AveragePrice = order.Price
|
||||||
} else {
|
} else {
|
||||||
order.AveragePrice = m.Market.TruncatePrice(m.LastPrice)
|
order.AveragePrice = m.Market.TruncatePrice(m.lastPrice)
|
||||||
}
|
}
|
||||||
price = order.AveragePrice
|
price = order.AveragePrice
|
||||||
}
|
}
|
||||||
|
@ -223,10 +225,10 @@ func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *ty
|
||||||
// the executed price is lower than the given price, so we will use less quote currency to buy the base asset.
|
// 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)
|
amount := order.Price.Sub(order.AveragePrice).Mul(order.Quantity)
|
||||||
if amount.Sign() > 0 {
|
if amount.Sign() > 0 {
|
||||||
if err := m.Account.UnlockBalance(m.Market.QuoteCurrency, amount); err != nil {
|
if err := m.account.UnlockBalance(m.Market.QuoteCurrency, amount); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
m.EmitBalanceUpdate(m.Account.Balances())
|
m.EmitBalanceUpdate(m.account.Balances())
|
||||||
}
|
}
|
||||||
|
|
||||||
case types.SideTypeSell:
|
case types.SideTypeSell:
|
||||||
|
@ -234,8 +236,8 @@ func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *ty
|
||||||
// the executed price is higher than the given price, so we will get more quote currency back
|
// 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)
|
amount := order.AveragePrice.Sub(order.Price).Mul(order.Quantity)
|
||||||
if amount.Sign() > 0 {
|
if amount.Sign() > 0 {
|
||||||
m.Account.AddBalance(m.Market.QuoteCurrency, amount)
|
m.account.AddBalance(m.Market.QuoteCurrency, amount)
|
||||||
m.EmitBalanceUpdate(m.Account.Balances())
|
m.EmitBalanceUpdate(m.account.Balances())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,7 +275,7 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
|
||||||
var err error
|
var err error
|
||||||
// execute trade, update account balances
|
// execute trade, update account balances
|
||||||
if trade.IsBuyer {
|
if trade.IsBuyer {
|
||||||
err = m.Account.UseLockedBalance(m.Market.QuoteCurrency, trade.QuoteQuantity)
|
err = m.account.UseLockedBalance(m.Market.QuoteCurrency, trade.QuoteQuantity)
|
||||||
|
|
||||||
// here the fee currency is the base currency
|
// here the fee currency is the base currency
|
||||||
q := trade.Quantity
|
q := trade.Quantity
|
||||||
|
@ -281,16 +283,16 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
|
||||||
q = q.Sub(trade.Fee)
|
q = q.Sub(trade.Fee)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Account.AddBalance(m.Market.BaseCurrency, q)
|
m.account.AddBalance(m.Market.BaseCurrency, q)
|
||||||
} else {
|
} else {
|
||||||
err = m.Account.UseLockedBalance(m.Market.BaseCurrency, trade.Quantity)
|
err = m.account.UseLockedBalance(m.Market.BaseCurrency, trade.Quantity)
|
||||||
|
|
||||||
// here the fee currency is the quote currency
|
// here the fee currency is the quote currency
|
||||||
qq := trade.QuoteQuantity
|
qq := trade.QuoteQuantity
|
||||||
if trade.FeeCurrency == m.Market.QuoteCurrency {
|
if trade.FeeCurrency == m.Market.QuoteCurrency {
|
||||||
qq = qq.Sub(trade.Fee)
|
qq = qq.Sub(trade.Fee)
|
||||||
}
|
}
|
||||||
m.Account.AddBalance(m.Market.QuoteCurrency, qq)
|
m.account.AddBalance(m.Market.QuoteCurrency, qq)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -298,16 +300,16 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
|
||||||
}
|
}
|
||||||
|
|
||||||
m.EmitTradeUpdate(trade)
|
m.EmitTradeUpdate(trade)
|
||||||
m.EmitBalanceUpdate(m.Account.Balances())
|
m.EmitBalanceUpdate(m.account.Balances())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SimplePriceMatching) getFeeRate(isMaker bool) (feeRate fixedpoint.Value) {
|
func (m *SimplePriceMatching) getFeeRate(isMaker bool) (feeRate fixedpoint.Value) {
|
||||||
// BINANCE uses 0.1% for both maker and taker
|
// BINANCE uses 0.1% for both maker and taker
|
||||||
// MAX uses 0.050% for maker and 0.15% for taker
|
// MAX uses 0.050% for maker and 0.15% for taker
|
||||||
if isMaker {
|
if isMaker {
|
||||||
feeRate = m.Account.MakerFeeRate
|
feeRate = m.account.MakerFeeRate
|
||||||
} else {
|
} else {
|
||||||
feeRate = m.Account.TakerFeeRate
|
feeRate = m.account.TakerFeeRate
|
||||||
}
|
}
|
||||||
return feeRate
|
return feeRate
|
||||||
}
|
}
|
||||||
|
@ -320,15 +322,14 @@ func (m *SimplePriceMatching) newTradeFromOrder(order *types.Order, isMaker bool
|
||||||
var fee fixedpoint.Value
|
var fee fixedpoint.Value
|
||||||
var feeCurrency string
|
var feeCurrency string
|
||||||
|
|
||||||
if useFeeToken {
|
if m.feeModeFunction != nil {
|
||||||
feeCurrency = FeeToken
|
fee, feeCurrency = m.feeModeFunction(order, &m.Market, feeRate)
|
||||||
fee = quoteQuantity.Mul(feeRate)
|
|
||||||
} else {
|
} else {
|
||||||
fee, feeCurrency = calculateNativeOrderFee(order, m.Market, feeRate)
|
fee, feeCurrency = feeModeFunctionQuote(order, &m.Market, feeRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update order time
|
// update order time
|
||||||
order.UpdateTime = types.Time(m.CurrentTime)
|
order.UpdateTime = types.Time(m.currentTime)
|
||||||
|
|
||||||
var id = incTradeID()
|
var id = incTradeID()
|
||||||
return types.Trade{
|
return types.Trade{
|
||||||
|
@ -342,7 +343,7 @@ func (m *SimplePriceMatching) newTradeFromOrder(order *types.Order, isMaker bool
|
||||||
Side: order.Side,
|
Side: order.Side,
|
||||||
IsBuyer: order.Side == types.SideTypeBuy,
|
IsBuyer: order.Side == types.SideTypeBuy,
|
||||||
IsMaker: isMaker,
|
IsMaker: isMaker,
|
||||||
Time: types.Time(m.CurrentTime),
|
Time: types.Time(m.currentTime),
|
||||||
Fee: fee,
|
Fee: fee,
|
||||||
FeeCurrency: feeCurrency,
|
FeeCurrency: feeCurrency,
|
||||||
}
|
}
|
||||||
|
@ -458,7 +459,7 @@ func (m *SimplePriceMatching) buyToPrice(price fixedpoint.Value) (closedOrders [
|
||||||
}
|
}
|
||||||
|
|
||||||
m.askOrders = askOrders
|
m.askOrders = askOrders
|
||||||
m.LastPrice = price
|
m.lastPrice = price
|
||||||
|
|
||||||
for i := range closedOrders {
|
for i := range closedOrders {
|
||||||
o := closedOrders[i]
|
o := closedOrders[i]
|
||||||
|
@ -587,7 +588,7 @@ func (m *SimplePriceMatching) sellToPrice(price fixedpoint.Value) (closedOrders
|
||||||
}
|
}
|
||||||
|
|
||||||
m.bidOrders = bidOrders
|
m.bidOrders = bidOrders
|
||||||
m.LastPrice = price
|
m.lastPrice = price
|
||||||
|
|
||||||
for i := range closedOrders {
|
for i := range closedOrders {
|
||||||
o := closedOrders[i]
|
o := closedOrders[i]
|
||||||
|
@ -631,12 +632,12 @@ func (m *SimplePriceMatching) getOrder(orderID uint64) (types.Order, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SimplePriceMatching) processKLine(kline types.KLine) {
|
func (m *SimplePriceMatching) processKLine(kline types.KLine) {
|
||||||
m.CurrentTime = kline.EndTime.Time()
|
m.currentTime = kline.EndTime.Time()
|
||||||
|
|
||||||
if m.LastPrice.IsZero() {
|
if m.lastPrice.IsZero() {
|
||||||
m.LastPrice = kline.Open
|
m.lastPrice = kline.Open
|
||||||
} else {
|
} else {
|
||||||
if m.LastPrice.Compare(kline.Open) > 0 {
|
if m.lastPrice.Compare(kline.Open) > 0 {
|
||||||
m.sellToPrice(kline.Open)
|
m.sellToPrice(kline.Open)
|
||||||
} else {
|
} else {
|
||||||
m.buyToPrice(kline.Open)
|
m.buyToPrice(kline.Open)
|
||||||
|
@ -669,12 +670,12 @@ func (m *SimplePriceMatching) processKLine(kline types.KLine) {
|
||||||
m.buyToPrice(kline.Close)
|
m.buyToPrice(kline.Close)
|
||||||
}
|
}
|
||||||
default: // no trade up or down
|
default: // no trade up or down
|
||||||
if m.LastPrice.IsZero() {
|
if m.lastPrice.IsZero() {
|
||||||
m.buyToPrice(kline.Close)
|
m.buyToPrice(kline.Close)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.LastKLine = kline
|
m.lastKLine = kline
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SimplePriceMatching) newOrder(o types.SubmitOrder, orderID uint64) types.Order {
|
func (m *SimplePriceMatching) newOrder(o types.SubmitOrder, orderID uint64) types.Order {
|
||||||
|
@ -685,27 +686,11 @@ func (m *SimplePriceMatching) newOrder(o types.SubmitOrder, orderID uint64) type
|
||||||
Status: types.OrderStatusNew,
|
Status: types.OrderStatusNew,
|
||||||
ExecutedQuantity: fixedpoint.Zero,
|
ExecutedQuantity: fixedpoint.Zero,
|
||||||
IsWorking: true,
|
IsWorking: true,
|
||||||
CreationTime: types.Time(m.CurrentTime),
|
CreationTime: types.Time(m.currentTime),
|
||||||
UpdateTime: types.Time(m.CurrentTime),
|
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 isTakerOrder(o types.Order) bool {
|
func isTakerOrder(o types.Order) bool {
|
||||||
if o.AveragePrice.IsZero() {
|
if o.AveragePrice.IsZero() {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -44,11 +44,11 @@ func TestSimplePriceMatching_orderUpdate(t *testing.T) {
|
||||||
|
|
||||||
t1 := time.Date(2021, 7, 1, 0, 0, 0, 0, time.UTC)
|
t1 := time.Date(2021, 7, 1, 0, 0, 0, 0, time.UTC)
|
||||||
engine := &SimplePriceMatching{
|
engine := &SimplePriceMatching{
|
||||||
Account: account,
|
account: account,
|
||||||
Market: market,
|
Market: market,
|
||||||
CurrentTime: t1,
|
currentTime: t1,
|
||||||
closedOrders: make(map[uint64]types.Order),
|
closedOrders: make(map[uint64]types.Order),
|
||||||
LastPrice: fixedpoint.NewFromFloat(25000),
|
lastPrice: fixedpoint.NewFromFloat(25000),
|
||||||
}
|
}
|
||||||
|
|
||||||
orderUpdateCnt := 0
|
orderUpdateCnt := 0
|
||||||
|
@ -97,11 +97,11 @@ func TestSimplePriceMatching_CancelOrder(t *testing.T) {
|
||||||
market := getTestMarket()
|
market := getTestMarket()
|
||||||
t1 := time.Date(2021, 7, 1, 0, 0, 0, 0, time.UTC)
|
t1 := time.Date(2021, 7, 1, 0, 0, 0, 0, time.UTC)
|
||||||
engine := &SimplePriceMatching{
|
engine := &SimplePriceMatching{
|
||||||
Account: account,
|
account: account,
|
||||||
Market: market,
|
Market: market,
|
||||||
CurrentTime: t1,
|
currentTime: t1,
|
||||||
closedOrders: make(map[uint64]types.Order),
|
closedOrders: make(map[uint64]types.Order),
|
||||||
LastPrice: fixedpoint.NewFromFloat(30000.0),
|
lastPrice: fixedpoint.NewFromFloat(30000.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
createdOrder1, trade1, err := engine.PlaceOrder(newLimitOrder("BTCUSDT", types.SideTypeBuy, 20000.0, 0.1))
|
createdOrder1, trade1, err := engine.PlaceOrder(newLimitOrder("BTCUSDT", types.SideTypeBuy, 20000.0, 0.1))
|
||||||
|
@ -139,11 +139,11 @@ func TestSimplePriceMatching_processKLine(t *testing.T) {
|
||||||
|
|
||||||
t1 := time.Date(2021, 7, 1, 0, 0, 0, 0, time.UTC)
|
t1 := time.Date(2021, 7, 1, 0, 0, 0, 0, time.UTC)
|
||||||
engine := &SimplePriceMatching{
|
engine := &SimplePriceMatching{
|
||||||
Account: account,
|
account: account,
|
||||||
Market: market,
|
Market: market,
|
||||||
CurrentTime: t1,
|
currentTime: t1,
|
||||||
closedOrders: make(map[uint64]types.Order),
|
closedOrders: make(map[uint64]types.Order),
|
||||||
LastPrice: fixedpoint.NewFromFloat(30000.0),
|
lastPrice: fixedpoint.NewFromFloat(30000.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i <= 5; i++ {
|
for i := 0; i <= 5; i++ {
|
||||||
|
@ -216,10 +216,10 @@ func TestSimplePriceMatching_LimitBuyTakerOrder(t *testing.T) {
|
||||||
account := getTestAccount()
|
account := getTestAccount()
|
||||||
market := getTestMarket()
|
market := getTestMarket()
|
||||||
engine := &SimplePriceMatching{
|
engine := &SimplePriceMatching{
|
||||||
Account: account,
|
account: account,
|
||||||
Market: market,
|
Market: market,
|
||||||
closedOrders: make(map[uint64]types.Order),
|
closedOrders: make(map[uint64]types.Order),
|
||||||
LastPrice: fixedpoint.NewFromFloat(19000.0),
|
lastPrice: fixedpoint.NewFromFloat(19000.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
takerOrder := types.SubmitOrder{
|
takerOrder := types.SubmitOrder{
|
||||||
|
@ -256,10 +256,10 @@ func TestSimplePriceMatching_StopLimitOrderBuy(t *testing.T) {
|
||||||
account := getTestAccount()
|
account := getTestAccount()
|
||||||
market := getTestMarket()
|
market := getTestMarket()
|
||||||
engine := &SimplePriceMatching{
|
engine := &SimplePriceMatching{
|
||||||
Account: account,
|
account: account,
|
||||||
Market: market,
|
Market: market,
|
||||||
closedOrders: make(map[uint64]types.Order),
|
closedOrders: make(map[uint64]types.Order),
|
||||||
LastPrice: fixedpoint.NewFromFloat(19000.0),
|
lastPrice: fixedpoint.NewFromFloat(19000.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
stopBuyOrder := types.SubmitOrder{
|
stopBuyOrder := types.SubmitOrder{
|
||||||
|
@ -299,7 +299,7 @@ func TestSimplePriceMatching_StopLimitOrderBuy(t *testing.T) {
|
||||||
assert.Equal(t, "21001", trades[0].Price.String())
|
assert.Equal(t, "21001", trades[0].Price.String())
|
||||||
assert.Equal(t, "22000", closedOrders[0].Price.String(), "order.Price should not be adjusted")
|
assert.Equal(t, "22000", closedOrders[0].Price.String(), "order.Price should not be adjusted")
|
||||||
|
|
||||||
assert.Equal(t, fixedpoint.NewFromFloat(21001.0).String(), engine.LastPrice.String())
|
assert.Equal(t, fixedpoint.NewFromFloat(21001.0).String(), engine.lastPrice.String())
|
||||||
|
|
||||||
stopOrder2 := types.SubmitOrder{
|
stopOrder2 := types.SubmitOrder{
|
||||||
Symbol: market.Symbol,
|
Symbol: market.Symbol,
|
||||||
|
@ -326,10 +326,10 @@ func TestSimplePriceMatching_StopLimitOrderSell(t *testing.T) {
|
||||||
account := getTestAccount()
|
account := getTestAccount()
|
||||||
market := getTestMarket()
|
market := getTestMarket()
|
||||||
engine := &SimplePriceMatching{
|
engine := &SimplePriceMatching{
|
||||||
Account: account,
|
account: account,
|
||||||
Market: market,
|
Market: market,
|
||||||
closedOrders: make(map[uint64]types.Order),
|
closedOrders: make(map[uint64]types.Order),
|
||||||
LastPrice: fixedpoint.NewFromFloat(22000.0),
|
lastPrice: fixedpoint.NewFromFloat(22000.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
stopSellOrder := types.SubmitOrder{
|
stopSellOrder := types.SubmitOrder{
|
||||||
|
@ -370,7 +370,7 @@ func TestSimplePriceMatching_StopLimitOrderSell(t *testing.T) {
|
||||||
assert.Equal(t, types.OrderTypeLimit, closedOrders[0].Type)
|
assert.Equal(t, types.OrderTypeLimit, closedOrders[0].Type)
|
||||||
assert.Equal(t, "20000", closedOrders[0].Price.String(), "limit order price should not be changed")
|
assert.Equal(t, "20000", closedOrders[0].Price.String(), "limit order price should not be changed")
|
||||||
assert.Equal(t, "20990", trades[0].Price.String())
|
assert.Equal(t, "20990", trades[0].Price.String())
|
||||||
assert.Equal(t, "20990", engine.LastPrice.String())
|
assert.Equal(t, "20990", engine.lastPrice.String())
|
||||||
|
|
||||||
// place a stop limit sell order with a higher price than the current price
|
// place a stop limit sell order with a higher price than the current price
|
||||||
stopOrder2 := types.SubmitOrder{
|
stopOrder2 := types.SubmitOrder{
|
||||||
|
@ -395,7 +395,7 @@ func TestSimplePriceMatching_StopLimitOrderSell(t *testing.T) {
|
||||||
assert.Equal(t, types.OrderStatusFilled, closedOrders[0].Status)
|
assert.Equal(t, types.OrderStatusFilled, closedOrders[0].Status)
|
||||||
assert.Equal(t, types.OrderTypeLimit, closedOrders[0].Type)
|
assert.Equal(t, types.OrderTypeLimit, closedOrders[0].Type)
|
||||||
assert.Equal(t, "21000", trades[0].Price.String(), "trade price should be the kline price not the order price")
|
assert.Equal(t, "21000", trades[0].Price.String(), "trade price should be the kline price not the order price")
|
||||||
assert.Equal(t, "21000", engine.LastPrice.String(), "engine last price should be updated correctly")
|
assert.Equal(t, "21000", engine.lastPrice.String(), "engine last price should be updated correctly")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,10 +403,10 @@ func TestSimplePriceMatching_StopMarketOrderSell(t *testing.T) {
|
||||||
account := getTestAccount()
|
account := getTestAccount()
|
||||||
market := getTestMarket()
|
market := getTestMarket()
|
||||||
engine := &SimplePriceMatching{
|
engine := &SimplePriceMatching{
|
||||||
Account: account,
|
account: account,
|
||||||
Market: market,
|
Market: market,
|
||||||
closedOrders: make(map[uint64]types.Order),
|
closedOrders: make(map[uint64]types.Order),
|
||||||
LastPrice: fixedpoint.NewFromFloat(22000.0),
|
lastPrice: fixedpoint.NewFromFloat(22000.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
stopOrder := types.SubmitOrder{
|
stopOrder := types.SubmitOrder{
|
||||||
|
@ -440,7 +440,7 @@ func TestSimplePriceMatching_PlaceLimitOrder(t *testing.T) {
|
||||||
account := getTestAccount()
|
account := getTestAccount()
|
||||||
market := getTestMarket()
|
market := getTestMarket()
|
||||||
engine := &SimplePriceMatching{
|
engine := &SimplePriceMatching{
|
||||||
Account: account,
|
account: account,
|
||||||
Market: market,
|
Market: market,
|
||||||
closedOrders: make(map[uint64]types.Order),
|
closedOrders: make(map[uint64]types.Order),
|
||||||
}
|
}
|
||||||
|
@ -540,10 +540,10 @@ func TestSimplePriceMatching_LimitTakerOrder(t *testing.T) {
|
||||||
account := getTestAccount()
|
account := getTestAccount()
|
||||||
market := getTestMarket()
|
market := getTestMarket()
|
||||||
engine := &SimplePriceMatching{
|
engine := &SimplePriceMatching{
|
||||||
Account: account,
|
account: account,
|
||||||
Market: market,
|
Market: market,
|
||||||
closedOrders: make(map[uint64]types.Order),
|
closedOrders: make(map[uint64]types.Order),
|
||||||
LastPrice: fixedpoint.NewFromFloat(20000.0),
|
lastPrice: fixedpoint.NewFromFloat(20000.0),
|
||||||
}
|
}
|
||||||
|
|
||||||
closedOrder, trade, err := engine.PlaceOrder(newLimitOrder("BTCUSDT", types.SideTypeBuy, 21000.0, 1.0))
|
closedOrder, trade, err := engine.PlaceOrder(newLimitOrder("BTCUSDT", types.SideTypeBuy, 21000.0, 1.0))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user