backtest: add gross profit and gross loss fields

This commit is contained in:
c9s 2022-07-12 19:50:28 +08:00
parent 7d232f86b8
commit a51f26e3a7
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
4 changed files with 45 additions and 12 deletions

View File

@ -19,6 +19,8 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
var bidVolume = fixedpoint.Zero var bidVolume = fixedpoint.Zero
var askVolume = fixedpoint.Zero var askVolume = fixedpoint.Zero
var feeUSD = fixedpoint.Zero var feeUSD = fixedpoint.Zero
var grossProfit = fixedpoint.Zero
var grossLoss = fixedpoint.Zero
if len(trades) == 0 { if len(trades) == 0 {
return &AverageCostPnlReport{ return &AverageCostPnlReport{
@ -64,6 +66,12 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
totalNetProfit = totalNetProfit.Add(netProfit) totalNetProfit = totalNetProfit.Add(netProfit)
} }
if profit.Sign() > 0 {
grossProfit = grossProfit.Add(profit)
} else if profit.Sign() < 0 {
grossLoss = grossLoss.Add(profit)
}
if trade.IsBuyer { if trade.IsBuyer {
bidVolume = bidVolume.Add(trade.Quantity) bidVolume = bidVolume.Add(trade.Quantity)
} else { } else {
@ -96,8 +104,12 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
Profit: totalProfit, Profit: totalProfit,
NetProfit: totalNetProfit, NetProfit: totalNetProfit,
UnrealizedProfit: unrealizedProfit, UnrealizedProfit: unrealizedProfit,
AverageCost: position.AverageCost,
FeeInUSD: totalProfit.Sub(totalNetProfit), GrossProfit: grossProfit,
CurrencyFees: currencyFees, GrossLoss: grossLoss,
AverageCost: position.AverageCost,
FeeInUSD: totalProfit.Sub(totalNetProfit),
CurrencyFees: currencyFees,
} }
} }

View File

@ -20,10 +20,14 @@ type AverageCostPnlReport struct {
Symbol string `json:"symbol"` Symbol string `json:"symbol"`
Market types.Market `json:"market"` Market types.Market `json:"market"`
NumTrades int `json:"numTrades"` NumTrades int `json:"numTrades"`
Profit fixedpoint.Value `json:"profit"` Profit fixedpoint.Value `json:"profit"`
NetProfit fixedpoint.Value `json:"netProfit"` UnrealizedProfit fixedpoint.Value `json:"unrealizedProfit"`
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"` AverageCost fixedpoint.Value `json:"averageCost"`
BuyVolume fixedpoint.Value `json:"buyVolume,omitempty"` BuyVolume fixedpoint.Value `json:"buyVolume,omitempty"`
SellVolume fixedpoint.Value `json:"sellVolume,omitempty"` SellVolume fixedpoint.Value `json:"sellVolume,omitempty"`

View File

@ -39,10 +39,16 @@ type SummaryReport struct {
InitialTotalBalances types.BalanceMap `json:"initialTotalBalances"` InitialTotalBalances types.BalanceMap `json:"initialTotalBalances"`
FinalTotalBalances types.BalanceMap `json:"finalTotalBalances"` 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 is the profit aggregated from the symbol reports
TotalProfit fixedpoint.Value `json:"totalProfit,omitempty"` TotalProfit fixedpoint.Value `json:"totalProfit,omitempty"`
TotalUnrealizedProfit fixedpoint.Value `json:"totalUnrealizedProfit,omitempty"` TotalUnrealizedProfit fixedpoint.Value `json:"totalUnrealizedProfit,omitempty"`
TotalGrossProfit fixedpoint.Value `json:"totalGrossProfit,omitempty"`
TotalGrossLoss fixedpoint.Value `json:"totalGrossLoss,omitempty"`
SymbolReports []SessionSymbolReport `json:"symbolReports,omitempty"` SymbolReports []SessionSymbolReport `json:"symbolReports,omitempty"`
Manifests Manifests `json:"manifests,omitempty"` Manifests Manifests `json:"manifests,omitempty"`
@ -75,13 +81,21 @@ type SessionSymbolReport struct {
Manifests Manifests `json:"manifests,omitempty"` 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) { func (r *SessionSymbolReport) Print(wantBaseAssetBaseline bool) {
color.Green("%s %s PROFIT AND LOSS REPORT", r.Exchange, r.Symbol) color.Green("%s %s PROFIT AND LOSS REPORT", r.Exchange, r.Symbol)
color.Green("===============================================") color.Green("===============================================")
r.PnL.Print() r.PnL.Print()
initQuoteAsset := inQuoteAsset(r.InitialBalances, r.Market, r.StartPrice) initQuoteAsset := r.InitialEquityValue()
finalQuoteAsset := inQuoteAsset(r.FinalBalances, r.Market, r.LastPrice) 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("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) 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) return WriteReportIndex(outputDirectory, reportIndex)
} }
// inQuoteAsset converts all balances in quote asset // InQuoteAsset converts all balances in quote asset
func inQuoteAsset(balances types.BalanceMap, market types.Market, price fixedpoint.Value) fixedpoint.Value { func InQuoteAsset(balances types.BalanceMap, market types.Market, price fixedpoint.Value) fixedpoint.Value {
quote := balances[market.QuoteCurrency] quote := balances[market.QuoteCurrency]
base := balances[market.BaseCurrency] base := balances[market.BaseCurrency]
return base.Total().Mul(price).Add(quote.Total()) return base.Total().Mul(price).Add(quote.Total())

View File

@ -493,7 +493,6 @@ var BacktestCmd = &cobra.Command{
} }
for _, session := range environ.Sessions() { for _, session := range environ.Sessions() {
for symbol, trades := range session.Trades { for symbol, trades := range session.Trades {
symbolReport, err := createSymbolReport(userConfig, session, symbol, trades.Trades) symbolReport, err := createSymbolReport(userConfig, session, symbol, trades.Trades)
if err != nil { if err != nil {
@ -504,6 +503,10 @@ var BacktestCmd = &cobra.Command{
summaryReport.SymbolReports = append(summaryReport.SymbolReports, *symbolReport) summaryReport.SymbolReports = append(summaryReport.SymbolReports, *symbolReport)
summaryReport.TotalProfit = symbolReport.PnL.Profit summaryReport.TotalProfit = symbolReport.PnL.Profit
summaryReport.TotalUnrealizedProfit = symbolReport.PnL.UnrealizedProfit 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 // write report to a file
if generatingReport { if generatingReport {