mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
Merge pull request #529 from zenixls2/fix/backtest_price
fix: rename EVWMP to VWEMP, fix backtesting fee
This commit is contained in:
commit
cb4500618e
|
@ -12,12 +12,6 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"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 orderID uint64 = 1
|
||||||
var tradeID uint64 = 1
|
var tradeID uint64 = 1
|
||||||
|
|
||||||
|
@ -45,9 +39,6 @@ type SimplePriceMatching struct {
|
||||||
|
|
||||||
Account *types.Account
|
Account *types.Account
|
||||||
|
|
||||||
MakerFeeRate fixedpoint.Value `json:"makerFeeRate"`
|
|
||||||
TakerFeeRate fixedpoint.Value `json:"takerFeeRate"`
|
|
||||||
|
|
||||||
tradeUpdateCallbacks []func(trade types.Trade)
|
tradeUpdateCallbacks []func(trade types.Trade)
|
||||||
orderUpdateCallbacks []func(order types.Order)
|
orderUpdateCallbacks []func(order types.Order)
|
||||||
balanceUpdateCallbacks []func(balances types.BalanceMap)
|
balanceUpdateCallbacks []func(balances types.BalanceMap)
|
||||||
|
@ -192,11 +183,11 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
|
||||||
if trade.IsBuyer {
|
if trade.IsBuyer {
|
||||||
err = m.Account.UseLockedBalance(m.Market.QuoteCurrency, trade.Price.Mul(trade.Quantity))
|
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 {
|
} else {
|
||||||
err = m.Account.UseLockedBalance(m.Market.BaseCurrency, trade.Quantity)
|
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 {
|
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 {
|
func (m *SimplePriceMatching) newTradeFromOrder(order types.Order, isMaker bool) types.Trade {
|
||||||
// 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
|
||||||
var feeRate = DefaultFeeRate
|
var feeRate fixedpoint.Value
|
||||||
if isMaker {
|
if isMaker {
|
||||||
if m.MakerFeeRate.Sign() > 0 {
|
feeRate = m.Account.MakerFeeRate
|
||||||
feeRate = m.MakerFeeRate
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if m.TakerFeeRate.Sign() > 0 {
|
feeRate = m.Account.TakerFeeRate
|
||||||
feeRate = m.TakerFeeRate
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
price := order.Price
|
price := order.Price
|
||||||
|
@ -230,7 +217,6 @@ func (m *SimplePriceMatching) newTradeFromOrder(order types.Order, isMaker bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
price = m.LastPrice
|
price = m.LastPrice
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var fee fixedpoint.Value
|
var fee fixedpoint.Value
|
||||||
|
@ -297,6 +283,9 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
|
||||||
|
|
||||||
// is it a taker order?
|
// is it a taker order?
|
||||||
if price.Compare(o.Price) >= 0 {
|
if price.Compare(o.Price) >= 0 {
|
||||||
|
if o.Price.Compare(m.LastKLine.Low) < 0 {
|
||||||
|
o.Price = m.LastKLine.Low
|
||||||
|
}
|
||||||
o.ExecutedQuantity = o.Quantity
|
o.ExecutedQuantity = o.Quantity
|
||||||
o.Status = types.OrderStatusFilled
|
o.Status = types.OrderStatusFilled
|
||||||
closedOrders = append(closedOrders, o)
|
closedOrders = append(closedOrders, o)
|
||||||
|
@ -307,6 +296,9 @@ func (m *SimplePriceMatching) BuyToPrice(price fixedpoint.Value) (closedOrders [
|
||||||
|
|
||||||
case types.OrderTypeLimit, types.OrderTypeLimitMaker:
|
case types.OrderTypeLimit, types.OrderTypeLimitMaker:
|
||||||
if price.Compare(o.Price) >= 0 {
|
if price.Compare(o.Price) >= 0 {
|
||||||
|
if o.Price.Compare(m.LastKLine.Low) < 0 {
|
||||||
|
o.Price = m.LastKLine.Low
|
||||||
|
}
|
||||||
o.ExecutedQuantity = o.Quantity
|
o.ExecutedQuantity = o.Quantity
|
||||||
o.Status = types.OrderStatusFilled
|
o.Status = types.OrderStatusFilled
|
||||||
closedOrders = append(closedOrders, o)
|
closedOrders = append(closedOrders, o)
|
||||||
|
@ -358,6 +350,9 @@ func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders
|
||||||
o.Type = types.OrderTypeLimit
|
o.Type = types.OrderTypeLimit
|
||||||
|
|
||||||
if sellPrice.Compare(o.Price) <= 0 {
|
if sellPrice.Compare(o.Price) <= 0 {
|
||||||
|
if o.Price.Compare(m.LastKLine.High) > 0 {
|
||||||
|
o.Price = m.LastKLine.High
|
||||||
|
}
|
||||||
o.ExecutedQuantity = o.Quantity
|
o.ExecutedQuantity = o.Quantity
|
||||||
o.Status = types.OrderStatusFilled
|
o.Status = types.OrderStatusFilled
|
||||||
closedOrders = append(closedOrders, o)
|
closedOrders = append(closedOrders, o)
|
||||||
|
@ -370,6 +365,9 @@ func (m *SimplePriceMatching) SellToPrice(price fixedpoint.Value) (closedOrders
|
||||||
|
|
||||||
case types.OrderTypeLimit, types.OrderTypeLimitMaker:
|
case types.OrderTypeLimit, types.OrderTypeLimitMaker:
|
||||||
if sellPrice.Compare(o.Price) <= 0 {
|
if sellPrice.Compare(o.Price) <= 0 {
|
||||||
|
if o.Price.Compare(m.LastKLine.High) > 0 {
|
||||||
|
o.Price = m.LastKLine.High
|
||||||
|
}
|
||||||
o.ExecutedQuantity = o.Quantity
|
o.ExecutedQuantity = o.Quantity
|
||||||
o.Status = types.OrderStatusFilled
|
o.Status = types.OrderStatusFilled
|
||||||
closedOrders = append(closedOrders, o)
|
closedOrders = append(closedOrders, o)
|
||||||
|
|
|
@ -17,6 +17,12 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"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 {
|
type PnLReporterConfig struct {
|
||||||
AverageCostBySymbols datatype.StringSlice `json:"averageCostBySymbols" yaml:"averageCostBySymbols"`
|
AverageCostBySymbols datatype.StringSlice `json:"averageCostBySymbols" yaml:"averageCostBySymbols"`
|
||||||
Of datatype.StringSlice `json:"of" yaml:"of"`
|
Of datatype.StringSlice `json:"of" yaml:"of"`
|
||||||
|
@ -106,10 +112,30 @@ type Backtest struct {
|
||||||
|
|
||||||
type BacktestAccount struct {
|
type BacktestAccount struct {
|
||||||
// TODO: MakerFeeRate should replace the commission fields
|
// TODO: MakerFeeRate should replace the commission fields
|
||||||
MakerFeeRate fixedpoint.Value `json:"makerFeeRate"`
|
MakerFeeRate fixedpoint.Value `json:"makerFeeRate,omitempty" yaml:"makerFeeRate,omitempty"`
|
||||||
TakerFeeRate fixedpoint.Value `json:"takerFeeRate"`
|
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
|
type BacktestAccountBalanceMap map[string]fixedpoint.Value
|
||||||
|
|
|
@ -66,16 +66,16 @@ type UpdatableSeries interface {
|
||||||
Update(value float64)
|
Update(value float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
type EVWMA struct {
|
type VWEMA struct {
|
||||||
PV UpdatableSeries
|
PV UpdatableSeries
|
||||||
V UpdatableSeries
|
V UpdatableSeries
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *EVWMA) Last() float64 {
|
func (inc *VWEMA) Last() float64 {
|
||||||
return inc.PV.Last() / inc.V.Last()
|
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() {
|
if i >= inc.PV.Length() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func (inc *EVWMA) Index(i int) float64 {
|
||||||
return inc.PV.Index(i) / vi
|
return inc.PV.Index(i) / vi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *EVWMA) Length() int {
|
func (inc *VWEMA) Length() int {
|
||||||
pvl := inc.PV.Length()
|
pvl := inc.PV.Length()
|
||||||
vl := inc.V.Length()
|
vl := inc.V.Length()
|
||||||
if pvl < vl {
|
if pvl < vl {
|
||||||
|
@ -95,12 +95,12 @@ func (inc *EVWMA) Length() int {
|
||||||
return vl
|
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.PV.Update(kline.Close.Mul(kline.Volume).Float64())
|
||||||
inc.V.Update(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.PV.Update(price * vol)
|
||||||
inc.V.Update(vol)
|
inc.V.Update(vol)
|
||||||
}
|
}
|
||||||
|
@ -247,11 +247,11 @@ func (s *Strategy) SetupIndicators() {
|
||||||
s.ma5 = sma5
|
s.ma5 = sma5
|
||||||
s.ma34 = sma34
|
s.ma34 = sma34
|
||||||
} else {
|
} else {
|
||||||
evwma5 := &EVWMA{
|
evwma5 := &VWEMA{
|
||||||
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 5}},
|
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 5}},
|
||||||
V: &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}},
|
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 34}},
|
||||||
V: &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.ma5 = indicatorSet.SMA(types.IntervalWindow{s.Interval, 5})
|
||||||
s.ma34 = indicatorSet.SMA(types.IntervalWindow{s.Interval, 34})
|
s.ma34 = indicatorSet.SMA(types.IntervalWindow{s.Interval, 34})
|
||||||
} else {
|
} else {
|
||||||
evwma5 := &EVWMA{
|
evwma5 := &VWEMA{
|
||||||
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 5}},
|
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 5}},
|
||||||
V: &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}},
|
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, 34}},
|
||||||
V: &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
|
s.ewoSignal = sig
|
||||||
} else {
|
} else {
|
||||||
sig := &EVWMA{
|
sig := &VWEMA{
|
||||||
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, s.SignalWindow}},
|
PV: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, s.SignalWindow}},
|
||||||
V: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, s.SignalWindow}},
|
V: &indicator.EWMA{IntervalWindow: types.IntervalWindow{s.Interval, s.SignalWindow}},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user