refactor pnl calculator

This commit is contained in:
c9s 2020-07-11 11:23:48 +08:00
parent a177e55172
commit 19b225737f
3 changed files with 88 additions and 36 deletions

View File

@ -1,18 +1,23 @@
package bbgo
import "time"
type TradingContext struct {
KLineWindowSize int
KLineWindows map[string]KLineWindow
AverageBidPrice float64
Stock float64
Profit float64
CurrentPrice float64
Trades []Trade
TradeStartTime time.Time
Symbol string
// Market is the market configuration of a symbol
Market Market
AverageBidPrice float64
CurrentPrice float64
ProfitAndLossCalculator *ProfitAndLossCalculator
}
func (c *TradingContext) SetCurrentPrice(price float64) {
c.CurrentPrice = price
c.ProfitAndLossCalculator.SetCurrentPrice(price)
}
func (c *TradingContext) AddKLine(kline KLine) KLineWindow {
@ -26,7 +31,4 @@ func (c *TradingContext) AddKLine(kline KLine) KLineWindow {
return klineWindow
}
func (c *TradingContext) UpdatePnL() {
c.AverageBidPrice, c.Stock, c.Profit, _ = CalculateCostAndProfit(c.Trades, c.CurrentPrice)
}

View File

@ -1,6 +1,9 @@
package bbgo
import log "github.com/sirupsen/logrus"
import (
log "github.com/sirupsen/logrus"
"time"
)
func CalculateAverageCost(trades []Trade) (averageCost float64) {
var totalCost = 0.0
@ -19,7 +22,25 @@ func CalculateAverageCost(trades []Trade) (averageCost float64) {
return
}
func CalculateCostAndProfit(trades []Trade, currentPrice float64) (averageBidPrice, stock, profit, fee float64) {
type ProfitAndLossCalculator struct {
Symbol string
StartTime time.Time
CurrentPrice float64
Trades []Trade
}
func (c *ProfitAndLossCalculator) AddTrade(trade Trade) {
c.Trades = append(c.Trades, trade)
}
func (c *ProfitAndLossCalculator) SetCurrentPrice(price float64) {
c.CurrentPrice = price
}
func (c *ProfitAndLossCalculator) Calculate() *ProfitAndLossReport {
// copy trades, so that we can truncate it.
var trades = c.Trades
var bidVolume = 0.0
var bidAmount = 0.0
var bidFee = 0.0
@ -48,7 +69,8 @@ func CalculateCostAndProfit(trades []Trade, currentPrice float64) (averageBidPri
}
log.Infof("average bid price = (total amount %f + total fee %f) / volume %f", bidAmount, bidFee, bidVolume)
averageBidPrice = (bidAmount + bidFee) / bidVolume
profit := 0.0
averageBidPrice := (bidAmount + bidFee) / bidVolume
var feeRate = 0.001
var askVolume = 0.0
@ -66,14 +88,46 @@ func CalculateCostAndProfit(trades []Trade, currentPrice float64) (averageBidPri
profit -= askFee
stock = bidVolume - askVolume
stock := bidVolume - askVolume
futureFee := 0.0
if stock > 0 {
stockfee := currentPrice * feeRate * stock
profit += (currentPrice-averageBidPrice)*stock - stockfee
futureFee += stockfee
stockFee := c.CurrentPrice * feeRate * stock
profit += (c.CurrentPrice-averageBidPrice)*stock - stockFee
futureFee += stockFee
}
fee = bidFee + askFee + futureFee
return
fee := bidFee + askFee + futureFee
return &ProfitAndLossReport{
CurrentPrice: c.CurrentPrice,
StartTime: c.StartTime,
NumTrades: len(trades),
Profit: profit,
AverageBidPrice: averageBidPrice,
Stock: stock,
Fee: fee,
}
}
type ProfitAndLossReport struct {
CurrentPrice float64
StartTime time.Time
NumTrades int
Profit float64
AverageBidPrice float64
Stock float64
Fee float64
}
func (report ProfitAndLossReport) Print() {
log.Infof("trades since: %v", report.StartTime)
log.Infof("average bid price: %s", USD.FormatMoneyFloat64(report.AverageBidPrice))
log.Infof("Stock volume: %f", report.Stock)
log.Infof("current price: %s", USD.FormatMoneyFloat64(report.CurrentPrice))
log.Infof("overall profit: %s", USD.FormatMoneyFloat64(report.Profit))
}
func CalculateCostAndProfit(trades []Trade, currentPrice float64, startTime time.Time) (report *ProfitAndLossReport) {
}

View File

@ -105,16 +105,12 @@ func (t *Trader) ReportTrade(e *BinanceExecutionReportEvent, trade *Trade) {
}
func (t *Trader) ReportPnL() {
t.Context.UpdatePnL()
tradingCtx := t.Context
logrus.Infof("current price: %s", USD.FormatMoneyFloat64(tradingCtx.CurrentPrice))
logrus.Infof("average bid price: %s", USD.FormatMoneyFloat64(tradingCtx.AverageBidPrice))
logrus.Infof("Stock volume: %f", tradingCtx.Stock)
logrus.Infof("overall Profit: %s", USD.FormatMoneyFloat64(tradingCtx.Profit))
report := tradingCtx.ProfitAndLossCalculator.Calculate()
report.Print()
var color = ""
if tradingCtx.Profit > 0 {
if report.Profit > 0 {
color = slackstyle.Green
} else {
color = slackstyle.Red
@ -122,10 +118,10 @@ func (t *Trader) ReportPnL() {
_, _, err := t.Slack.PostMessageContext(context.Background(), t.TradingChannel,
slack.MsgOptionText(util.Render(
`:heavy_dollar_sign: Here is your *{{ .Symbol }}* PnL report collected since *{{ .startTime }}*`,
`:heavy_dollar_sign: Here is your *{{ .symbol }}* PnL report collected since *{{ .startTime }}*`,
map[string]interface{}{
"Symbol": tradingCtx.Symbol,
"startTime": tradingCtx.TradeStartTime.Format(time.RFC822),
"symbol": tradingCtx.Symbol,
"startTime": report.StartTime.Format(time.RFC822),
}), true),
slack.MsgOptionAttachments(slack.Attachment{
Title: "Profit and Loss report",
@ -140,31 +136,31 @@ func (t *Trader) ReportPnL() {
},
{
Title: "Profit",
Value: USD.FormatMoney(tradingCtx.Profit),
Value: USD.FormatMoney(report.Profit),
Short: true,
},
{
Title: "Current Price",
Value: USD.FormatMoney(tradingCtx.CurrentPrice),
Value: USD.FormatMoney(report.CurrentPrice),
Short: true,
},
{
Title: "Average Bid Price",
Value: USD.FormatMoney(tradingCtx.AverageBidPrice),
Value: USD.FormatMoney(report.AverageBidPrice),
Short: true,
},
{
Title: "Current Stock",
Value: tradingCtx.Market.FormatVolume(tradingCtx.Stock),
Value: tradingCtx.Market.FormatVolume(report.Stock),
Short: true,
},
{
Title: "Number of Trades",
Value: strconv.Itoa(len(tradingCtx.Trades)),
Value: strconv.Itoa(report.NumTrades),
Short: true,
},
},
Footer: tradingCtx.TradeStartTime.Format(time.RFC822),
Footer: report.StartTime.Format(time.RFC822),
FooterIcon: "",
}))