diff --git a/pkg/accounting/pnl/avg_cost.go b/pkg/accounting/pnl/avg_cost.go index a288d874e..f1c9b2182 100644 --- a/pkg/accounting/pnl/avg_cost.go +++ b/pkg/accounting/pnl/avg_cost.go @@ -14,7 +14,7 @@ type AverageCostCalculator struct { Market types.Market } -func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, currentPrice fixedpoint.Value) *AverageCostPnlReport { +func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, currentPrice fixedpoint.Value) *AverageCostPnLReport { // copy trades, so that we can truncate it. var bidVolume = fixedpoint.Zero var askVolume = fixedpoint.Zero @@ -23,7 +23,7 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c var grossLoss = fixedpoint.Zero if len(trades) == 0 { - return &AverageCostPnlReport{ + return &AverageCostPnLReport{ Symbol: symbol, Market: c.Market, LastPrice: currentPrice, @@ -90,12 +90,13 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c unrealizedProfit := currentPrice.Sub(position.AverageCost). Mul(position.GetBase()) - return &AverageCostPnlReport{ + return &AverageCostPnLReport{ Symbol: symbol, Market: c.Market, LastPrice: currentPrice, NumTrades: len(trades), StartTime: time.Time(trades[0].Time), + Position: position, BuyVolume: bidVolume, SellVolume: askVolume, diff --git a/pkg/accounting/pnl/report.go b/pkg/accounting/pnl/report.go index 81d0ea2b9..1c8152423 100644 --- a/pkg/accounting/pnl/report.go +++ b/pkg/accounting/pnl/report.go @@ -14,7 +14,7 @@ import ( "github.com/c9s/bbgo/pkg/types" ) -type AverageCostPnlReport struct { +type AverageCostPnLReport struct { LastPrice fixedpoint.Value `json:"lastPrice"` StartTime time.Time `json:"startTime"` Symbol string `json:"symbol"` @@ -27,6 +27,7 @@ type AverageCostPnlReport struct { NetProfit fixedpoint.Value `json:"netProfit"` GrossProfit fixedpoint.Value `json:"grossProfit"` GrossLoss fixedpoint.Value `json:"grossLoss"` + Position *types.Position `json:"position,omitempty"` AverageCost fixedpoint.Value `json:"averageCost"` BuyVolume fixedpoint.Value `json:"buyVolume,omitempty"` @@ -36,14 +37,14 @@ type AverageCostPnlReport struct { CurrencyFees map[string]fixedpoint.Value `json:"currencyFees"` } -func (report *AverageCostPnlReport) JSON() ([]byte, error) { +func (report *AverageCostPnLReport) JSON() ([]byte, error) { return json.MarshalIndent(report, "", " ") } -func (report AverageCostPnlReport) Print() { +func (report AverageCostPnLReport) Print() { color.Green("TRADES SINCE: %v", report.StartTime) color.Green("NUMBER OF TRADES: %d", report.NumTrades) - + color.Green(report.Position.String()) color.Green("AVERAGE COST: %s", types.USD.FormatMoney(report.AverageCost)) color.Green("BASE ASSET POSITION: %s", report.BaseAssetPosition.String()) @@ -69,7 +70,7 @@ func (report AverageCostPnlReport) Print() { } } -func (report AverageCostPnlReport) SlackAttachment() slack.Attachment { +func (report AverageCostPnLReport) SlackAttachment() slack.Attachment { var color = slackstyle.Red if report.UnrealizedProfit.Sign() > 0 { diff --git a/pkg/backtest/matching.go b/pkg/backtest/matching.go index 286fabe36..e0440eddd 100644 --- a/pkg/backtest/matching.go +++ b/pkg/backtest/matching.go @@ -277,9 +277,15 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) { if trade.IsBuyer { err = m.account.UseLockedBalance(m.Market.QuoteCurrency, trade.QuoteQuantity) - // here the fee currency is the base currency + // all-in buy trade, we can only deduct the fee from the quote quantity and re-calculate the base quantity q := trade.Quantity - if trade.FeeCurrency == m.Market.BaseCurrency { + qq := trade.QuoteQuantity + switch trade.FeeCurrency { + case m.Market.QuoteCurrency: + quoteFee := trade.Fee + qq = qq.Sub(quoteFee) + q = qq.Div(trade.Price) // re-calculate the base quantity according to the quote quantity and fee. + case m.Market.BaseCurrency: q = q.Sub(trade.Fee) } @@ -287,11 +293,17 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) { } else { err = m.account.UseLockedBalance(m.Market.BaseCurrency, trade.Quantity) - // here the fee currency is the quote currency + // all-in sell trade + q := trade.Quantity qq := trade.QuoteQuantity - if trade.FeeCurrency == m.Market.QuoteCurrency { + switch trade.FeeCurrency { + case m.Market.QuoteCurrency: qq = qq.Sub(trade.Fee) + case m.Market.BaseCurrency: + q = q.Sub(trade.Fee) + qq = q.Div(trade.Price) } + m.account.AddBalance(m.Market.QuoteCurrency, qq) } diff --git a/pkg/backtest/report.go b/pkg/backtest/report.go index 0f5de6a5f..7af0340c3 100644 --- a/pkg/backtest/report.go +++ b/pkg/backtest/report.go @@ -75,7 +75,7 @@ type SessionSymbolReport struct { Market types.Market `json:"market"` LastPrice fixedpoint.Value `json:"lastPrice,omitempty"` StartPrice fixedpoint.Value `json:"startPrice,omitempty"` - PnL *pnl.AverageCostPnlReport `json:"pnl,omitempty"` + PnL *pnl.AverageCostPnLReport `json:"pnl,omitempty"` InitialBalances types.BalanceMap `json:"initialBalances,omitempty"` FinalBalances types.BalanceMap `json:"finalBalances,omitempty"` Manifests Manifests `json:"manifests,omitempty"` diff --git a/pkg/cmd/backtest.go b/pkg/cmd/backtest.go index dfa14919c..56a6b43ef 100644 --- a/pkg/cmd/backtest.go +++ b/pkg/cmd/backtest.go @@ -583,7 +583,6 @@ var BacktestCmd = &cobra.Command{ color.Green("END TIME: %s\n", endTime.Format(time.RFC1123)) color.Green("INITIAL TOTAL BALANCE: %v\n", initTotalBalances) color.Green("FINAL TOTAL BALANCE: %v\n", finalTotalBalances) - for _, symbolReport := range summaryReport.SymbolReports { symbolReport.Print(wantBaseAssetBaseline) }