mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 08:45:16 +00:00
Merge pull request #940 from c9s/strategy/pivotshort
This commit is contained in:
commit
a6e01c6c2f
|
@ -33,8 +33,19 @@ exchangeStrategies:
|
||||||
quantity: 10.0
|
quantity: 10.0
|
||||||
|
|
||||||
# marketOrder submits the market sell order when the closed price is lower than the previous pivot low.
|
# marketOrder submits the market sell order when the closed price is lower than the previous pivot low.
|
||||||
|
# by default we will use market order
|
||||||
marketOrder: true
|
marketOrder: true
|
||||||
|
|
||||||
|
# limitOrder place limit order to open the short position instead of using market order
|
||||||
|
# this is useful when your quantity or leverage is quiet large.
|
||||||
|
limitOrder: false
|
||||||
|
|
||||||
|
# limitOrderTakerRatio is the price ratio to adjust your limit order as a taker order. e.g., 0.1%
|
||||||
|
# for sell order, 0.1% ratio means your final price = price * (1 - 0.1%)
|
||||||
|
# for buy order, 0.1% ratio means your final price = price * (1 + 0.1%)
|
||||||
|
# this is only enabled when the limitOrder option set to true
|
||||||
|
limitOrderTakerRatio: 0
|
||||||
|
|
||||||
# bounceRatio is used for calculating the price of the limit sell order.
|
# bounceRatio is used for calculating the price of the limit sell order.
|
||||||
# it's ratio of pivot low bounce when a new pivot low is detected.
|
# it's ratio of pivot low bounce when a new pivot low is detected.
|
||||||
# Sometimes when the price breaks the previous low, the price might be pulled back to a higher price.
|
# Sometimes when the price breaks the previous low, the price might be pulled back to a higher price.
|
||||||
|
|
|
@ -129,34 +129,43 @@ func (e *GeneralOrderExecutor) SubmitOrders(ctx context.Context, submitOrders ..
|
||||||
|
|
||||||
type OpenPositionOptions struct {
|
type OpenPositionOptions struct {
|
||||||
// Long is for open a long position
|
// Long is for open a long position
|
||||||
// Long or Short must be set
|
// Long or Short must be set, avoid loading it from the config file
|
||||||
Long bool `json:"long"`
|
// it should be set from the strategy code
|
||||||
|
Long bool `json:"-" yaml:"-"`
|
||||||
|
|
||||||
// Short is for open a short position
|
// Short is for open a short position
|
||||||
// Long or Short must be set
|
// Long or Short must be set
|
||||||
Short bool `json:"short"`
|
Short bool `json:"-" yaml:"-"`
|
||||||
|
|
||||||
// Leverage is used for leveraged position and account
|
// Leverage is used for leveraged position and account
|
||||||
|
// Leverage is not effected when using non-leverage spot account
|
||||||
Leverage fixedpoint.Value `json:"leverage,omitempty"`
|
Leverage fixedpoint.Value `json:"leverage,omitempty"`
|
||||||
|
|
||||||
// Quantity will be used first, it will override the leverage if it's given.
|
// Quantity will be used first, it will override the leverage if it's given
|
||||||
Quantity fixedpoint.Value `json:"quantity,omitempty"`
|
Quantity fixedpoint.Value `json:"quantity,omitempty"`
|
||||||
|
|
||||||
// MarketOrder set to true to open a position with a market order
|
// MarketOrder set to true to open a position with a market order
|
||||||
|
// default is MarketOrder = true
|
||||||
MarketOrder bool `json:"marketOrder,omitempty"`
|
MarketOrder bool `json:"marketOrder,omitempty"`
|
||||||
|
|
||||||
// LimitOrder set to true to open a position with a limit order
|
// LimitOrder set to true to open a position with a limit order
|
||||||
LimitOrder bool `json:"limitOrder,omitempty"`
|
LimitOrder bool `json:"limitOrder,omitempty"`
|
||||||
|
|
||||||
// LimitTakerRatio is used when LimitOrder = true, it adjusts the price of the limit order with a ratio.
|
// LimitOrderTakerRatio is used when LimitOrder = true, it adjusts the price of the limit order with a ratio.
|
||||||
// So you can ensure that the limit order can be a taker order. Higher the ratio, higher the chance it could be a taker order.
|
// So you can ensure that the limit order can be a taker order. Higher the ratio, higher the chance it could be a taker order.
|
||||||
LimitTakerRatio fixedpoint.Value `json:"limitTakerRatio,omitempty"`
|
//
|
||||||
CurrentPrice fixedpoint.Value `json:"currentPrice,omitempty"`
|
// limitOrderTakerRatio is the price ratio to adjust your limit order as a taker order. e.g., 0.1%
|
||||||
Tags []string `json:"tags"`
|
// for sell order, 0.1% ratio means your final price = price * (1 - 0.1%)
|
||||||
|
// for buy order, 0.1% ratio means your final price = price * (1 + 0.1%)
|
||||||
|
// this is only enabled when the limitOrder option set to true
|
||||||
|
LimitOrderTakerRatio fixedpoint.Value `json:"limitOrderTakerRatio,omitempty"`
|
||||||
|
|
||||||
|
Price fixedpoint.Value `json:"-" yaml:"-"`
|
||||||
|
Tags []string `json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPositionOptions) error {
|
func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPositionOptions) error {
|
||||||
price := options.CurrentPrice
|
price := options.Price
|
||||||
submitOrder := types.SubmitOrder{
|
submitOrder := types.SubmitOrder{
|
||||||
Symbol: e.position.Symbol,
|
Symbol: e.position.Symbol,
|
||||||
Type: types.OrderTypeMarket,
|
Type: types.OrderTypeMarket,
|
||||||
|
@ -164,13 +173,13 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos
|
||||||
Tag: strings.Join(options.Tags, ","),
|
Tag: strings.Join(options.Tags, ","),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.LimitTakerRatio.IsZero() {
|
if !options.LimitOrderTakerRatio.IsZero() {
|
||||||
if options.Long {
|
if options.Long {
|
||||||
// use higher price to buy (this ensures that our order will be filled)
|
// use higher price to buy (this ensures that our order will be filled)
|
||||||
price = price.Mul(one.Add(options.LimitTakerRatio))
|
price = price.Mul(one.Add(options.LimitOrderTakerRatio))
|
||||||
} else if options.Short {
|
} else if options.Short {
|
||||||
// use lower price to sell (this ensures that our order will be filled)
|
// use lower price to sell (this ensures that our order will be filled)
|
||||||
price = price.Mul(one.Sub(options.LimitTakerRatio))
|
price = price.Mul(one.Sub(options.LimitOrderTakerRatio))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,32 +19,40 @@ type BreakLow struct {
|
||||||
Market types.Market
|
Market types.Market
|
||||||
types.IntervalWindow
|
types.IntervalWindow
|
||||||
|
|
||||||
|
// FastWindow is used for fast pivot (this is to to filter the nearest high/low)
|
||||||
|
FastWindow int `json:"fastWindow"`
|
||||||
|
|
||||||
// Ratio is a number less than 1.0, price * ratio will be the price triggers the short order.
|
// Ratio is a number less than 1.0, price * ratio will be the price triggers the short order.
|
||||||
Ratio fixedpoint.Value `json:"ratio"`
|
Ratio fixedpoint.Value `json:"ratio"`
|
||||||
|
|
||||||
// MarketOrder is the option to enable market order short.
|
// MarketOrder is the option to enable market order short.
|
||||||
MarketOrder bool `json:"marketOrder"`
|
MarketOrder bool `json:"marketOrder"`
|
||||||
|
|
||||||
|
// LimitOrder is the option to use limit order instead of market order to short
|
||||||
|
LimitOrder bool `json:"limitOrder"`
|
||||||
|
LimitTakerRatio fixedpoint.Value `json:"limitTakerRatio"`
|
||||||
|
Leverage fixedpoint.Value `json:"leverage"`
|
||||||
|
Quantity fixedpoint.Value `json:"quantity"`
|
||||||
|
|
||||||
|
bbgo.OpenPositionOptions
|
||||||
|
|
||||||
// BounceRatio is a ratio used for placing the limit order sell price
|
// BounceRatio is a ratio used for placing the limit order sell price
|
||||||
// limit sell price = breakLowPrice * (1 + BounceRatio)
|
// limit sell price = breakLowPrice * (1 + BounceRatio)
|
||||||
BounceRatio fixedpoint.Value `json:"bounceRatio"`
|
BounceRatio fixedpoint.Value `json:"bounceRatio"`
|
||||||
|
|
||||||
Leverage fixedpoint.Value `json:"leverage"`
|
|
||||||
Quantity fixedpoint.Value `json:"quantity"`
|
|
||||||
|
|
||||||
StopEMA *bbgo.StopEMA `json:"stopEMA"`
|
StopEMA *bbgo.StopEMA `json:"stopEMA"`
|
||||||
|
|
||||||
TrendEMA *bbgo.TrendEMA `json:"trendEMA"`
|
TrendEMA *bbgo.TrendEMA `json:"trendEMA"`
|
||||||
|
|
||||||
FakeBreakStop *FakeBreakStop `json:"fakeBreakStop"`
|
FakeBreakStop *FakeBreakStop `json:"fakeBreakStop"`
|
||||||
|
|
||||||
lastLow fixedpoint.Value
|
lastLow, lastFastLow fixedpoint.Value
|
||||||
|
|
||||||
// lastBreakLow is the low that the price just break
|
// lastBreakLow is the low that the price just break
|
||||||
lastBreakLow fixedpoint.Value
|
lastBreakLow fixedpoint.Value
|
||||||
|
|
||||||
pivotLow *indicator.PivotLow
|
pivotLow, fastPivotLow *indicator.PivotLow
|
||||||
pivotLowPrices []fixedpoint.Value
|
pivotLowPrices []fixedpoint.Value
|
||||||
|
|
||||||
trendEWMALast, trendEWMACurrent float64
|
trendEWMALast, trendEWMACurrent float64
|
||||||
|
|
||||||
|
@ -73,6 +81,10 @@ func (s *BreakLow) Subscribe(session *bbgo.ExchangeSession) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.GeneralOrderExecutor) {
|
func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.GeneralOrderExecutor) {
|
||||||
|
if s.FastWindow == 0 {
|
||||||
|
s.FastWindow = 3
|
||||||
|
}
|
||||||
|
|
||||||
s.session = session
|
s.session = session
|
||||||
s.orderExecutor = orderExecutor
|
s.orderExecutor = orderExecutor
|
||||||
|
|
||||||
|
@ -84,8 +96,11 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
|
||||||
standardIndicator := session.StandardIndicatorSet(s.Symbol)
|
standardIndicator := session.StandardIndicatorSet(s.Symbol)
|
||||||
|
|
||||||
s.lastLow = fixedpoint.Zero
|
s.lastLow = fixedpoint.Zero
|
||||||
|
|
||||||
s.pivotLow = standardIndicator.PivotLow(s.IntervalWindow)
|
s.pivotLow = standardIndicator.PivotLow(s.IntervalWindow)
|
||||||
|
s.fastPivotLow = standardIndicator.PivotLow(types.IntervalWindow{
|
||||||
|
Interval: s.Interval,
|
||||||
|
Window: s.FastWindow, // make it faster
|
||||||
|
})
|
||||||
|
|
||||||
if s.StopEMA != nil {
|
if s.StopEMA != nil {
|
||||||
s.StopEMA.Bind(session, orderExecutor)
|
s.StopEMA.Bind(session, orderExecutor)
|
||||||
|
@ -143,12 +158,12 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
|
||||||
}
|
}
|
||||||
|
|
||||||
session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, types.Interval1m, func(kline types.KLine) {
|
session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, types.Interval1m, func(kline types.KLine) {
|
||||||
if len(s.pivotLowPrices) == 0 {
|
if len(s.pivotLowPrices) == 0 || s.lastLow.IsZero() {
|
||||||
log.Infof("currently there is no pivot low prices, can not check break low...")
|
log.Infof("currently there is no pivot low prices, can not check break low...")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
previousLow := s.pivotLowPrices[len(s.pivotLowPrices)-1]
|
previousLow := s.lastLow
|
||||||
ratio := fixedpoint.One.Add(s.Ratio)
|
ratio := fixedpoint.One.Add(s.Ratio)
|
||||||
breakPrice := previousLow.Mul(ratio)
|
breakPrice := previousLow.Mul(ratio)
|
||||||
|
|
||||||
|
@ -207,40 +222,17 @@ func (s *BreakLow) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
|
||||||
// graceful cancel all active orders
|
// graceful cancel all active orders
|
||||||
_ = orderExecutor.GracefulCancel(ctx)
|
_ = orderExecutor.GracefulCancel(ctx)
|
||||||
|
|
||||||
quantity, err := bbgo.CalculateBaseQuantity(s.session, s.Market, closePrice, s.Quantity, s.Leverage)
|
bbgo.Notify("%s price %f breaks the previous low %f with ratio %f, opening short position", symbol, kline.Close.Float64(), previousLow.Float64(), s.Ratio.Float64())
|
||||||
if err != nil {
|
opts := s.OpenPositionOptions
|
||||||
log.WithError(err).Errorf("quantity calculation error")
|
opts.Short = true
|
||||||
|
opts.Price = closePrice
|
||||||
|
opts.Tags = []string{"breakLowMarket"}
|
||||||
|
if opts.LimitOrder && !s.BounceRatio.IsZero() {
|
||||||
|
opts.Price = previousLow.Mul(fixedpoint.One.Add(s.BounceRatio))
|
||||||
}
|
}
|
||||||
|
|
||||||
if quantity.IsZero() {
|
if err := s.orderExecutor.OpenPosition(ctx, opts); err != nil {
|
||||||
log.Warn("quantity is zero, can not submit order, skip")
|
log.WithError(err).Errorf("failed to open short position")
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.MarketOrder {
|
|
||||||
bbgo.Notify("%s price %f breaks the previous low %f with ratio %f, submitting market sell to open a short position", symbol, kline.Close.Float64(), previousLow.Float64(), s.Ratio.Float64())
|
|
||||||
_, _ = s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
|
||||||
Symbol: s.Symbol,
|
|
||||||
Side: types.SideTypeSell,
|
|
||||||
Type: types.OrderTypeMarket,
|
|
||||||
Quantity: quantity,
|
|
||||||
MarginSideEffect: types.SideEffectTypeMarginBuy,
|
|
||||||
Tag: "breakLowMarket",
|
|
||||||
})
|
|
||||||
|
|
||||||
} else {
|
|
||||||
sellPrice := previousLow.Mul(fixedpoint.One.Add(s.BounceRatio))
|
|
||||||
|
|
||||||
bbgo.Notify("%s price %f breaks the previous low %f with ratio %f, submitting limit sell @ %f", symbol, kline.Close.Float64(), previousLow.Float64(), s.Ratio.Float64(), sellPrice.Float64())
|
|
||||||
_, _ = s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
|
||||||
Symbol: kline.Symbol,
|
|
||||||
Side: types.SideTypeSell,
|
|
||||||
Type: types.OrderTypeLimit,
|
|
||||||
Price: sellPrice,
|
|
||||||
Quantity: quantity,
|
|
||||||
MarginSideEffect: types.SideEffectTypeMarginBuy,
|
|
||||||
Tag: "breakLowLimit",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -265,12 +257,28 @@ func (s *BreakLow) pilotQuantityCalculation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BreakLow) updatePivotLow() bool {
|
func (s *BreakLow) updatePivotLow() bool {
|
||||||
lastLow := fixedpoint.NewFromFloat(s.pivotLow.Last())
|
low := fixedpoint.NewFromFloat(s.pivotLow.Last())
|
||||||
if lastLow.IsZero() || lastLow.Compare(s.lastLow) == 0 {
|
if low.IsZero() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
s.lastLow = lastLow
|
lastLowChanged := low.Compare(s.lastLow) != 0
|
||||||
s.pivotLowPrices = append(s.pivotLowPrices, lastLow)
|
if lastLowChanged {
|
||||||
return true
|
if s.lastFastLow.IsZero() || low.Compare(s.lastFastLow) > 0 {
|
||||||
|
s.lastLow = low
|
||||||
|
s.pivotLowPrices = append(s.pivotLowPrices, low)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fastLow := fixedpoint.NewFromFloat(s.fastPivotLow.Last())
|
||||||
|
if !fastLow.IsZero() {
|
||||||
|
if fastLow.Compare(s.lastLow) < 0 {
|
||||||
|
// invalidate the last low
|
||||||
|
s.lastLow = fixedpoint.Zero
|
||||||
|
lastLowChanged = false
|
||||||
|
}
|
||||||
|
s.lastFastLow = fastLow
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastLowChanged
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ type FailedBreakHigh struct {
|
||||||
// IntervalWindow is used for finding the pivot high
|
// IntervalWindow is used for finding the pivot high
|
||||||
types.IntervalWindow
|
types.IntervalWindow
|
||||||
|
|
||||||
|
bbgo.OpenPositionOptions
|
||||||
|
|
||||||
// BreakInterval is used for checking failed break
|
// BreakInterval is used for checking failed break
|
||||||
BreakInterval types.Interval `json:"breakInterval"`
|
BreakInterval types.Interval `json:"breakInterval"`
|
||||||
|
|
||||||
|
@ -25,23 +27,17 @@ type FailedBreakHigh struct {
|
||||||
// Ratio is a number less than 1.0, price * ratio will be the price triggers the short order.
|
// Ratio is a number less than 1.0, price * ratio will be the price triggers the short order.
|
||||||
Ratio fixedpoint.Value `json:"ratio"`
|
Ratio fixedpoint.Value `json:"ratio"`
|
||||||
|
|
||||||
// MarketOrder is the option to enable market order short.
|
|
||||||
MarketOrder bool `json:"marketOrder"`
|
|
||||||
|
|
||||||
Leverage fixedpoint.Value `json:"leverage"`
|
|
||||||
Quantity fixedpoint.Value `json:"quantity"`
|
|
||||||
|
|
||||||
VWMA *types.IntervalWindow `json:"vwma"`
|
VWMA *types.IntervalWindow `json:"vwma"`
|
||||||
|
|
||||||
StopEMA *bbgo.StopEMA `json:"stopEMA"`
|
StopEMA *bbgo.StopEMA `json:"stopEMA"`
|
||||||
|
|
||||||
TrendEMA *bbgo.TrendEMA `json:"trendEMA"`
|
TrendEMA *bbgo.TrendEMA `json:"trendEMA"`
|
||||||
|
|
||||||
lastFailedBreakHigh, lastHigh fixedpoint.Value
|
lastFailedBreakHigh, lastHigh, lastFastHigh fixedpoint.Value
|
||||||
|
|
||||||
pivotHigh *indicator.PivotHigh
|
pivotHigh, fastPivotHigh *indicator.PivotHigh
|
||||||
vwma *indicator.VWMA
|
vwma *indicator.VWMA
|
||||||
PivotHighPrices []fixedpoint.Value
|
pivotHighPrices []fixedpoint.Value
|
||||||
|
|
||||||
orderExecutor *bbgo.GeneralOrderExecutor
|
orderExecutor *bbgo.GeneralOrderExecutor
|
||||||
session *bbgo.ExchangeSession
|
session *bbgo.ExchangeSession
|
||||||
|
@ -82,6 +78,10 @@ func (s *FailedBreakHigh) Bind(session *bbgo.ExchangeSession, orderExecutor *bbg
|
||||||
|
|
||||||
s.lastHigh = fixedpoint.Zero
|
s.lastHigh = fixedpoint.Zero
|
||||||
s.pivotHigh = standardIndicator.PivotHigh(s.IntervalWindow)
|
s.pivotHigh = standardIndicator.PivotHigh(s.IntervalWindow)
|
||||||
|
s.fastPivotHigh = standardIndicator.PivotHigh(types.IntervalWindow{
|
||||||
|
Interval: s.IntervalWindow.Interval,
|
||||||
|
Window: 3,
|
||||||
|
})
|
||||||
|
|
||||||
// StrategyController
|
// StrategyController
|
||||||
s.Status = types.StrategyStatusRunning
|
s.Status = types.StrategyStatusRunning
|
||||||
|
@ -156,7 +156,7 @@ func (s *FailedBreakHigh) Bind(session *bbgo.ExchangeSession, orderExecutor *bbg
|
||||||
}))
|
}))
|
||||||
|
|
||||||
session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, s.BreakInterval, func(kline types.KLine) {
|
session.MarketDataStream.OnKLineClosed(types.KLineWith(s.Symbol, s.BreakInterval, func(kline types.KLine) {
|
||||||
if len(s.PivotHighPrices) == 0 || s.lastHigh.IsZero() {
|
if len(s.pivotHighPrices) == 0 || s.lastHigh.IsZero() {
|
||||||
log.Infof("currently there is no pivot high prices, can not check failed break high...")
|
log.Infof("currently there is no pivot high prices, can not check failed break high...")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -221,50 +221,21 @@ func (s *FailedBreakHigh) Bind(session *bbgo.ExchangeSession, orderExecutor *bbg
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
bbgo.Notify("%s price %f failed breaking the previous high %f with ratio %f, opening short position",
|
||||||
|
symbol,
|
||||||
|
kline.Close.Float64(),
|
||||||
|
previousHigh.Float64(),
|
||||||
|
s.Ratio.Float64())
|
||||||
|
|
||||||
// graceful cancel all active orders
|
// graceful cancel all active orders
|
||||||
_ = orderExecutor.GracefulCancel(ctx)
|
_ = orderExecutor.GracefulCancel(ctx)
|
||||||
|
|
||||||
quantity, err := bbgo.CalculateBaseQuantity(s.session, s.Market, closePrice, s.Quantity, s.Leverage)
|
opts := s.OpenPositionOptions
|
||||||
if err != nil {
|
opts.Short = true
|
||||||
log.WithError(err).Errorf("quantity calculation error")
|
opts.Price = closePrice
|
||||||
}
|
opts.Tags = []string{"FailedBreakHighMarket"}
|
||||||
|
if err := s.orderExecutor.OpenPosition(ctx, opts); err != nil {
|
||||||
if quantity.IsZero() {
|
log.WithError(err).Errorf("failed to open short position")
|
||||||
log.Warn("quantity is zero, can not submit order, skip")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.MarketOrder {
|
|
||||||
bbgo.Notify("%s price %f failed breaking the previous high %f with ratio %f, submitting market sell %f to open a short position", symbol, kline.Close.Float64(), previousHigh.Float64(), s.Ratio.Float64(), quantity.Float64())
|
|
||||||
_, err := s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
|
||||||
Symbol: s.Symbol,
|
|
||||||
Side: types.SideTypeSell,
|
|
||||||
Type: types.OrderTypeMarket,
|
|
||||||
Quantity: quantity,
|
|
||||||
MarginSideEffect: types.SideEffectTypeMarginBuy,
|
|
||||||
Tag: "FailedBreakHighMarket",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
bbgo.Notify(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
sellPrice := previousHigh
|
|
||||||
|
|
||||||
bbgo.Notify("%s price %f failed breaking the previous high %f with ratio %f, submitting limit sell @ %f", symbol, kline.Close.Float64(), previousHigh.Float64(), s.Ratio.Float64(), sellPrice.Float64())
|
|
||||||
_, err := s.orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
|
||||||
Symbol: kline.Symbol,
|
|
||||||
Side: types.SideTypeSell,
|
|
||||||
Type: types.OrderTypeLimit,
|
|
||||||
Price: sellPrice,
|
|
||||||
Quantity: quantity,
|
|
||||||
MarginSideEffect: types.SideEffectTypeMarginBuy,
|
|
||||||
Tag: "FailedBreakHighLimit",
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
bbgo.Notify(err.Error())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -290,11 +261,25 @@ func (s *FailedBreakHigh) pilotQuantityCalculation() {
|
||||||
|
|
||||||
func (s *FailedBreakHigh) updatePivotHigh() bool {
|
func (s *FailedBreakHigh) updatePivotHigh() bool {
|
||||||
lastHigh := fixedpoint.NewFromFloat(s.pivotHigh.Last())
|
lastHigh := fixedpoint.NewFromFloat(s.pivotHigh.Last())
|
||||||
if lastHigh.IsZero() || lastHigh.Compare(s.lastHigh) == 0 {
|
if lastHigh.IsZero() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
s.lastHigh = lastHigh
|
lastHighChanged := lastHigh.Compare(s.lastHigh) != 0
|
||||||
s.PivotHighPrices = append(s.PivotHighPrices, lastHigh)
|
if lastHighChanged {
|
||||||
return true
|
s.lastHigh = lastHigh
|
||||||
|
s.pivotHighPrices = append(s.pivotHighPrices, lastHigh)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFastHigh := fixedpoint.NewFromFloat(s.fastPivotHigh.Last())
|
||||||
|
if !lastFastHigh.IsZero() {
|
||||||
|
if lastFastHigh.Compare(s.lastHigh) > 0 {
|
||||||
|
// invalidate the last low
|
||||||
|
s.lastHigh = fixedpoint.Zero
|
||||||
|
lastHighChanged = false
|
||||||
|
}
|
||||||
|
s.lastFastHigh = lastFastHigh
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastHighChanged
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user