Merge pull request #529 from zenixls2/fix/backtest_price

fix: rename EVWMP to VWEMP, fix backtesting fee
This commit is contained in:
Yo-An Lin 2022-04-16 00:46:55 +08:00 committed by GitHub
commit cb4500618e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 33 deletions

View File

@ -12,12 +12,6 @@ import (
"github.com/c9s/bbgo/pkg/types"
)
// DefaultFeeRate set the fee rate for most cases
// BINANCE uses 0.1% for both maker and taker
// for BNB holders, it's 0.075% for both maker and taker
// MAX uses 0.050% for maker and 0.15% for taker
var DefaultFeeRate = fixedpoint.NewFromFloat(0.075 * 0.01)
var orderID uint64 = 1
var tradeID uint64 = 1
@ -45,9 +39,6 @@ type SimplePriceMatching struct {
Account *types.Account
MakerFeeRate fixedpoint.Value `json:"makerFeeRate"`
TakerFeeRate fixedpoint.Value `json:"takerFeeRate"`
tradeUpdateCallbacks []func(trade types.Trade)
orderUpdateCallbacks []func(order types.Order)
balanceUpdateCallbacks []func(balances types.BalanceMap)
@ -192,11 +183,11 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
if trade.IsBuyer {
err = m.Account.UseLockedBalance(m.Market.QuoteCurrency, trade.Price.Mul(trade.Quantity))
m.Account.AddBalance(m.Market.BaseCurrency, trade.Quantity)
m.Account.AddBalance(m.Market.BaseCurrency, trade.Quantity.Sub(trade.Fee.Div(trade.Price)))
} else {
err = m.Account.UseLockedBalance(m.Market.BaseCurrency, trade.Quantity)
m.Account.AddBalance(m.Market.QuoteCurrency, trade.Quantity.Mul(trade.Price))
m.Account.AddBalance(m.Market.QuoteCurrency, trade.Quantity.Mul(trade.Price).Sub(trade.Fee))
}
if err != nil {
@ -211,15 +202,11 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
func (m *SimplePriceMatching) newTradeFromOrder(order types.Order, isMaker bool) types.Trade {
// BINANCE uses 0.1% for both maker and taker
// MAX uses 0.050% for maker and 0.15% for taker
var feeRate = DefaultFeeRate
var feeRate fixedpoint.Value
if isMaker {
if m.MakerFeeRate.Sign() > 0 {
feeRate = m.MakerFeeRate
}
feeRate = m.Account.MakerFeeRate
} else {
if m.TakerFeeRate.Sign() > 0 {
feeRate = m.TakerFeeRate
}
feeRate = m.Account.TakerFeeRate
}
price := order.Price
@ -230,7 +217,6 @@ func (m *SimplePriceMatching) newTradeFromOrder(order types.Order, isMaker bool)
}
price = m.LastPrice
}
var fee fixedpoint.Value
@ -297,6 +283,9 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
// is it a taker order?
if price.Compare(o.Price) >= 0 {
if o.Price.Compare(m.LastKLine.Low) < 0 {
o.Price = m.LastKLine.Low
}
o.ExecutedQuantity = o.Quantity
o.Status = types.OrderStatusFilled
closedOrders = append(closedOrders, o)
@ -307,6 +296,9 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
case types.OrderTypeLimit, types.OrderTypeLimitMaker:
if price.Compare(o.Price) >= 0 {
if o.Price.Compare(m.LastKLine.Low) < 0 {
o.Price = m.LastKLine.Low
}
o.ExecutedQuantity = o.Quantity
o.Status = types.OrderStatusFilled
closedOrders = append(closedOrders, o)
@ -358,6 +350,9 @@ func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders
o.Type = types.OrderTypeLimit
if sellPrice.Compare(o.Price) <= 0 {
if o.Price.Compare(m.LastKLine.High) > 0 {
o.Price = m.LastKLine.High
}
o.ExecutedQuantity = o.Quantity
o.Status = types.OrderStatusFilled
closedOrders = append(closedOrders, o)
@ -370,6 +365,9 @@ func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders
case types.OrderTypeLimit, types.OrderTypeLimitMaker:
if sellPrice.Compare(o.Price) <= 0 {
if o.Price.Compare(m.LastKLine.High) > 0 {
o.Price = m.LastKLine.High
}
o.ExecutedQuantity = o.Quantity
o.Status = types.OrderStatusFilled
closedOrders = append(closedOrders, o)

View File

@ -17,6 +17,12 @@ import (
"github.com/c9s/bbgo/pkg/types"
)
// DefaultFeeRate set the fee rate for most cases
// BINANCE uses 0.1% for both maker and taker
// for BNB holders, it's 0.075% for both maker and taker
// MAX uses 0.050% for maker and 0.15% for taker
var DefaultFeeRate = fixedpoint.NewFromFloat(0.075 * 0.01)
type PnLReporterConfig struct {
AverageCostBySymbols datatype.StringSlice `json:"averageCostBySymbols" yaml:"averageCostBySymbols"`
Of datatype.StringSlice `json:"of" yaml:"of"`
@ -106,10 +112,30 @@ type Backtest struct {
type BacktestAccount struct {
// TODO: MakerFeeRate should replace the commission fields
MakerFeeRate fixedpoint.Value `json:"makerFeeRate"`
TakerFeeRate fixedpoint.Value `json:"takerFeeRate"`
MakerFeeRate fixedpoint.Value `json:"makerFeeRate,omitempty" yaml:"makerFeeRate,omitempty"`
TakerFeeRate fixedpoint.Value `json:"takerFeeRate,omitempty" yaml:"takerFeeRate,omitempty"`
Balances BacktestAccountBalanceMap `json:"balances" yaml:"balances"`
Balances BacktestAccountBalanceMap `json:"balances" yaml:"balances"`
}
type BA BacktestAccount
func (b *BacktestAccount) UnmarshalYAML(value *yaml.Node) error {
bb := &BA{MakerFeeRate: DefaultFeeRate, TakerFeeRate: DefaultFeeRate}
if err := value.Decode(bb); err != nil {
return err
}
*b = BacktestAccount(*bb)
return nil
}
func (b *BacktestAccount) UnmarshalJSON(input []byte) error {
bb := &BA{MakerFeeRate: DefaultFeeRate, TakerFeeRate: DefaultFeeRate}
if err := json.Unmarshal(input, bb); err != nil {
return err
}
*b = BacktestAccount(*bb)
return nil
}
type BacktestAccountBalanceMap map[string]fixedpoint.Value

View File

@ -66,16 +66,16 @@ type UpdatableSeries interface {
Update(value float64)
}
type EVWMA struct {
type VWEMA struct {
PV UpdatableSeries
V UpdatableSeries
}
func (inc *EVWMA) Last() float64 {
func (inc *VWEMA) Last() float64 {
return inc.PV.Last() / inc.V.Last()
}
func (inc *EVWMA) Index(i int) float64 {
func (inc *VWEMA) Index(i int) float64 {
if i >= inc.PV.Length() {
return 0
}
@ -86,7 +86,7 @@ func (inc *EVWMA) Index(i int) float64 {
return inc.PV.Index(i) / vi
}
func (inc *EVWMA) Length() int {
func (inc *VWEMA) Length() int {
pvl := inc.PV.Length()
vl := inc.V.Length()
if pvl < vl {
@ -95,12 +95,12 @@ func (inc *EVWMA) Length() int {
return vl
}
func (inc *EVWMA) Update(kline types.KLine) {
func (inc *VWEMA) Update(kline types.KLine) {
inc.PV.Update(kline.Close.Mul(kline.Volume).Float64())
inc.V.Update(kline.Volume.Float64())
}
func (inc *EVWMA) UpdateVal(price float64, vol float64) {
func (inc *VWEMA) UpdateVal(price float64, vol float64) {
inc.PV.Update(price * vol)
inc.V.Update(vol)
}
@ -247,11 +247,11 @@ func (s *Strategy) SetupIndicators() {
s.ma5 = sma5
s.ma34 = sma34
} else {
evwma5 := &EVWMA{
evwma5 := &VWEMA{
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 5}},
V: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 5}},
}
evwma34 := &EVWMA{
evwma34 := &VWEMA{
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 34}},
V: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 34}},
}
@ -289,11 +289,11 @@ func (s *Strategy) SetupIndicators() {
s.ma5 = indicatorSet.SMA(types.IntervalWindow{s.Interval, 5})
s.ma34 = indicatorSet.SMA(types.IntervalWindow{s.Interval, 34})
} else {
evwma5 := &EVWMA{
evwma5 := &VWEMA{
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 5}},
V: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 5}},
}
evwma34 := &EVWMA{
evwma34 := &VWEMA{
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 34}},
V: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 34}},
}
@ -354,7 +354,7 @@ func (s *Strategy) SetupIndicators() {
})
s.ewoSignal = sig
} else {
sig := &EVWMA{
sig := &VWEMA{
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, s.SignalWindow}},
V: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, s.SignalWindow}},
}