mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
Merge pull request #969 from zenixls2/fix/precheck_reduceQuantity
fix: reduce Quantity precheck, drift condition, ewo refactor
This commit is contained in:
commit
ffdf2a69a4
|
@ -27,29 +27,30 @@ exchangeStrategies:
|
|||
- on: binance
|
||||
drift:
|
||||
limitOrder: false
|
||||
quantity: 0.001
|
||||
canvasPath: "./output.png"
|
||||
symbol: BTCUSDT
|
||||
# kline interval for indicators
|
||||
interval: 4m
|
||||
window: 2
|
||||
stoploss: 0.13%
|
||||
source: ohlc4
|
||||
interval: 1m
|
||||
window: 4
|
||||
stoploss: 0.02%
|
||||
source: hl2
|
||||
predictOffset: 2
|
||||
noTrailingStopLoss: false
|
||||
trailingStopLossType: kline
|
||||
trailingStopLossType: realtime
|
||||
# stddev on high/low-source
|
||||
hlVarianceMultiplier: 0.22
|
||||
hlVarianceMultiplier: 0.2
|
||||
hlRangeWindow: 4
|
||||
smootherWindow: 1
|
||||
fisherTransformWindow: 96
|
||||
smootherWindow: 31
|
||||
fisherTransformWindow: 80
|
||||
window1m: 8
|
||||
smootherWindow1m: 4
|
||||
fisherTransformWindow1m: 320
|
||||
atrWindow: 14
|
||||
# orders not been traded will be canceled after `pendingMinutes` minutes
|
||||
pendingMinutes: 10
|
||||
pendingMinutes: 3
|
||||
noRebalance: true
|
||||
trendWindow: 576
|
||||
trendWindow: 185
|
||||
rebalanceFilter: 1.2
|
||||
#driftFilterPos: 0.5
|
||||
#driftFilterNeg: -0.5
|
||||
|
@ -60,12 +61,12 @@ exchangeStrategies:
|
|||
# when farest price from entry goes over that ratio, start using the callback ratio accordingly to do trailingstop
|
||||
#trailingActivationRatio: [0.01, 0.016, 0.05]
|
||||
#trailingActivationRatio: [0.001, 0.0081, 0.022]
|
||||
trailingActivationRatio: [0.0012, 0.0016, 0.01]
|
||||
trailingActivationRatio: [0.0007, 0.0016, 0.008, 0.01]
|
||||
#trailingActivationRatio: []
|
||||
#trailingCallbackRate: []
|
||||
#trailingCallbackRate: [0.002, 0.01, 0.1]
|
||||
#trailingCallbackRate: [0.0004, 0.0009, 0.018]
|
||||
trailingCallbackRate: [0.0003, 0.0006, 0.0019]
|
||||
trailingCallbackRate: [0.0003, 0.0005, 0.0010, 0.0016]
|
||||
|
||||
generateGraph: true
|
||||
graphPNLDeductFee: false
|
||||
|
@ -128,7 +129,7 @@ sync:
|
|||
- BTCUSDT
|
||||
|
||||
backtest:
|
||||
startTime: "2022-09-01"
|
||||
startTime: "2022-09-26"
|
||||
endTime: "2022-09-30"
|
||||
symbols:
|
||||
- BTCUSDT
|
||||
|
@ -139,4 +140,4 @@ backtest:
|
|||
takerFeeRate: 0.000
|
||||
balances:
|
||||
BTC: 0
|
||||
USDT: 1000
|
||||
USDT: 50
|
||||
|
|
|
@ -24,12 +24,14 @@ exchangeStrategies:
|
|||
- on: binance
|
||||
elliottwave:
|
||||
symbol: BNBBUSD
|
||||
limitOrder: true
|
||||
quantity: 0.16
|
||||
# kline interval for indicators
|
||||
interval: 3m
|
||||
stoploss: 0.2%
|
||||
interval: 1m
|
||||
stoploss: 0.01%
|
||||
windowATR: 14
|
||||
windowQuick: 5
|
||||
windowSlow: 19
|
||||
windowSlow: 9
|
||||
source: hl2
|
||||
pendingMinutes: 10
|
||||
useHeikinAshi: true
|
||||
|
@ -49,7 +51,7 @@ exchangeStrategies:
|
|||
#trailingCallbackRate: []
|
||||
#trailingCallbackRate: [0.002, 0.01, 0.1]
|
||||
#trailingCallbackRate: [0.0004, 0.0009, 0.018]
|
||||
trailingCallbackRate: [0.0006, 0.0049, 0.006]
|
||||
trailingCallbackRate: [0.0006, 0.0019, 0.006]
|
||||
|
||||
#exits:
|
||||
# - roiStopLoss:
|
||||
|
@ -119,4 +121,4 @@ backtest:
|
|||
takerFeeRate: 0.000
|
||||
balances:
|
||||
BNB: 0
|
||||
BUSD: 20
|
||||
BUSD: 100
|
||||
|
|
|
@ -230,7 +230,16 @@ func (e *GeneralOrderExecutor) reduceQuantityAndSubmitOrder(ctx context.Context,
|
|||
var err error
|
||||
for i := 0; i < submitOrderRetryLimit; i++ {
|
||||
q := submitOrder.Quantity.Mul(fixedpoint.One.Sub(quantityReduceDelta))
|
||||
log.Warnf("retrying order, adjusting order quantity: %f -> %f", submitOrder.Quantity.Float64(), q.Float64())
|
||||
if submitOrder.Side == types.SideTypeSell {
|
||||
if baseBalance, ok := e.session.GetAccount().Balance(e.position.Market.BaseCurrency); ok {
|
||||
q = fixedpoint.Min(q, baseBalance.Available)
|
||||
}
|
||||
} else {
|
||||
if quoteBalance, ok := e.session.GetAccount().Balance(e.position.Market.QuoteCurrency); ok {
|
||||
q = fixedpoint.Min(q, quoteBalance.Available.Div(price))
|
||||
}
|
||||
}
|
||||
log.Warnf("retrying order, adjusting order quantity: %v -> %v", submitOrder.Quantity, q)
|
||||
|
||||
submitOrder.Quantity = q
|
||||
if e.position.Market.IsDustQuantity(submitOrder.Quantity, price) {
|
||||
|
@ -260,7 +269,7 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos
|
|||
Tag: strings.Join(options.Tags, ","),
|
||||
}
|
||||
|
||||
baseBalance, _ := e.session.Account.Balance(e.position.Market.BaseCurrency)
|
||||
baseBalance, _ := e.session.GetAccount().Balance(e.position.Market.BaseCurrency)
|
||||
|
||||
// FIXME: fix the max quote borrowing checking
|
||||
// quoteBalance, _ := e.session.Account.Balance(e.position.Market.QuoteCurrency)
|
||||
|
|
|
@ -14,9 +14,9 @@ import (
|
|||
type Drift struct {
|
||||
types.SeriesBase
|
||||
types.IntervalWindow
|
||||
chng *types.Queue
|
||||
Values floats.Slice
|
||||
MA types.UpdatableSeriesExtend
|
||||
chng *types.Queue
|
||||
Values floats.Slice
|
||||
MA types.UpdatableSeriesExtend
|
||||
LastValue float64
|
||||
|
||||
UpdateCallbacks []func(value float64)
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wcharczuk/go-chart/v2"
|
||||
"go.uber.org/multierr"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/datatype/floats"
|
||||
|
@ -37,6 +38,19 @@ func init() {
|
|||
bbgo.RegisterStrategy(ID, &Strategy{})
|
||||
}
|
||||
|
||||
func filterErrors(errs []error) (es []error) {
|
||||
for _, e := range errs {
|
||||
if _, ok := e.(types.ZeroAssetError); ok {
|
||||
continue
|
||||
}
|
||||
if bbgo.ErrExceededSubmitOrderRetryLimit == e {
|
||||
continue
|
||||
}
|
||||
es = append(es, e)
|
||||
}
|
||||
return es
|
||||
}
|
||||
|
||||
type Strategy struct {
|
||||
Symbol string `json:"symbol"`
|
||||
|
||||
|
@ -152,7 +166,6 @@ func (s *Strategy) CurrentPosition() *types.Position {
|
|||
func (s *Strategy) ClosePosition(ctx context.Context, percentage fixedpoint.Value) error {
|
||||
order := s.p.NewMarketCloseOrder(percentage)
|
||||
if order == nil {
|
||||
s.positionLock.Unlock()
|
||||
return nil
|
||||
}
|
||||
order.Tag = "close"
|
||||
|
@ -169,7 +182,6 @@ func (s *Strategy) ClosePosition(ctx context.Context, percentage fixedpoint.Valu
|
|||
order.Quantity = baseBalance
|
||||
}
|
||||
order.MarginSideEffect = types.SideEffectTypeAutoRepay
|
||||
s.positionLock.Unlock()
|
||||
for {
|
||||
if s.Market.IsDustQuantity(order.Quantity, price) {
|
||||
return nil
|
||||
|
@ -383,11 +395,11 @@ func (s *Strategy) initTickerFunctions(ctx context.Context) {
|
|||
s.trailingCheck(pricef, "short"))
|
||||
exitLongCondition := s.buyPrice > 0 && (s.buyPrice*(1.-stoploss) >= pricef ||
|
||||
s.trailingCheck(pricef, "long"))
|
||||
|
||||
s.positionLock.Unlock()
|
||||
if exitShortCondition || exitLongCondition {
|
||||
s.ClosePosition(ctx, fixedpoint.One)
|
||||
log.Infof("close position by orderbook changes")
|
||||
} else {
|
||||
s.positionLock.Unlock()
|
||||
}
|
||||
})
|
||||
s.getLastPrice = func() (lastPrice fixedpoint.Value) {
|
||||
|
@ -621,10 +633,9 @@ func (s *Strategy) klineHandler1m(ctx context.Context, kline types.KLine) {
|
|||
s.trailingCheck(highf, "short") /* || s.drift1m.Last() > 0*/)
|
||||
exitLongCondition := s.buyPrice > 0 && (s.buyPrice*(1.-stoploss) >= lowf ||
|
||||
s.trailingCheck(lowf, "long") /* || s.drift1m.Last() < 0*/)
|
||||
s.positionLock.Unlock()
|
||||
if exitShortCondition || exitLongCondition {
|
||||
_ = s.ClosePosition(ctx, fixedpoint.One)
|
||||
} else {
|
||||
s.positionLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -706,8 +717,8 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
|||
return v <= 0
|
||||
}, 50).Mean(50)
|
||||
|
||||
shortCondition := (drift[1] >= s.DriftFilterNeg || ddrift[1] >= 0) && (driftPred <= s.DDriftFilterNeg || ddriftPred <= 0) || drift[1] < 0 && drift[0] < 0
|
||||
longCondition := (drift[1] <= s.DriftFilterPos || ddrift[1] <= 0) && (driftPred >= s.DDriftFilterPos || ddriftPred >= 0) || drift[1] > 0 && drift[0] > 0
|
||||
shortCondition := drift[1] >= 0 && drift[0] <= 0 || (drift[1] >= drift[0] && drift[1] <= 0) || ddrift[1] >= 0 && ddrift[0] <= 0 || (ddrift[1] >= ddrift[0] && ddrift[1] <= 0)
|
||||
longCondition := drift[1] <= 0 && drift[0] >= 0 || (drift[1] <= drift[0] && drift[1] >= 0) || ddrift[1] <= 0 && ddrift[0] >= 0 || (ddrift[1] <= ddrift[0] && ddrift[1] >= 0)
|
||||
if shortCondition && longCondition {
|
||||
if drift[1] > drift[0] {
|
||||
longCondition = false
|
||||
|
@ -721,14 +732,16 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
|||
s.trailingCheck(pricef, "long"))
|
||||
|
||||
if exitShortCondition || exitLongCondition {
|
||||
s.positionLock.Unlock()
|
||||
if err := s.GeneralOrderExecutor.GracefulCancel(ctx); err != nil {
|
||||
log.WithError(err).Errorf("cannot cancel orders")
|
||||
s.positionLock.Unlock()
|
||||
return
|
||||
}
|
||||
_ = s.ClosePosition(ctx, fixedpoint.One)
|
||||
if longCondition || shortCondition {
|
||||
if shortCondition || longCondition {
|
||||
s.positionLock.Lock()
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -756,10 +769,11 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
|||
opt.Tags = []string{"long"}
|
||||
createdOrders, err := s.GeneralOrderExecutor.OpenPosition(ctx, opt)
|
||||
if err != nil {
|
||||
if _, ok := err.(types.ZeroAssetError); ok {
|
||||
return
|
||||
errs := filterErrors(multierr.Errors(err))
|
||||
if len(errs) > 0 {
|
||||
log.Errorf("%v", errs)
|
||||
log.WithError(err).Errorf("cannot place buy order")
|
||||
}
|
||||
log.WithError(err).Errorf("cannot place buy order")
|
||||
return
|
||||
}
|
||||
log.Infof("orders %v", createdOrders)
|
||||
|
@ -774,6 +788,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
|||
s.positionLock.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
/*source = source.Add(fixedpoint.NewFromFloat(s.stdevHigh.Last() * s.HighLowVarianceMultiplier))
|
||||
if source.Compare(price) < 0 {
|
||||
source = price
|
||||
|
@ -793,10 +808,10 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
|||
opt.Tags = []string{"short"}
|
||||
createdOrders, err := s.GeneralOrderExecutor.OpenPosition(ctx, opt)
|
||||
if err != nil {
|
||||
if _, ok := err.(types.ZeroAssetError); ok {
|
||||
return
|
||||
errs := filterErrors(multierr.Errors(err))
|
||||
if len(errs) > 0 {
|
||||
log.WithError(err).Errorf("cannot place sell order")
|
||||
}
|
||||
log.WithError(err).Errorf("cannot place buy order")
|
||||
return
|
||||
}
|
||||
log.Infof("orders %v", createdOrders)
|
||||
|
@ -889,46 +904,21 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
}
|
||||
s.positionLock.Lock()
|
||||
defer s.positionLock.Unlock()
|
||||
// tag == "" is for exits trades
|
||||
if tag == "close" || tag == "" {
|
||||
if s.p.IsDust(trade.Price) {
|
||||
s.buyPrice = 0
|
||||
s.sellPrice = 0
|
||||
s.highestPrice = 0
|
||||
s.lowestPrice = 0
|
||||
} else if s.p.IsLong() {
|
||||
s.sellPrice = 0
|
||||
s.lowestPrice = 0
|
||||
} else {
|
||||
s.buyPrice = 0
|
||||
s.highestPrice = 0
|
||||
}
|
||||
} else if tag == "long" {
|
||||
if s.p.IsDust(trade.Price) {
|
||||
s.buyPrice = 0
|
||||
s.sellPrice = 0
|
||||
s.highestPrice = 0
|
||||
s.lowestPrice = 0
|
||||
} else if s.p.IsLong() {
|
||||
s.buyPrice = trade.Price.Float64()
|
||||
s.sellPrice = 0
|
||||
s.highestPrice = s.buyPrice
|
||||
s.lowestPrice = 0
|
||||
}
|
||||
} else if tag == "short" {
|
||||
if s.p.IsDust(trade.Price) {
|
||||
s.sellPrice = 0
|
||||
s.buyPrice = 0
|
||||
s.highestPrice = 0
|
||||
s.lowestPrice = 0
|
||||
} else if s.p.IsShort() {
|
||||
s.sellPrice = trade.Price.Float64()
|
||||
s.buyPrice = 0
|
||||
s.highestPrice = 0
|
||||
s.lowestPrice = s.sellPrice
|
||||
}
|
||||
} else {
|
||||
panic("tag unknown")
|
||||
if s.p.IsDust(trade.Price) {
|
||||
s.buyPrice = 0
|
||||
s.sellPrice = 0
|
||||
s.highestPrice = 0
|
||||
s.lowestPrice = 0
|
||||
} else if s.p.IsLong() {
|
||||
s.buyPrice = s.p.ApproximateAverageCost.Float64() //trade.Price.Float64()
|
||||
s.sellPrice = 0
|
||||
s.highestPrice = math.Max(s.buyPrice, s.highestPrice)
|
||||
s.lowestPrice = 0
|
||||
} else if s.p.IsShort() {
|
||||
s.sellPrice = s.p.ApproximateAverageCost.Float64() //trade.Price.Float64()
|
||||
s.buyPrice = 0
|
||||
s.highestPrice = 0
|
||||
s.lowestPrice = math.Min(s.lowestPrice, s.sellPrice)
|
||||
}
|
||||
bbgo.Notify("tag: %s, sp: %.4f bp: %.4f hp: %.4f lp: %.4f, trade: %s, pos: %s", tag, s.sellPrice, s.buyPrice, s.highestPrice, s.lowestPrice, trade.String(), s.p.String())
|
||||
})
|
||||
|
|
|
@ -152,7 +152,8 @@ func (s *Strategy) initIndicators(store *bbgo.SerialMarketDataStore) error {
|
|||
maSlow := &indicator.SMA{IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.WindowSlow}}
|
||||
maQuick := &indicator.SMA{IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.WindowQuick}}
|
||||
s.ewo = &ElliottWave{
|
||||
maSlow, maQuick,
|
||||
maSlow: maSlow,
|
||||
maQuick: maQuick,
|
||||
}
|
||||
s.atr = &indicator.ATR{IntervalWindow: types.IntervalWindow{Interval: s.Interval, Window: s.WindowATR}}
|
||||
klines, ok := store.KLinesOfInterval(s.Interval)
|
||||
|
@ -371,10 +372,10 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
s.InitDrawCommands(store, &profit, &cumProfit)
|
||||
store.OnKLineClosed(func(kline types.KLine) {
|
||||
s.minutesCounter = int(kline.StartTime.Time().Add(kline.Interval.Duration()).Sub(s.startTime).Minutes())
|
||||
if kline.Interval == types.Interval1m {
|
||||
s.klineHandler1m(ctx, kline)
|
||||
} else if kline.Interval == s.Interval {
|
||||
if kline.Interval == s.Interval {
|
||||
s.klineHandler(ctx, kline)
|
||||
} else if kline.Interval == types.Interval1m {
|
||||
s.klineHandler1m(ctx, kline)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -213,9 +213,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
|
||||
// use kline direction to prevent reversing position too soon
|
||||
if diffQty.Sign() > 0 { // && kline.Direction() >= 0
|
||||
s.orderExecutor.OpenPosition(context.Background(), bbgo.OpenPositionOptions{Quantity: diffQty.Abs(), Long: true, MarketOrder: true})
|
||||
s.orderExecutor.OpenPosition(context.Background(), bbgo.OpenPositionOptions{Quantity: diffQty.Abs(), Long: true, LimitOrder: false})
|
||||
} else if diffQty.Sign() < 0 { // && kline.Direction() <= 0
|
||||
s.orderExecutor.OpenPosition(context.Background(), bbgo.OpenPositionOptions{Quantity: diffQty.Abs(), Short: true, MarketOrder: true})
|
||||
s.orderExecutor.OpenPosition(context.Background(), bbgo.OpenPositionOptions{Quantity: diffQty.Abs(), Short: true, LimitOrder: false})
|
||||
}
|
||||
|
||||
}))
|
||||
|
|
Loading…
Reference in New Issue
Block a user