diff --git a/pkg/bbgo/order_executor_general.go b/pkg/bbgo/order_executor_general.go index b3d3bdf13..3a7320fbe 100644 --- a/pkg/bbgo/order_executor_general.go +++ b/pkg/bbgo/order_executor_general.go @@ -3,6 +3,7 @@ package bbgo import ( "context" "fmt" + "github.com/c9s/bbgo/pkg/report" "strings" "time" @@ -162,6 +163,10 @@ func (e *GeneralOrderExecutor) BindProfitStats(profitStats *types.ProfitStats) { }) } +func (e *GeneralOrderExecutor) BindProfitTracker(profitTracker *report.ProfitTracker) { + profitTracker.Bind(e.tradeCollector, e.session) +} + func (e *GeneralOrderExecutor) Bind() { e.activeMakerOrders.BindStream(e.session.UserDataStream) e.orderStore.BindStream(e.session.UserDataStream) diff --git a/pkg/report/profit_tracker.go b/pkg/report/profit_tracker.go new file mode 100644 index 000000000..04f67acae --- /dev/null +++ b/pkg/report/profit_tracker.go @@ -0,0 +1,63 @@ +package report + +import ( + "github.com/c9s/bbgo/pkg/bbgo" + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type ProfitTracker struct { + types.IntervalWindow + + market types.Market + profitStatsSlice []*types.ProfitStats + currentProfitStats **types.ProfitStats +} + +// InitOld is for backward capability. ps is the ProfitStats of the strategy, market is the strategy market +func (p *ProfitTracker) InitOld(ps **types.ProfitStats, market types.Market) { + p.market = market + + if *ps == nil { + *ps = types.NewProfitStats(p.market) + } + + p.currentProfitStats = ps + p.profitStatsSlice = append(p.profitStatsSlice, *ps) +} + +// Init initialize the tracker with the given market +func (p *ProfitTracker) Init(market types.Market) { + p.market = market + *p.currentProfitStats = types.NewProfitStats(p.market) + p.profitStatsSlice = append(p.profitStatsSlice, *p.currentProfitStats) +} + +func (p *ProfitTracker) Bind(tradeCollector *bbgo.TradeCollector, session *bbgo.ExchangeSession) { + // TODO: Register kline close callback + tradeCollector.OnProfit(func(trade types.Trade, profit *types.Profit) { + p.AddProfit(*profit) + }) + + tradeCollector.OnTrade(func(trade types.Trade, profit fixedpoint.Value, netProfit fixedpoint.Value) { + + }) + + session.MarketDataStream.OnKLineClosed(types.KLineWith(p.market.Symbol, p.Interval, func(kline types.KLine) { + + })) +} + +// Rotate the tracker to make a new ProfitStats to record the profits +func (p *ProfitTracker) Rotate() { + *p.currentProfitStats = types.NewProfitStats(p.market) + p.profitStatsSlice = append(p.profitStatsSlice, *p.currentProfitStats) + // Truncate + if len(p.profitStatsSlice) > p.Window { + p.profitStatsSlice = p.profitStatsSlice[len(p.profitStatsSlice)-p.Window:] + } +} + +func (p *ProfitTracker) AddProfit(profit types.Profit) { + (*p.currentProfitStats).AddProfit(profit) +} diff --git a/pkg/types/profit.go b/pkg/types/profit.go index 121e4bc88..aa57b2e22 100644 --- a/pkg/types/profit.go +++ b/pkg/types/profit.go @@ -164,6 +164,9 @@ type ProfitStats struct { TodayGrossProfit fixedpoint.Value `json:"todayGrossProfit,omitempty"` TodayGrossLoss fixedpoint.Value `json:"todayGrossLoss,omitempty"` TodaySince int64 `json:"todaySince,omitempty"` + + startTime time.Time + endTime time.Time } func NewProfitStats(market Market) *ProfitStats { @@ -182,6 +185,8 @@ func NewProfitStats(market Market) *ProfitStats { TodayGrossProfit: fixedpoint.Zero, TodayGrossLoss: fixedpoint.Zero, TodaySince: 0, + startTime: time.Now().UTC(), + endTime: time.Now().UTC(), } } @@ -223,6 +228,8 @@ func (s *ProfitStats) AddProfit(profit Profit) { s.AccumulatedGrossLoss = s.AccumulatedGrossLoss.Add(profit.Profit) s.TodayGrossLoss = s.TodayGrossLoss.Add(profit.Profit) } + + s.endTime = profit.TradedAt.UTC() } func (s *ProfitStats) AddTrade(trade Trade) {