diff --git a/pkg/bbgo/order_executor_general.go b/pkg/bbgo/order_executor_general.go index 026829466..31ea385bc 100644 --- a/pkg/bbgo/order_executor_general.go +++ b/pkg/bbgo/order_executor_general.go @@ -170,7 +170,7 @@ func (e *GeneralOrderExecutor) BindTradeStats(tradeStats *types.TradeStats) { return } - tradeStats.Add(profit) + tradeStats.AddProfit(profit) }) } @@ -181,7 +181,7 @@ func (e *GeneralOrderExecutor) BindProfitStats(profitStats *types.ProfitStats) { return } - profitStats.AddProfit(*profit) + profitStats.AddProfit(profit) if !e.disableNotify { Notify(profit) diff --git a/pkg/cmd/backtest.go b/pkg/cmd/backtest.go index f83dfae53..2440b6e94 100644 --- a/pkg/cmd/backtest.go +++ b/pkg/cmd/backtest.go @@ -338,7 +338,7 @@ var BacktestCmd = &cobra.Command{ if profit == nil { return } - tradeStats.Add(profit) + tradeStats.AddProfit(profit) }) tradeStatsMap[usedSymbol] = tradeStats diff --git a/pkg/report/profit_stats_tracker.go b/pkg/report/profit_stats_tracker.go index 1f61cacd6..8fbf0b7f3 100644 --- a/pkg/report/profit_stats_tracker.go +++ b/pkg/report/profit_stats_tracker.go @@ -84,7 +84,7 @@ func (p *ProfitStatsTracker) Rotate() { } func (p *ProfitStatsTracker) AddProfit(profit types.Profit) { - (*p.CurrentProfitStats).AddProfit(profit) + (*p.CurrentProfitStats).AddProfit(&profit) } func (p *ProfitStatsTracker) AddTrade(trade types.Trade) { diff --git a/pkg/report/stats_collector.go b/pkg/report/stats_collector.go new file mode 100644 index 000000000..9cf14c3e7 --- /dev/null +++ b/pkg/report/stats_collector.go @@ -0,0 +1,83 @@ +package report + +import ( + "github.com/c9s/bbgo/pkg/bbgo" + "github.com/c9s/bbgo/pkg/core" + "github.com/c9s/bbgo/pkg/types" +) + +type TradeAdder interface { + AddTrade(trade *types.Trade) +} + +type ProfitAdder interface { + AddProfit(trade *types.Profit) +} + +// StatsCollector is the v2 profit stats tracker +type StatsCollector struct { + Market types.Market `json:"market"` + Interval types.Interval `json:"interval"` + Window int `json:"window"` + + CurrentProfitStats *types.ProfitStats `json:"profitStats"` + AccumulatedProfitStats *types.ProfitStats `json:"accumulatedProfitStats"` + HistoryProfitStats []types.ProfitStats `json:"historyProfitStats"` + + CurrentTradeStats *types.TradeStats `json:"tradeStats"` + AccumulatedTradeStats *types.TradeStats `json:"accumulatedTradeStats"` + HistoryTradeStats []types.TradeStats `json:"historyTradeStats"` + + tradeCollector *core.TradeCollector +} + +func NewStatsCollector(market types.Market, interval types.Interval, window int, tradeCollector *core.TradeCollector) *StatsCollector { + return &StatsCollector{ + Market: market, + Interval: interval, + Window: window, + CurrentProfitStats: types.NewProfitStats(market), + CurrentTradeStats: types.NewTradeStats(market.Symbol), + AccumulatedProfitStats: types.NewProfitStats(market), + AccumulatedTradeStats: types.NewTradeStats(market.Symbol), + tradeCollector: tradeCollector, + } +} + +func (c *StatsCollector) Subscribe(session *bbgo.ExchangeSession) { + session.Subscribe(types.KLineChannel, c.Market.Symbol, types.SubscribeOptions{Interval: c.Interval}) +} + +func (c *StatsCollector) Bind(session *bbgo.ExchangeSession) { + c.tradeCollector.OnProfit(func(trade types.Trade, profit *types.Profit) { + if profit != nil { + c.CurrentProfitStats.AddProfit(profit) + c.AccumulatedProfitStats.AddProfit(profit) + } + + c.CurrentProfitStats.AddTrade(trade) + c.AccumulatedProfitStats.AddTrade(trade) + + c.CurrentTradeStats.AddProfit(profit) + c.AccumulatedTradeStats.AddProfit(profit) + }) + + // Rotate profitStats slice + session.MarketDataStream.OnKLineClosed(types.KLineWith(c.Market.Symbol, c.Interval, func(k types.KLine) { + // p.Rotate() + })) +} + +// Rotate the tracker to make a new ProfitStats to record the profits +func (c *StatsCollector) Rotate() { + c.HistoryProfitStats = append(c.HistoryProfitStats, *c.CurrentProfitStats) + c.HistoryTradeStats = append(c.HistoryTradeStats, *c.CurrentTradeStats) + /* + *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:] + } + */ +} diff --git a/pkg/strategy/drift/strategy.go b/pkg/strategy/drift/strategy.go index cf9c53e8e..96893f592 100644 --- a/pkg/strategy/drift/strategy.go +++ b/pkg/strategy/drift/strategy.go @@ -831,9 +831,9 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se if madeProfit { p := s.Position.NewProfit(trade, profit, netProfit) s.Environment.RecordPosition(s.Position, trade, &p) - s.TradeStats.Add(&p) + s.TradeStats.AddProfit(&p) s.ProfitStats.AddTrade(trade) - s.ProfitStats.AddProfit(p) + s.ProfitStats.AddProfit(&p) bbgo.Notify(&p) bbgo.Notify(s.ProfitStats) } diff --git a/pkg/strategy/fmaker/strategy.go b/pkg/strategy/fmaker/strategy.go index b62d790cd..0fca16221 100644 --- a/pkg/strategy/fmaker/strategy.go +++ b/pkg/strategy/fmaker/strategy.go @@ -193,7 +193,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se p.StrategyInstanceID = instanceID bbgo.Notify(&p) - s.ProfitStats.AddProfit(p) + s.ProfitStats.AddProfit(&p) bbgo.Notify(&s.ProfitStats) s.Environment.RecordPosition(s.Position, trade, &p) diff --git a/pkg/strategy/xfunding/strategy.go b/pkg/strategy/xfunding/strategy.go index 11f8be1e3..9746985de 100644 --- a/pkg/strategy/xfunding/strategy.go +++ b/pkg/strategy/xfunding/strategy.go @@ -1096,7 +1096,7 @@ func (s *Strategy) allocateOrderExecutor(ctx context.Context, session *bbgo.Exch if profit, netProfit, madeProfit := s.NeutralPosition.AddTrade(trade); madeProfit { p := s.NeutralPosition.NewProfit(trade, profit, netProfit) - s.ProfitStats.AddProfit(p) + s.ProfitStats.AddProfit(&p) } }) return orderExecutor diff --git a/pkg/strategy/xmaker/strategy.go b/pkg/strategy/xmaker/strategy.go index c8c376a5e..728500f46 100644 --- a/pkg/strategy/xmaker/strategy.go +++ b/pkg/strategy/xmaker/strategy.go @@ -809,7 +809,7 @@ func (s *Strategy) CrossRun( p.Strategy = ID p.StrategyInstanceID = instanceID bbgo.Notify(&p) - s.ProfitStats.AddProfit(p) + s.ProfitStats.AddProfit(&p) s.Environment.RecordPosition(s.Position, trade, &p) } diff --git a/pkg/types/profit.go b/pkg/types/profit.go index cca988414..4c2e1272b 100644 --- a/pkg/types/profit.go +++ b/pkg/types/profit.go @@ -216,7 +216,7 @@ func (s *ProfitStats) Init(market Market) { } } -func (s *ProfitStats) AddProfit(profit Profit) { +func (s *ProfitStats) AddProfit(profit *Profit) { if s.IsOver24Hours() { s.ResetToday(profit.TradedAt) } diff --git a/pkg/types/trade_stats.go b/pkg/types/trade_stats.go index b20d3791c..a38fda3b6 100644 --- a/pkg/types/trade_stats.go +++ b/pkg/types/trade_stats.go @@ -240,7 +240,7 @@ func (s *TradeStats) CsvRecords() [][]string { } } -func (s *TradeStats) Add(profit *Profit) { +func (s *TradeStats) AddProfit(profit *Profit) { if s.Symbol != "" && profit.Symbol != s.Symbol { return }