diff --git a/pkg/bbgo/order_executor_general.go b/pkg/bbgo/order_executor_general.go index 2bed9fd6cd..391a8d1813 100644 --- a/pkg/bbgo/order_executor_general.go +++ b/pkg/bbgo/order_executor_general.go @@ -143,7 +143,7 @@ func (e *GeneralOrderExecutor) BindTradeStats(tradeStats *types.TradeStats) { return } - tradeStats.Add(profit) + tradeStats.AddProfit(profit) }) } @@ -154,7 +154,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 2f8f97a536..232f1a08f7 100644 --- a/pkg/cmd/backtest.go +++ b/pkg/cmd/backtest.go @@ -344,7 +344,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 1f61cacd6e..8fbf0b7f32 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 0000000000..9cf14c3e7c --- /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 defaf7f459..7b00147675 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 b62d790cdc..0fca162211 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 50d1b392bb..6ab8a980f3 100644 --- a/pkg/strategy/xfunding/strategy.go +++ b/pkg/strategy/xfunding/strategy.go @@ -1153,7 +1153,7 @@ func (s *Strategy) allocateOrderExecutor( 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 b649525c89..b3580b4f76 100644 --- a/pkg/strategy/xmaker/strategy.go +++ b/pkg/strategy/xmaker/strategy.go @@ -2442,6 +2442,16 @@ func (s *Strategy) CrossRun( s.tradeCollector.OnTrade(func(trade types.Trade, profit fixedpoint.Value, netProfit fixedpoint.Value) { if profit.Compare(fixedpoint.Zero) == 0 { s.Environment.RecordPosition(s.Position, trade, nil) + } else { + log.Infof("%s generated profit: %v", s.Symbol, profit) + + p := s.Position.NewProfit(trade, profit, netProfit) + p.Strategy = ID + p.StrategyInstanceID = instanceID + bbgo.Notify(&p) + s.ProfitStats.AddProfit(&p) + + s.Environment.RecordPosition(s.Position, trade, &p) } }) s.tradeCollector.OnProfit(func(trade types.Trade, profit *types.Profit) { diff --git a/pkg/types/profit.go b/pkg/types/profit.go index c7d9710f24..d7e9fa6cda 100644 --- a/pkg/types/profit.go +++ b/pkg/types/profit.go @@ -215,7 +215,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 63940f42dd..28f52f6d21 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 }