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"
|
||||
)
|
||||
|
||||
// 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)
|
||||
|
|
|
@ -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,12 +112,32 @@ 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"`
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
func (m BacktestAccountBalanceMap) BalanceMap() types.BalanceMap {
|
||||
|
|
|
@ -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}},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user