mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 14:55:16 +00:00
pivotshort: fix support take profit method
This commit is contained in:
parent
74cac6e977
commit
278fbb7b51
|
@ -47,22 +47,22 @@ exchangeStrategies:
|
|||
|
||||
resistanceShort:
|
||||
enabled: true
|
||||
interval: 1h
|
||||
window: 8
|
||||
interval: 5m
|
||||
window: 80
|
||||
|
||||
quantity: 10.0
|
||||
|
||||
# minDistance is used to ignore the place that is too near to the current price
|
||||
minDistance: 3%
|
||||
minDistance: 5%
|
||||
groupDistance: 1%
|
||||
|
||||
# ratio is the ratio of the resistance price,
|
||||
# higher the ratio, lower the price
|
||||
# first_layer_price = resistance_price * (1 - ratio)
|
||||
# second_layer_price = (resistance_price * (1 - ratio)) * (2 * layerSpread)
|
||||
ratio: 0%
|
||||
numOfLayers: 1
|
||||
layerSpread: 0.1%
|
||||
# higher the ratio, higher the sell price
|
||||
# first_layer_price = resistance_price * (1 + ratio)
|
||||
# second_layer_price = (resistance_price * (1 + ratio)) * (2 * layerSpread)
|
||||
ratio: 1.2%
|
||||
numOfLayers: 3
|
||||
layerSpread: 0.4%
|
||||
|
||||
exits:
|
||||
# (0) roiStopLoss is the stop loss percentage of the position ROI (currently the price change)
|
||||
|
|
66
pkg/strategy/pivotshort/math.go
Normal file
66
pkg/strategy/pivotshort/math.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package pivotshort
|
||||
|
||||
import "sort"
|
||||
|
||||
func lower(arr []float64, x float64) []float64 {
|
||||
sort.Float64s(arr)
|
||||
|
||||
var rst []float64
|
||||
for _, a := range arr {
|
||||
// filter prices that are lower than the current closed price
|
||||
if a > x {
|
||||
continue
|
||||
}
|
||||
|
||||
rst = append(rst, a)
|
||||
}
|
||||
|
||||
return rst
|
||||
}
|
||||
|
||||
func higher(arr []float64, x float64) []float64 {
|
||||
sort.Float64s(arr)
|
||||
|
||||
var rst []float64
|
||||
for _, a := range arr {
|
||||
// filter prices that are lower than the current closed price
|
||||
if a < x {
|
||||
continue
|
||||
}
|
||||
rst = append(rst, a)
|
||||
}
|
||||
|
||||
return rst
|
||||
}
|
||||
|
||||
func group(arr []float64, minDistance float64) []float64 {
|
||||
if len(arr) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var groups []float64
|
||||
var grp = []float64{arr[0]}
|
||||
for _, price := range arr {
|
||||
avg := average(grp)
|
||||
if (price / avg) > (1.0 + minDistance) {
|
||||
groups = append(groups, avg)
|
||||
grp = []float64{price}
|
||||
} else {
|
||||
grp = append(grp, price)
|
||||
}
|
||||
}
|
||||
|
||||
if len(grp) > 0 {
|
||||
groups = append(groups, average(grp))
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
func average(arr []float64) float64 {
|
||||
s := 0.0
|
||||
for _, a := range arr {
|
||||
s += a
|
||||
}
|
||||
return s / float64(len(arr))
|
||||
}
|
|
@ -2,7 +2,6 @@ package pivotshort
|
|||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
|
@ -98,6 +97,9 @@ func (s *ResistanceShort) updateNextResistancePrice(closePrice fixedpoint.Value)
|
|||
return true
|
||||
}
|
||||
|
||||
// if the current sell price is out-dated
|
||||
// or
|
||||
// the next resistance is lower than the current one.
|
||||
currentSellPrice := s.currentResistancePrice.Mul(one.Add(s.Ratio))
|
||||
if closePrice.Compare(currentSellPrice) > 0 ||
|
||||
nextResistancePrice.Compare(currentSellPrice) < 0 {
|
||||
|
@ -184,69 +186,6 @@ func findPossibleSupportPrices(closePrice float64, minDistance float64, lows []f
|
|||
return group(lower(lows, closePrice), minDistance)
|
||||
}
|
||||
|
||||
func lower(arr []float64, x float64) []float64 {
|
||||
sort.Float64s(arr)
|
||||
|
||||
var rst []float64
|
||||
for _, a := range arr {
|
||||
// filter prices that are lower than the current closed price
|
||||
if a > x {
|
||||
continue
|
||||
}
|
||||
|
||||
rst = append(rst, a)
|
||||
}
|
||||
|
||||
return rst
|
||||
}
|
||||
|
||||
func higher(arr []float64, x float64) []float64 {
|
||||
sort.Float64s(arr)
|
||||
|
||||
var rst []float64
|
||||
for _, a := range arr {
|
||||
// filter prices that are lower than the current closed price
|
||||
if a < x {
|
||||
continue
|
||||
}
|
||||
rst = append(rst, a)
|
||||
}
|
||||
|
||||
return rst
|
||||
}
|
||||
|
||||
func group(arr []float64, minDistance float64) []float64 {
|
||||
if len(arr) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var groups []float64
|
||||
var grp = []float64{arr[0]}
|
||||
for _, price := range arr {
|
||||
avg := average(grp)
|
||||
if (price / avg) > (1.0 + minDistance) {
|
||||
groups = append(groups, avg)
|
||||
grp = []float64{price}
|
||||
} else {
|
||||
grp = append(grp, price)
|
||||
}
|
||||
}
|
||||
|
||||
if len(grp) > 0 {
|
||||
groups = append(groups, average(grp))
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
func findPossibleResistancePrices(closePrice float64, minDistance float64, lows []float64) []float64 {
|
||||
return group(higher(lows, closePrice), minDistance)
|
||||
}
|
||||
|
||||
func average(arr []float64) float64 {
|
||||
s := 0.0
|
||||
for _, a := range arr {
|
||||
s += a
|
||||
}
|
||||
return s / float64(len(arr))
|
||||
}
|
||||
|
|
|
@ -35,16 +35,42 @@ type SupportTakeProfit struct {
|
|||
types.IntervalWindow
|
||||
Ratio fixedpoint.Value `json:"ratio"`
|
||||
|
||||
pivot *indicator.Pivot
|
||||
orderExecutor *bbgo.GeneralOrderExecutor
|
||||
session *bbgo.ExchangeSession
|
||||
activeOrders *bbgo.ActiveOrderBook
|
||||
pivot *indicator.Pivot
|
||||
orderExecutor *bbgo.GeneralOrderExecutor
|
||||
session *bbgo.ExchangeSession
|
||||
activeOrders *bbgo.ActiveOrderBook
|
||||
currentSupportPrice fixedpoint.Value
|
||||
}
|
||||
|
||||
func (s *SupportTakeProfit) Subscribe(session *bbgo.ExchangeSession) {
|
||||
session.Subscribe(types.KLineChannel, s.Symbol, types.SubscribeOptions{Interval: s.Interval})
|
||||
}
|
||||
|
||||
func (s *SupportTakeProfit) updateSupportPrice(closePrice fixedpoint.Value) bool {
|
||||
supportPrices := findPossibleSupportPrices(closePrice.Float64(), 0.05, s.pivot.Lows)
|
||||
|
||||
if len(supportPrices) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// nextSupportPrice are sorted in decreasing order
|
||||
nextSupportPrice := fixedpoint.NewFromFloat(supportPrices[0])
|
||||
currentBuyPrice := s.currentSupportPrice.Mul(one.Add(s.Ratio))
|
||||
|
||||
if s.currentSupportPrice.IsZero() {
|
||||
s.currentSupportPrice = nextSupportPrice
|
||||
return true
|
||||
}
|
||||
|
||||
// the close price is already lower than the support price, than we should update
|
||||
if closePrice.Compare(currentBuyPrice) < 0 || nextSupportPrice.Compare(s.currentSupportPrice) > 0 {
|
||||
s.currentSupportPrice = nextSupportPrice
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *SupportTakeProfit) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.GeneralOrderExecutor) {
|
||||
s.session = session
|
||||
s.orderExecutor = orderExecutor
|
||||
|
@ -58,27 +84,23 @@ func (s *SupportTakeProfit) Bind(session *bbgo.ExchangeSession, orderExecutor *b
|
|||
preloadPivot(s.pivot, store)
|
||||
|
||||
session.UserDataStream.OnKLineClosed(types.KLineWith(s.Symbol, s.Interval, func(kline types.KLine) {
|
||||
supportPrices := findPossibleSupportPrices(kline.Close.Float64(), 0.1, s.pivot.Lows)
|
||||
// supportPrices are sorted in decreasing order
|
||||
if len(supportPrices) == 0 {
|
||||
log.Infof("support prices not found")
|
||||
return
|
||||
}
|
||||
|
||||
if !position.IsOpened(kline.Close) {
|
||||
return
|
||||
}
|
||||
|
||||
nextSupport := fixedpoint.NewFromFloat(supportPrices[0])
|
||||
buyPrice := nextSupport.Mul(one.Add(s.Ratio))
|
||||
quantity := position.GetQuantity()
|
||||
if !s.updateSupportPrice(kline.Close) {
|
||||
return
|
||||
}
|
||||
|
||||
buyPrice := s.currentSupportPrice.Mul(one.Add(s.Ratio))
|
||||
quantity := position.GetQuantity()
|
||||
ctx := context.Background()
|
||||
|
||||
if err := orderExecutor.GracefulCancelActiveOrderBook(ctx, s.activeOrders); err != nil {
|
||||
log.WithError(err).Errorf("cancel order failed")
|
||||
}
|
||||
|
||||
bbgo.Notify("placing %s take profit order at price %f", s.Symbol, buyPrice.Float64())
|
||||
createdOrders, err := orderExecutor.SubmitOrders(ctx, types.SubmitOrder{
|
||||
Symbol: symbol,
|
||||
Type: types.OrderTypeLimitMaker,
|
||||
|
@ -112,7 +134,7 @@ type Strategy struct {
|
|||
// ResistanceShort is one of the entry method
|
||||
ResistanceShort *ResistanceShort `json:"resistanceShort"`
|
||||
|
||||
SupportTakeProfit *SupportTakeProfit `json:"supportTakeProfit"`
|
||||
SupportTakeProfit []SupportTakeProfit `json:"supportTakeProfit"`
|
||||
|
||||
ExitMethods bbgo.ExitMethodSet `json:"exits"`
|
||||
|
||||
|
@ -141,9 +163,9 @@ func (s *Strategy) Subscribe(session *bbgo.ExchangeSession) {
|
|||
s.BreakLow.Subscribe(session)
|
||||
}
|
||||
|
||||
if s.SupportTakeProfit != nil {
|
||||
dynamic.InheritStructValues(s.SupportTakeProfit, s)
|
||||
s.SupportTakeProfit.Subscribe(session)
|
||||
for i := range s.SupportTakeProfit {
|
||||
dynamic.InheritStructValues(&s.SupportTakeProfit[i], s)
|
||||
s.SupportTakeProfit[i].Subscribe(session)
|
||||
}
|
||||
|
||||
if !bbgo.IsBackTesting {
|
||||
|
|
Loading…
Reference in New Issue
Block a user