mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 23:05:15 +00:00
pivotshort: refactor exit methods
Signed-off-by: c9s <yoanlin93@gmail.com>
This commit is contained in:
parent
47677e303f
commit
e9b87f6f1e
64
pkg/strategy/pivotshort/cumulated_volume_take_profit.go
Normal file
64
pkg/strategy/pivotshort/cumulated_volume_take_profit.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package pivotshort
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CumulatedVolumeTakeProfit struct {
|
||||||
|
types.IntervalWindow
|
||||||
|
Ratio fixedpoint.Value `json:"ratio"`
|
||||||
|
MinQuoteVolume fixedpoint.Value `json:"minQuoteVolume"`
|
||||||
|
|
||||||
|
session *bbgo.ExchangeSession
|
||||||
|
orderExecutor *bbgo.GeneralOrderExecutor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CumulatedVolumeTakeProfit) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.GeneralOrderExecutor) {
|
||||||
|
s.session = session
|
||||||
|
s.orderExecutor = orderExecutor
|
||||||
|
|
||||||
|
position := orderExecutor.Position()
|
||||||
|
|
||||||
|
store, _ := session.MarketDataStore(position.Symbol)
|
||||||
|
|
||||||
|
session.MarketDataStream.OnKLineClosed(func(kline types.KLine) {
|
||||||
|
if kline.Symbol != position.Symbol || kline.Interval != types.Interval1m {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
closePrice := kline.Close
|
||||||
|
if position.IsClosed() || position.IsDust(closePrice) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
roi := position.ROI(closePrice)
|
||||||
|
if roi.Sign() < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if klines, ok := store.KLinesOfInterval(s.Interval); ok {
|
||||||
|
var cbv = fixedpoint.Zero
|
||||||
|
var cqv = fixedpoint.Zero
|
||||||
|
for i := 0; i < s.Window; i++ {
|
||||||
|
last := (*klines)[len(*klines)-1-i]
|
||||||
|
cqv = cqv.Add(last.QuoteVolume)
|
||||||
|
cbv = cbv.Add(last.Volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cqv.Compare(s.MinQuoteVolume) > 0 {
|
||||||
|
bbgo.Notify("%s TakeProfit triggered by cumulated volume (window: %d) %f > %f, price = %f",
|
||||||
|
position.Symbol,
|
||||||
|
s.Window,
|
||||||
|
cqv.Float64(),
|
||||||
|
s.MinQuoteVolume.Float64(), kline.Close.Float64())
|
||||||
|
|
||||||
|
_ = orderExecutor.ClosePosition(context.Background(), fixedpoint.One)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
28
pkg/strategy/pivotshort/exit.go
Normal file
28
pkg/strategy/pivotshort/exit.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package pivotshort
|
||||||
|
|
||||||
|
import "github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
|
||||||
|
type ExitMethod struct {
|
||||||
|
RoiStopLoss *RoiStopLoss `json:"roiStopLoss"`
|
||||||
|
ProtectionStopLoss *ProtectionStopLoss `json:"protectionStopLoss"`
|
||||||
|
|
||||||
|
RoiTakeProfit *RoiTakeProfit `json:"roiTakeProfit"`
|
||||||
|
LowerShadowTakeProfit *LowerShadowTakeProfit `json:"lowerShadowTakeProfit"`
|
||||||
|
|
||||||
|
CumulatedVolumeTakeProfit *CumulatedVolumeTakeProfit `json:"cumulatedVolumeTakeProfit"`
|
||||||
|
// MarginSideEffect types.MarginOrderSideEffectType `json:"marginOrderSideEffect"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ExitMethod) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.GeneralOrderExecutor) {
|
||||||
|
if m.ProtectionStopLoss != nil {
|
||||||
|
m.ProtectionStopLoss.Bind(session, orderExecutor)
|
||||||
|
} else if m.RoiStopLoss != nil {
|
||||||
|
m.RoiStopLoss.Bind(session, orderExecutor)
|
||||||
|
} else if m.RoiTakeProfit != nil {
|
||||||
|
m.RoiTakeProfit.Bind(session, orderExecutor)
|
||||||
|
} else if m.LowerShadowTakeProfit != nil {
|
||||||
|
m.LowerShadowTakeProfit.Bind(session, orderExecutor)
|
||||||
|
} else if m.CumulatedVolumeTakeProfit != nil {
|
||||||
|
m.CumulatedVolumeTakeProfit.Bind(session, orderExecutor)
|
||||||
|
}
|
||||||
|
}
|
53
pkg/strategy/pivotshort/lower_shadow_take_profit.go
Normal file
53
pkg/strategy/pivotshort/lower_shadow_take_profit.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package pivotshort
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LowerShadowTakeProfit struct {
|
||||||
|
Ratio fixedpoint.Value `json:"ratio"`
|
||||||
|
|
||||||
|
session *bbgo.ExchangeSession
|
||||||
|
orderExecutor *bbgo.GeneralOrderExecutor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LowerShadowTakeProfit) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.GeneralOrderExecutor) {
|
||||||
|
s.session = session
|
||||||
|
s.orderExecutor = orderExecutor
|
||||||
|
|
||||||
|
position := orderExecutor.Position()
|
||||||
|
session.MarketDataStream.OnKLineClosed(func(kline types.KLine) {
|
||||||
|
if kline.Symbol != position.Symbol || kline.Interval != types.Interval1m {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
closePrice := kline.Close
|
||||||
|
if position.IsClosed() || position.IsDust(closePrice) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
roi := position.ROI(closePrice)
|
||||||
|
if roi.Sign() < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Ratio.IsZero() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if kline.GetLowerShadowHeight().Div(kline.Close).Compare(s.Ratio) > 0 {
|
||||||
|
bbgo.Notify("%s TakeProfit triggered by shadow ratio %f, price = %f",
|
||||||
|
position.Symbol,
|
||||||
|
kline.GetLowerShadowRatio().Float64(),
|
||||||
|
kline.Close.Float64(),
|
||||||
|
kline)
|
||||||
|
|
||||||
|
_ = orderExecutor.ClosePosition(context.Background(), fixedpoint.One)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -74,20 +74,6 @@ type CumulatedVolume struct {
|
||||||
Window int `json:"window"`
|
Window int `json:"window"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Exit struct {
|
|
||||||
RoiMinTakeProfitPercentage fixedpoint.Value `json:"roiMinTakeProfitPercentage"`
|
|
||||||
|
|
||||||
RoiTakeProfit *RoiTakeProfit `json:"roiTakeProfit"`
|
|
||||||
RoiStopLoss *RoiStopLoss `json:"roiStopLoss"`
|
|
||||||
ProtectionStopLoss *ProtectionStopLoss `json:"protectionStopLoss"`
|
|
||||||
|
|
||||||
LowerShadowRatio fixedpoint.Value `json:"lowerShadowRatio"`
|
|
||||||
|
|
||||||
CumulatedVolume *CumulatedVolume `json:"cumulatedVolume"`
|
|
||||||
|
|
||||||
MarginSideEffect types.MarginOrderSideEffectType `json:"marginOrderSideEffect"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Strategy struct {
|
type Strategy struct {
|
||||||
*bbgo.Graceful
|
*bbgo.Graceful
|
||||||
|
|
||||||
|
@ -107,8 +93,8 @@ type Strategy struct {
|
||||||
|
|
||||||
BounceShort *BounceShort `json:"bounceShort"`
|
BounceShort *BounceShort `json:"bounceShort"`
|
||||||
|
|
||||||
Entry Entry `json:"entry"`
|
Entry Entry `json:"entry"`
|
||||||
Exit Exit `json:"exit"`
|
ExitMethods []ExitMethod `json:"exits"`
|
||||||
|
|
||||||
session *bbgo.ExchangeSession
|
session *bbgo.ExchangeSession
|
||||||
orderExecutor *bbgo.GeneralOrderExecutor
|
orderExecutor *bbgo.GeneralOrderExecutor
|
||||||
|
@ -278,16 +264,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if s.Exit.ProtectionStopLoss != nil {
|
for _, method := range s.ExitMethods {
|
||||||
s.Exit.ProtectionStopLoss.Bind(session, s.orderExecutor)
|
method.Bind(session, s.orderExecutor)
|
||||||
}
|
|
||||||
|
|
||||||
if s.Exit.RoiStopLoss != nil {
|
|
||||||
s.Exit.RoiStopLoss.Bind(session, s.orderExecutor)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Exit.RoiTakeProfit != nil {
|
|
||||||
s.Exit.RoiTakeProfit.Bind(session, s.orderExecutor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always check whether you can open a short position or not
|
// Always check whether you can open a short position or not
|
||||||
|
@ -301,42 +279,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
|
|
||||||
isPositionOpened := !s.Position.IsClosed() && !s.Position.IsDust(kline.Close)
|
isPositionOpened := !s.Position.IsClosed() && !s.Position.IsDust(kline.Close)
|
||||||
|
|
||||||
if isPositionOpened && s.Position.IsShort() {
|
if isPositionOpened && s.Position.IsShort() {
|
||||||
roi := s.Position.ROI(kline.Close)
|
return
|
||||||
if !s.Exit.RoiMinTakeProfitPercentage.IsZero() {
|
|
||||||
if roi.Compare(s.Exit.RoiMinTakeProfitPercentage) > 0 {
|
|
||||||
if !s.Exit.LowerShadowRatio.IsZero() && kline.GetLowerShadowHeight().Div(kline.Close).Compare(s.Exit.LowerShadowRatio) > 0 {
|
|
||||||
bbgo.Notify("%s TakeProfit triggered at price %f: by shadow ratio %f",
|
|
||||||
s.Symbol,
|
|
||||||
kline.Close.Float64(),
|
|
||||||
kline.GetLowerShadowRatio().Float64(), kline)
|
|
||||||
_ = s.ClosePosition(ctx, fixedpoint.One)
|
|
||||||
return
|
|
||||||
} else if s.Exit.CumulatedVolume != nil && s.Exit.CumulatedVolume.Enabled {
|
|
||||||
if klines, ok := store.KLinesOfInterval(s.Interval); ok {
|
|
||||||
var cbv = fixedpoint.Zero
|
|
||||||
var cqv = fixedpoint.Zero
|
|
||||||
for i := 0; i < s.Exit.CumulatedVolume.Window; i++ {
|
|
||||||
last := (*klines)[len(*klines)-1-i]
|
|
||||||
cqv = cqv.Add(last.QuoteVolume)
|
|
||||||
cbv = cbv.Add(last.Volume)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cqv.Compare(s.Exit.CumulatedVolume.MinQuoteVolume) > 0 {
|
|
||||||
bbgo.Notify("%s TakeProfit triggered at price %f: by cumulated volume (window: %d) %f > %f",
|
|
||||||
s.Symbol,
|
|
||||||
kline.Close.Float64(),
|
|
||||||
s.Exit.CumulatedVolume.Window,
|
|
||||||
cqv.Float64(),
|
|
||||||
s.Exit.CumulatedVolume.MinQuoteVolume.Float64())
|
|
||||||
_ = s.ClosePosition(ctx, fixedpoint.One)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.pivotLowPrices) == 0 {
|
if len(s.pivotLowPrices) == 0 {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user