diff --git a/pkg/accounting/pnl/avg_cost.go b/pkg/accounting/pnl/avg_cost.go index 23260bf3f..a288d874e 100644 --- a/pkg/accounting/pnl/avg_cost.go +++ b/pkg/accounting/pnl/avg_cost.go @@ -19,6 +19,8 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c var bidVolume = fixedpoint.Zero var askVolume = fixedpoint.Zero var feeUSD = fixedpoint.Zero + var grossProfit = fixedpoint.Zero + var grossLoss = fixedpoint.Zero if len(trades) == 0 { return &AverageCostPnlReport{ @@ -64,6 +66,12 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c totalNetProfit = totalNetProfit.Add(netProfit) } + if profit.Sign() > 0 { + grossProfit = grossProfit.Add(profit) + } else if profit.Sign() < 0 { + grossLoss = grossLoss.Add(profit) + } + if trade.IsBuyer { bidVolume = bidVolume.Add(trade.Quantity) } else { @@ -96,8 +104,12 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c Profit: totalProfit, NetProfit: totalNetProfit, UnrealizedProfit: unrealizedProfit, - AverageCost: position.AverageCost, - FeeInUSD: totalProfit.Sub(totalNetProfit), - CurrencyFees: currencyFees, + + GrossProfit: grossProfit, + GrossLoss: grossLoss, + + AverageCost: position.AverageCost, + FeeInUSD: totalProfit.Sub(totalNetProfit), + CurrencyFees: currencyFees, } } diff --git a/pkg/accounting/pnl/report.go b/pkg/accounting/pnl/report.go index 1dcd19f10..81d0ea2b9 100644 --- a/pkg/accounting/pnl/report.go +++ b/pkg/accounting/pnl/report.go @@ -20,10 +20,14 @@ type AverageCostPnlReport struct { Symbol string `json:"symbol"` Market types.Market `json:"market"` - NumTrades int `json:"numTrades"` - Profit fixedpoint.Value `json:"profit"` - NetProfit fixedpoint.Value `json:"netProfit"` - UnrealizedProfit fixedpoint.Value `json:"unrealizedProfit"` + NumTrades int `json:"numTrades"` + Profit fixedpoint.Value `json:"profit"` + UnrealizedProfit fixedpoint.Value `json:"unrealizedProfit"` + + NetProfit fixedpoint.Value `json:"netProfit"` + GrossProfit fixedpoint.Value `json:"grossProfit"` + GrossLoss fixedpoint.Value `json:"grossLoss"` + AverageCost fixedpoint.Value `json:"averageCost"` BuyVolume fixedpoint.Value `json:"buyVolume,omitempty"` SellVolume fixedpoint.Value `json:"sellVolume,omitempty"` diff --git a/pkg/backtest/report.go b/pkg/backtest/report.go index 782e6e4d6..1d52525b6 100644 --- a/pkg/backtest/report.go +++ b/pkg/backtest/report.go @@ -39,10 +39,16 @@ type SummaryReport struct { InitialTotalBalances types.BalanceMap `json:"initialTotalBalances"` FinalTotalBalances types.BalanceMap `json:"finalTotalBalances"` + InitialEquityValue fixedpoint.Value `json:"initialEquityValue"` + FinalEquityValue fixedpoint.Value `json:"finalEquityValue"` + // TotalProfit is the profit aggregated from the symbol reports TotalProfit fixedpoint.Value `json:"totalProfit,omitempty"` TotalUnrealizedProfit fixedpoint.Value `json:"totalUnrealizedProfit,omitempty"` + TotalGrossProfit fixedpoint.Value `json:"totalGrossProfit,omitempty"` + TotalGrossLoss fixedpoint.Value `json:"totalGrossLoss,omitempty"` + SymbolReports []SessionSymbolReport `json:"symbolReports,omitempty"` Manifests Manifests `json:"manifests,omitempty"` @@ -75,13 +81,21 @@ type SessionSymbolReport struct { Manifests Manifests `json:"manifests,omitempty"` } +func (r *SessionSymbolReport) InitialEquityValue() fixedpoint.Value { + return InQuoteAsset(r.InitialBalances, r.Market, r.StartPrice) +} + +func (r *SessionSymbolReport) FinalEquityValue() fixedpoint.Value { + return InQuoteAsset(r.FinalBalances, r.Market, r.StartPrice) +} + func (r *SessionSymbolReport) Print(wantBaseAssetBaseline bool) { color.Green("%s %s PROFIT AND LOSS REPORT", r.Exchange, r.Symbol) color.Green("===============================================") r.PnL.Print() - initQuoteAsset := inQuoteAsset(r.InitialBalances, r.Market, r.StartPrice) - finalQuoteAsset := inQuoteAsset(r.FinalBalances, r.Market, r.LastPrice) + initQuoteAsset := r.InitialEquityValue() + finalQuoteAsset := r.FinalEquityValue() color.Green("INITIAL ASSET IN %s ~= %s %s (1 %s = %v)", r.Market.QuoteCurrency, r.Market.FormatQuantity(initQuoteAsset), r.Market.QuoteCurrency, r.Market.BaseCurrency, r.StartPrice) color.Green("FINAL ASSET IN %s ~= %s %s (1 %s = %v)", r.Market.QuoteCurrency, r.Market.FormatQuantity(finalQuoteAsset), r.Market.QuoteCurrency, r.Market.BaseCurrency, r.LastPrice) @@ -186,8 +200,8 @@ func AddReportIndexRun(outputDirectory string, run Run) error { return WriteReportIndex(outputDirectory, reportIndex) } -// inQuoteAsset converts all balances in quote asset -func inQuoteAsset(balances types.BalanceMap, market types.Market, price fixedpoint.Value) fixedpoint.Value { +// InQuoteAsset converts all balances in quote asset +func InQuoteAsset(balances types.BalanceMap, market types.Market, price fixedpoint.Value) fixedpoint.Value { quote := balances[market.QuoteCurrency] base := balances[market.BaseCurrency] return base.Total().Mul(price).Add(quote.Total()) diff --git a/pkg/cmd/backtest.go b/pkg/cmd/backtest.go index cb2dbd990..596a51d94 100644 --- a/pkg/cmd/backtest.go +++ b/pkg/cmd/backtest.go @@ -493,7 +493,6 @@ var BacktestCmd = &cobra.Command{ } for _, session := range environ.Sessions() { - for symbol, trades := range session.Trades { symbolReport, err := createSymbolReport(userConfig, session, symbol, trades.Trades) if err != nil { @@ -504,6 +503,10 @@ var BacktestCmd = &cobra.Command{ summaryReport.SymbolReports = append(summaryReport.SymbolReports, *symbolReport) summaryReport.TotalProfit = symbolReport.PnL.Profit summaryReport.TotalUnrealizedProfit = symbolReport.PnL.UnrealizedProfit + summaryReport.InitialEquityValue = summaryReport.InitialEquityValue.Add(symbolReport.InitialEquityValue()) + summaryReport.FinalEquityValue = summaryReport.FinalEquityValue.Add(symbolReport.FinalEquityValue()) + summaryReport.TotalGrossProfit.Add(symbolReport.PnL.GrossProfit) + summaryReport.TotalGrossLoss.Add(symbolReport.PnL.GrossLoss) // write report to a file if generatingReport {