mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 14:55:16 +00:00
pivotshort: add TradeStats
This commit is contained in:
parent
b79e4f2fb8
commit
260857b5b1
|
@ -3,8 +3,10 @@ package pivotshort
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/bbgo"
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
@ -12,6 +14,39 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TradeStats struct {
|
||||||
|
WinningRatio fixedpoint.Value `json:"winningRatio" yaml:"winningRatio"`
|
||||||
|
NumOfLossTrade int `json:"numOfLossTrade" yaml:"numOfLossTrade"`
|
||||||
|
NumOfProfitTrade int `json:"numOfProfitTrade" yaml:"numOfProfitTrade"`
|
||||||
|
GrossProfit fixedpoint.Value `json:"grossProfit" yaml:"grossProfit"`
|
||||||
|
GrossLoss fixedpoint.Value `json:"grossLoss" yaml:"grossLoss"`
|
||||||
|
Profits []fixedpoint.Value `json:"profits" yaml:"profits"`
|
||||||
|
Losses []fixedpoint.Value `json:"losses" yaml:"losses"`
|
||||||
|
MostProfitableTrade fixedpoint.Value `json:"mostProfitableTrade" yaml:"mostProfitableTrade"`
|
||||||
|
MostLossTrade fixedpoint.Value `json:"mostLossTrade" yaml:"mostLossTrade"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TradeStats) Add(pnl fixedpoint.Value) {
|
||||||
|
if pnl.Sign() > 0 {
|
||||||
|
s.NumOfProfitTrade++
|
||||||
|
s.Profits = append(s.Profits, pnl)
|
||||||
|
s.GrossProfit = s.GrossProfit.Add(pnl)
|
||||||
|
s.MostProfitableTrade = fixedpoint.Max(s.MostProfitableTrade, pnl)
|
||||||
|
} else {
|
||||||
|
s.NumOfLossTrade++
|
||||||
|
s.Losses = append(s.Losses, pnl)
|
||||||
|
s.GrossLoss = s.GrossLoss.Add(pnl)
|
||||||
|
s.MostLossTrade = fixedpoint.Min(s.MostLossTrade, pnl)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.WinningRatio = fixedpoint.NewFromFloat(float64(s.NumOfProfitTrade) / float64(s.NumOfLossTrade))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TradeStats) String() string {
|
||||||
|
out, _ := yaml.Marshal(s)
|
||||||
|
return string(out)
|
||||||
|
}
|
||||||
|
|
||||||
const ID = "pivotshort"
|
const ID = "pivotshort"
|
||||||
|
|
||||||
var log = logrus.WithField("strategy", ID)
|
var log = logrus.WithField("strategy", ID)
|
||||||
|
@ -63,6 +98,7 @@ type Strategy struct {
|
||||||
// persistence fields
|
// persistence fields
|
||||||
Position *types.Position `json:"position,omitempty" persistence:"position"`
|
Position *types.Position `json:"position,omitempty" persistence:"position"`
|
||||||
ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"`
|
ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"`
|
||||||
|
TradeStats *TradeStats `persistence:"trade_stats"`
|
||||||
|
|
||||||
PivotLength int `json:"pivotLength"`
|
PivotLength int `json:"pivotLength"`
|
||||||
|
|
||||||
|
@ -152,6 +188,7 @@ func (s *Strategy) ClosePosition(ctx context.Context, percentage fixedpoint.Valu
|
||||||
s.tradeCollector.Process()
|
s.tradeCollector.Process()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) InstanceID() string {
|
func (s *Strategy) InstanceID() string {
|
||||||
return fmt.Sprintf("%s:%s", ID, s.Symbol)
|
return fmt.Sprintf("%s:%s", ID, s.Symbol)
|
||||||
}
|
}
|
||||||
|
@ -174,6 +211,10 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
s.ProfitStats = types.NewProfitStats(s.Market)
|
s.ProfitStats = types.NewProfitStats(s.Market)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.TradeStats == nil {
|
||||||
|
s.TradeStats = &TradeStats{}
|
||||||
|
}
|
||||||
|
|
||||||
instanceID := s.InstanceID()
|
instanceID := s.InstanceID()
|
||||||
|
|
||||||
// Always update the position fields
|
// Always update the position fields
|
||||||
|
@ -198,6 +239,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
s.ProfitStats.AddProfit(p)
|
s.ProfitStats.AddProfit(p)
|
||||||
s.Notify(&s.ProfitStats)
|
s.Notify(&s.ProfitStats)
|
||||||
|
|
||||||
|
s.TradeStats.Add(profit)
|
||||||
|
|
||||||
s.Environment.RecordPosition(s.Position, trade, &p)
|
s.Environment.RecordPosition(s.Position, trade, &p)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -242,10 +285,10 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
if isPositionOpened && s.Position.IsShort() {
|
if isPositionOpened && s.Position.IsShort() {
|
||||||
// calculate return rate
|
// calculate return rate
|
||||||
// TODO: apply quantity to this formula
|
// TODO: apply quantity to this formula
|
||||||
roi := kline.Close.Sub(s.Position.AverageCost).Div(s.Position.AverageCost)
|
roi := s.Position.AverageCost.Sub(kline.Close).Div(s.Position.AverageCost)
|
||||||
if roi.Compare(s.Exit.RoiStopLossPercentage) > 0 {
|
if roi.Compare(s.Exit.RoiStopLossPercentage.Neg()) < 0 {
|
||||||
// SL
|
// SL
|
||||||
s.Notify("%s ROI StopLoss triggered at price %f", s.Symbol, kline.Close.Float64())
|
s.Notify("%s ROI StopLoss triggered at price %f, ROI = %s", s.Symbol, kline.Close.Float64(), roi.Percentage())
|
||||||
if err := s.activeMakerOrders.GracefulCancel(ctx, s.session.Exchange); err != nil {
|
if err := s.activeMakerOrders.GracefulCancel(ctx, s.session.Exchange); err != nil {
|
||||||
log.WithError(err).Errorf("graceful cancel order error")
|
log.WithError(err).Errorf("graceful cancel order error")
|
||||||
}
|
}
|
||||||
|
@ -255,8 +298,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
} else if roi.Compare(s.Exit.RoiTakeProfitPercentage.Neg()) < 0 {
|
} else if roi.Compare(s.Exit.RoiTakeProfitPercentage) > 0 {
|
||||||
s.Notify("%s TakeProfit triggered at price %f", s.Symbol, kline.Close.Float64())
|
s.Notify("%s TakeProfit triggered at price %f, ROI take profit percentage by %s", s.Symbol, kline.Close.Float64(), roi.Percentage(), kline)
|
||||||
if err := s.activeMakerOrders.GracefulCancel(ctx, s.session.Exchange); err != nil {
|
if err := s.activeMakerOrders.GracefulCancel(ctx, s.session.Exchange); err != nil {
|
||||||
log.WithError(err).Errorf("graceful cancel order error")
|
log.WithError(err).Errorf("graceful cancel order error")
|
||||||
}
|
}
|
||||||
|
@ -336,6 +379,11 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
s.Graceful.OnShutdown(func(ctx context.Context, wg *sync.WaitGroup) {
|
||||||
|
log.Info(s.TradeStats.String())
|
||||||
|
wg.Done()
|
||||||
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user