mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
pivotshort: add TradeStats
This commit is contained in:
parent
b79e4f2fb8
commit
260857b5b1
|
@ -3,8 +3,10 @@ package pivotshort
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/bbgo"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
|
@ -12,6 +14,39 @@ import (
|
|||
"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"
|
||||
|
||||
var log = logrus.WithField("strategy", ID)
|
||||
|
@ -63,6 +98,7 @@ type Strategy struct {
|
|||
// persistence fields
|
||||
Position *types.Position `json:"position,omitempty" persistence:"position"`
|
||||
ProfitStats *types.ProfitStats `json:"profitStats,omitempty" persistence:"profit_stats"`
|
||||
TradeStats *TradeStats `persistence:"trade_stats"`
|
||||
|
||||
PivotLength int `json:"pivotLength"`
|
||||
|
||||
|
@ -152,6 +188,7 @@ func (s *Strategy) ClosePosition(ctx context.Context, percentage fixedpoint.Valu
|
|||
s.tradeCollector.Process()
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Strategy) InstanceID() string {
|
||||
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)
|
||||
}
|
||||
|
||||
if s.TradeStats == nil {
|
||||
s.TradeStats = &TradeStats{}
|
||||
}
|
||||
|
||||
instanceID := s.InstanceID()
|
||||
|
||||
// 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.Notify(&s.ProfitStats)
|
||||
|
||||
s.TradeStats.Add(profit)
|
||||
|
||||
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() {
|
||||
// calculate return rate
|
||||
// TODO: apply quantity to this formula
|
||||
roi := kline.Close.Sub(s.Position.AverageCost).Div(s.Position.AverageCost)
|
||||
if roi.Compare(s.Exit.RoiStopLossPercentage) > 0 {
|
||||
roi := s.Position.AverageCost.Sub(kline.Close).Div(s.Position.AverageCost)
|
||||
if roi.Compare(s.Exit.RoiStopLossPercentage.Neg()) < 0 {
|
||||
// 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 {
|
||||
log.WithError(err).Errorf("graceful cancel order error")
|
||||
}
|
||||
|
@ -255,8 +298,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
|||
}
|
||||
|
||||
return
|
||||
} else if roi.Compare(s.Exit.RoiTakeProfitPercentage.Neg()) < 0 {
|
||||
s.Notify("%s TakeProfit triggered at price %f", s.Symbol, kline.Close.Float64())
|
||||
} else if roi.Compare(s.Exit.RoiTakeProfitPercentage) > 0 {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user