diff --git a/pkg/bbgo/environment.go b/pkg/bbgo/environment.go index cf77ad2a2..187bc5020 100644 --- a/pkg/bbgo/environment.go +++ b/pkg/bbgo/environment.go @@ -22,6 +22,7 @@ import ( "gopkg.in/tucnak/telebot.v2" "github.com/c9s/bbgo/pkg/cmd/cmdutil" + "github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/interact" "github.com/c9s/bbgo/pkg/notifier/slacknotifier" "github.com/c9s/bbgo/pkg/notifier/telegramnotifier" @@ -596,16 +597,21 @@ func (environ *Environment) RecordPosition(position *types.Position, trade types position.StrategyInstanceID = profit.StrategyInstanceID } - if err := environ.PositionService.Insert(position, trade, profit.Profit); err != nil { - log.WithError(err).Errorf("can not insert position record") - } if profit != nil { + if err := environ.PositionService.Insert(position, trade, profit.Profit); err != nil { + log.WithError(err).Errorf("can not insert position record") + } if err := environ.ProfitService.Insert(*profit); err != nil { log.WithError(err).Errorf("can not insert profit record: %+v", profit) } + } else { + if err := environ.PositionService.Insert(position, trade, fixedpoint.Zero); err != nil { + log.WithError(err).Errorf("can not insert position record") + } } + // if: // 1) we are not using sync // 2) and not sync-ing trades from the user data stream diff --git a/pkg/bbgo/tradecollector.go b/pkg/bbgo/tradecollector.go index 1b9e63fd0..f81b37f3c 100644 --- a/pkg/bbgo/tradecollector.go +++ b/pkg/bbgo/tradecollector.go @@ -23,7 +23,7 @@ type TradeCollector struct { doneTrades map[types.TradeKey]struct{} recoverCallbacks []func(trade types.Trade) - tradeCallbacks []func(trade types.Trade) + tradeCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value) positionUpdateCallbacks []func(position *types.Position) profitCallbacks []func(trade types.Trade, profit, netProfit fixedpoint.Value) } @@ -108,10 +108,10 @@ func (c *TradeCollector) Process() bool { if c.orderStore.Exists(trade.OrderID) { c.doneTrades[key] = struct{}{} if profit, netProfit, madeProfit := c.position.AddTrade(trade); madeProfit { - c.EmitTrade(trade) + c.EmitTrade(trade, fixedpoint.Zero, fixedpoint.Zero) c.EmitProfit(trade, profit, netProfit) } else { - c.EmitTrade(trade) + c.EmitTrade(trade, fixedpoint.Zero, fixedpoint.Zero) } positionChanged = true return true @@ -139,10 +139,10 @@ func (c *TradeCollector) processTrade(trade types.Trade) bool { } if profit, netProfit, madeProfit := c.position.AddTrade(trade); madeProfit { - c.EmitTrade(trade) + c.EmitTrade(trade, fixedpoint.Zero, fixedpoint.Zero) c.EmitProfit(trade, profit, netProfit) } else { - c.EmitTrade(trade) + c.EmitTrade(trade, fixedpoint.Zero, fixedpoint.Zero) } c.EmitPositionUpdate(c.position) c.doneTrades[key] = struct{}{} diff --git a/pkg/bbgo/tradecollector_callbacks.go b/pkg/bbgo/tradecollector_callbacks.go index 2819099f9..af8bf1bd1 100644 --- a/pkg/bbgo/tradecollector_callbacks.go +++ b/pkg/bbgo/tradecollector_callbacks.go @@ -17,13 +17,13 @@ func (c *TradeCollector) EmitRecover(trade types.Trade) { } } -func (c *TradeCollector) OnTrade(cb func(trade types.Trade)) { +func (c *TradeCollector) OnTrade(cb func(trade types.Trade, profit fixedpoint.Value, netProfit fixedpoint.Value)) { c.tradeCallbacks = append(c.tradeCallbacks, cb) } -func (c *TradeCollector) EmitTrade(trade types.Trade) { +func (c *TradeCollector) EmitTrade(trade types.Trade, profit fixedpoint.Value, netProfit fixedpoint.Value) { for _, cb := range c.tradeCallbacks { - cb(trade) + cb(trade, profit, netProfit) } } diff --git a/pkg/strategy/bollmaker/strategy.go b/pkg/strategy/bollmaker/strategy.go index cecf90b59..1a649289a 100644 --- a/pkg/strategy/bollmaker/strategy.go +++ b/pkg/strategy/bollmaker/strategy.go @@ -566,22 +566,26 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se s.orderStore.BindStream(session.UserDataStream) s.tradeCollector = bbgo.NewTradeCollector(s.Symbol, s.state.Position, s.orderStore) - s.tradeCollector.OnProfit(func(trade types.Trade, profit fixedpoint.Value, netProfit fixedpoint.Value) { - log.Infof("generated profit: %v", profit) - p := s.state.Position.NewProfit(trade, profit, netProfit) - p.Strategy = ID - p.StrategyInstanceID = instanceID - s.Environment.RecordPosition(s.state.Position, trade, &p) - - s.state.ProfitStats.AddProfit(p) - s.Notify(&p) + s.tradeCollector.OnProfit(func(trade types.Trade, profit, netProfit fixedpoint.Value) { s.Notify(&s.state.ProfitStats) }) - s.tradeCollector.OnTrade(func(trade types.Trade) { + s.tradeCollector.OnTrade(func(trade types.Trade, profit, netProfit fixedpoint.Value) { s.Notifiability.Notify(trade) s.state.ProfitStats.AddTrade(trade) - s.Environment.RecordPosition(s.state.Position, trade, nil) + + if profit.Compare(fixedpoint.Zero) == 0 { + s.Environment.RecordPosition(s.state.Position, trade, nil) + } else { + log.Infof("%s generated profit: %v", s.Symbol, profit) + p := s.state.Position.NewProfit(trade, profit, netProfit) + p.Strategy = ID + p.StrategyInstanceID = instanceID + s.state.ProfitStats.AddProfit(p) + s.Notify(&p) + + s.Environment.RecordPosition(s.state.Position, trade, &p) + } }) s.tradeCollector.OnPositionUpdate(func(position *types.Position) { diff --git a/pkg/strategy/grid/strategy.go b/pkg/strategy/grid/strategy.go index f03d2df7b..c1d224a4f 100644 --- a/pkg/strategy/grid/strategy.go +++ b/pkg/strategy/grid/strategy.go @@ -605,7 +605,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se s.tradeCollector = bbgo.NewTradeCollector(s.Symbol, s.state.Position, s.orderStore) - s.tradeCollector.OnTrade(func(trade types.Trade) { + s.tradeCollector.OnTrade(func(trade types.Trade, profit, netProfit fixedpoint.Value) { s.Notifiability.Notify(trade) s.state.ProfitStats.AddTrade(trade) }) diff --git a/pkg/strategy/xmaker/strategy.go b/pkg/strategy/xmaker/strategy.go index 6a02a9be5..572804ace 100644 --- a/pkg/strategy/xmaker/strategy.go +++ b/pkg/strategy/xmaker/strategy.go @@ -38,6 +38,7 @@ type Strategy struct { *bbgo.Graceful *bbgo.Notifiability *bbgo.Persistence + Environment *bbgo.Environment Symbol string `json:"symbol"` @@ -745,12 +746,12 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order s.tradeCollector = bbgo.NewTradeCollector(s.Symbol, s.state.Position, s.orderStore) if s.NotifyTrade { - s.tradeCollector.OnTrade(func(trade types.Trade) { + s.tradeCollector.OnTrade(func(trade types.Trade, profit, netProfit fixedpoint.Value) { s.Notifiability.Notify(trade) }) } - s.tradeCollector.OnTrade(func(trade types.Trade) { + s.tradeCollector.OnTrade(func(trade types.Trade, profit, netProfit fixedpoint.Value) { c := trade.PositionChange() if trade.Exchange == s.sourceSession.ExchangeName { s.state.CoveredPosition = s.state.CoveredPosition.Add(c) @@ -758,25 +759,25 @@ func (s *Strategy) CrossRun(ctx context.Context, orderExecutionRouter bbgo.Order s.state.ProfitStats.AddTrade(trade) + if profit.Compare(fixedpoint.Zero) == 0 { + s.Environment.RecordPosition(s.state.Position, trade, nil) + } else { + log.Infof("%s generated profit: %v", s.Symbol, profit) + + p := s.state.Position.NewProfit(trade, profit, netProfit) + p.Strategy = ID + p.StrategyInstanceID = instanceID + s.Notify(&p) + s.state.ProfitStats.AddProfit(p) + + s.Environment.RecordPosition(s.state.Position, trade, &p) + } + if err := s.SaveState(); err != nil { log.WithError(err).Error("save state error") } }) - s.tradeCollector.OnProfit(func(trade types.Trade, profit fixedpoint.Value, netProfit fixedpoint.Value) { - p := types.Profit{ - Symbol: s.Symbol, - Profit: profit, - NetProfit: netProfit, - QuoteQuantity: trade.QuoteQuantity, - ProfitMargin: profit.Div(trade.QuoteQuantity), - NetProfitMargin: netProfit.Div(trade.QuoteQuantity), - QuoteCurrency: s.state.Position.QuoteCurrency, - BaseCurrency: s.state.Position.BaseCurrency, - TradedAt: trade.Time.Time(), - } - s.state.ProfitStats.AddProfit(p) - s.Notify(&p) - }) + s.tradeCollector.OnPositionUpdate(func(position *types.Position) { s.Notifiability.Notify(position) })