backtest,accounting: add position info to the average cost pnl report

This commit is contained in:
c9s 2022-09-01 19:02:18 +08:00
parent 45fb87f2b8
commit d2f9a352a2
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
5 changed files with 27 additions and 14 deletions

View File

@ -14,7 +14,7 @@ type AverageCostCalculator struct {
Market types.Market 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. // copy trades, so that we can truncate it.
var bidVolume = fixedpoint.Zero var bidVolume = fixedpoint.Zero
var askVolume = fixedpoint.Zero var askVolume = fixedpoint.Zero
@ -23,7 +23,7 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
var grossLoss = fixedpoint.Zero var grossLoss = fixedpoint.Zero
if len(trades) == 0 { if len(trades) == 0 {
return &AverageCostPnlReport{ return &AverageCostPnLReport{
Symbol: symbol, Symbol: symbol,
Market: c.Market, Market: c.Market,
LastPrice: currentPrice, LastPrice: currentPrice,
@ -90,12 +90,13 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
unrealizedProfit := currentPrice.Sub(position.AverageCost). unrealizedProfit := currentPrice.Sub(position.AverageCost).
Mul(position.GetBase()) Mul(position.GetBase())
return &AverageCostPnlReport{ return &AverageCostPnLReport{
Symbol: symbol, Symbol: symbol,
Market: c.Market, Market: c.Market,
LastPrice: currentPrice, LastPrice: currentPrice,
NumTrades: len(trades), NumTrades: len(trades),
StartTime: time.Time(trades[0].Time), StartTime: time.Time(trades[0].Time),
Position: position,
BuyVolume: bidVolume, BuyVolume: bidVolume,
SellVolume: askVolume, SellVolume: askVolume,

View File

@ -14,7 +14,7 @@ import (
"github.com/c9s/bbgo/pkg/types" "github.com/c9s/bbgo/pkg/types"
) )
type AverageCostPnlReport struct { type AverageCostPnLReport struct {
LastPrice fixedpoint.Value `json:"lastPrice"` LastPrice fixedpoint.Value `json:"lastPrice"`
StartTime time.Time `json:"startTime"` StartTime time.Time `json:"startTime"`
Symbol string `json:"symbol"` Symbol string `json:"symbol"`
@ -27,6 +27,7 @@ type AverageCostPnlReport struct {
NetProfit fixedpoint.Value `json:"netProfit"` NetProfit fixedpoint.Value `json:"netProfit"`
GrossProfit fixedpoint.Value `json:"grossProfit"` GrossProfit fixedpoint.Value `json:"grossProfit"`
GrossLoss fixedpoint.Value `json:"grossLoss"` GrossLoss fixedpoint.Value `json:"grossLoss"`
Position *types.Position `json:"position,omitempty"`
AverageCost fixedpoint.Value `json:"averageCost"` AverageCost fixedpoint.Value `json:"averageCost"`
BuyVolume fixedpoint.Value `json:"buyVolume,omitempty"` BuyVolume fixedpoint.Value `json:"buyVolume,omitempty"`
@ -36,14 +37,14 @@ type AverageCostPnlReport struct {
CurrencyFees map[string]fixedpoint.Value `json:"currencyFees"` CurrencyFees map[string]fixedpoint.Value `json:"currencyFees"`
} }
func (report *AverageCostPnlReport) JSON() ([]byte, error) { func (report *AverageCostPnLReport) JSON() ([]byte, error) {
return json.MarshalIndent(report, "", " ") return json.MarshalIndent(report, "", " ")
} }
func (report AverageCostPnlReport) Print() { func (report AverageCostPnLReport) Print() {
color.Green("TRADES SINCE: %v", report.StartTime) color.Green("TRADES SINCE: %v", report.StartTime)
color.Green("NUMBER OF TRADES: %d", report.NumTrades) 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("AVERAGE COST: %s", types.USD.FormatMoney(report.AverageCost))
color.Green("BASE ASSET POSITION: %s", report.BaseAssetPosition.String()) 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 var color = slackstyle.Red
if report.UnrealizedProfit.Sign() > 0 { if report.UnrealizedProfit.Sign() > 0 {

View File

@ -277,9 +277,15 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
if trade.IsBuyer { if trade.IsBuyer {
err = m.account.UseLockedBalance(m.Market.QuoteCurrency, trade.QuoteQuantity) 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 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) q = q.Sub(trade.Fee)
} }
@ -287,11 +293,17 @@ func (m *SimplePriceMatching) executeTrade(trade types.Trade) {
} else { } else {
err = m.account.UseLockedBalance(m.Market.BaseCurrency, trade.Quantity) 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 qq := trade.QuoteQuantity
if trade.FeeCurrency == m.Market.QuoteCurrency { switch trade.FeeCurrency {
case m.Market.QuoteCurrency:
qq = qq.Sub(trade.Fee) 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) m.account.AddBalance(m.Market.QuoteCurrency, qq)
} }

View File

@ -75,7 +75,7 @@ type SessionSymbolReport struct {
Market types.Market `json:"market"` Market types.Market `json:"market"`
LastPrice fixedpoint.Value `json:"lastPrice,omitempty"` LastPrice fixedpoint.Value `json:"lastPrice,omitempty"`
StartPrice fixedpoint.Value `json:"startPrice,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"` InitialBalances types.BalanceMap `json:"initialBalances,omitempty"`
FinalBalances types.BalanceMap `json:"finalBalances,omitempty"` FinalBalances types.BalanceMap `json:"finalBalances,omitempty"`
Manifests Manifests `json:"manifests,omitempty"` Manifests Manifests `json:"manifests,omitempty"`

View File

@ -583,7 +583,6 @@ var BacktestCmd = &cobra.Command{
color.Green("END TIME: %s\n", endTime.Format(time.RFC1123)) color.Green("END TIME: %s\n", endTime.Format(time.RFC1123))
color.Green("INITIAL TOTAL BALANCE: %v\n", initTotalBalances) color.Green("INITIAL TOTAL BALANCE: %v\n", initTotalBalances)
color.Green("FINAL TOTAL BALANCE: %v\n", finalTotalBalances) color.Green("FINAL TOTAL BALANCE: %v\n", finalTotalBalances)
for _, symbolReport := range summaryReport.SymbolReports { for _, symbolReport := range summaryReport.SymbolReports {
symbolReport.Print(wantBaseAssetBaseline) symbolReport.Print(wantBaseAssetBaseline)
} }