bbgo_origin/bbgo/pnl.go

186 lines
4.8 KiB
Go
Raw Normal View History

2020-06-08 02:42:50 +00:00
package bbgo
2020-07-11 03:23:48 +00:00
import (
2020-07-12 14:52:37 +00:00
"strconv"
2020-07-11 04:37:09 +00:00
"strings"
2020-07-11 03:23:48 +00:00
"time"
2020-09-05 08:22:46 +00:00
log "github.com/sirupsen/logrus"
"github.com/slack-go/slack"
2020-09-19 00:13:32 +00:00
"github.com/c9s/bbgo/pkg/bbgo/slackstyle"
2020-09-05 08:22:46 +00:00
"github.com/c9s/bbgo/pkg/bbgo/types"
2020-07-11 03:23:48 +00:00
)
2020-06-08 02:42:50 +00:00
2020-07-11 03:23:48 +00:00
type ProfitAndLossCalculator struct {
2020-08-03 12:06:33 +00:00
Symbol string
StartTime time.Time
CurrentPrice float64
Trades []types.Trade
TradingFeeCurrency string
2020-07-11 03:23:48 +00:00
}
2020-07-12 14:52:37 +00:00
func (c *ProfitAndLossCalculator) AddTrade(trade types.Trade) {
2020-07-11 03:23:48 +00:00
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
2020-06-08 02:42:50 +00:00
var bidVolume = 0.0
var bidAmount = 0.0
2020-07-13 05:41:51 +00:00
var askVolume = 0.0
2020-08-05 10:49:32 +00:00
var feeUSD = 0.0
var bidFeeUSD = 0.0
2020-08-04 01:47:54 +00:00
var feeRate = 0.0015
2020-07-13 05:41:51 +00:00
2020-08-05 10:39:08 +00:00
var currencyFees = map[string]float64{}
2020-08-03 12:06:33 +00:00
for _, trade := range trades {
if trade.Symbol == c.Symbol {
if trade.IsBuyer {
bidVolume += trade.Quantity
bidAmount += trade.Price * trade.Quantity
2020-08-04 01:47:54 +00:00
}
2020-08-03 12:06:33 +00:00
2020-08-04 01:47:54 +00:00
// since we use USDT as the quote currency, we simply check if it matches the currency symbol
if strings.HasPrefix(trade.Symbol, trade.FeeCurrency) {
bidVolume -= trade.Fee
2020-08-05 10:49:32 +00:00
feeUSD += trade.Price * trade.Fee
if trade.IsBuyer {
bidFeeUSD += trade.Price * trade.Fee
}
2020-08-04 01:47:54 +00:00
} else if trade.FeeCurrency == "USDT" {
2020-08-05 10:49:32 +00:00
feeUSD += trade.Fee
if trade.IsBuyer {
bidFeeUSD += trade.Fee
}
2020-06-08 02:42:50 +00:00
}
2020-08-04 01:47:54 +00:00
2020-08-05 10:49:32 +00:00
} else {
if trade.FeeCurrency == c.TradingFeeCurrency {
bidVolume -= trade.Fee
}
2020-06-08 02:42:50 +00:00
}
2020-08-05 10:39:08 +00:00
if _, ok := currencyFees[trade.FeeCurrency]; !ok {
currencyFees[trade.FeeCurrency] = 0.0
}
currencyFees[trade.FeeCurrency] += trade.Fee
2020-06-08 02:42:50 +00:00
}
2020-08-05 10:49:32 +00:00
log.Infof("average bid price = (total amount %f + total feeUSD %f) / volume %f", bidAmount, bidFeeUSD, bidVolume)
2020-07-11 03:23:48 +00:00
profit := 0.0
2020-08-06 05:27:32 +00:00
averageCost := (bidAmount + bidFeeUSD) / bidVolume
2020-06-08 02:42:50 +00:00
for _, t := range trades {
2020-08-03 12:06:33 +00:00
if t.Symbol != c.Symbol {
continue
}
2020-08-04 01:47:54 +00:00
if t.IsBuyer {
continue
}
2020-08-06 05:27:32 +00:00
profit += (t.Price - averageCost) * t.Quantity
2020-08-04 01:47:54 +00:00
askVolume += t.Quantity
2020-06-08 02:42:50 +00:00
}
2020-08-05 10:49:32 +00:00
profit -= feeUSD
2020-08-06 05:27:32 +00:00
unrealizedProfit := profit
2020-06-08 02:42:50 +00:00
2020-07-11 03:23:48 +00:00
stock := bidVolume - askVolume
2020-06-08 02:42:50 +00:00
if stock > 0 {
2020-08-06 05:27:32 +00:00
stockFee := c.CurrentPrice * stock * feeRate
unrealizedProfit += (c.CurrentPrice-averageCost)*stock - stockFee
2020-06-08 02:42:50 +00:00
}
2020-07-11 03:23:48 +00:00
return &ProfitAndLossReport{
2020-07-11 04:07:24 +00:00
Symbol: c.Symbol,
StartTime: c.StartTime,
CurrentPrice: c.CurrentPrice,
NumTrades: len(trades),
2020-07-11 03:23:48 +00:00
2020-07-16 10:25:40 +00:00
BidVolume: bidVolume,
AskVolume: askVolume,
2020-08-05 10:49:32 +00:00
Stock: stock,
Profit: profit,
2020-08-06 05:27:32 +00:00
UnrealizedProfit: unrealizedProfit,
AverageBidCost: averageCost,
2020-08-05 10:49:32 +00:00
FeeUSD: feeUSD,
CurrencyFees: currencyFees,
2020-07-11 03:23:48 +00:00
}
}
type ProfitAndLossReport struct {
CurrentPrice float64
StartTime time.Time
2020-07-11 04:07:24 +00:00
Symbol string
2020-07-11 03:23:48 +00:00
2020-08-05 10:49:32 +00:00
NumTrades int
Profit float64
2020-08-06 05:27:32 +00:00
UnrealizedProfit float64
2020-08-05 10:49:32 +00:00
AverageBidCost float64
BidVolume float64
AskVolume float64
FeeUSD float64
Stock float64
CurrencyFees map[string]float64
2020-07-11 03:23:48 +00:00
}
func (report ProfitAndLossReport) Print() {
log.Infof("trades since: %v", report.StartTime)
2020-08-05 10:49:32 +00:00
log.Infof("average bid cost: %s", USD.FormatMoneyFloat64(report.AverageBidCost))
2020-07-16 10:25:40 +00:00
log.Infof("total bid volume: %f", report.BidVolume)
log.Infof("total ask volume: %f", report.AskVolume)
2020-08-04 01:47:54 +00:00
log.Infof("stock: %f", report.Stock)
2020-08-05 10:49:32 +00:00
log.Infof("fee (USD): %f", report.FeeUSD)
2020-07-11 03:23:48 +00:00
log.Infof("current price: %s", USD.FormatMoneyFloat64(report.CurrentPrice))
2020-08-04 01:47:54 +00:00
log.Infof("profit: %s", USD.FormatMoneyFloat64(report.Profit))
2020-08-06 05:27:32 +00:00
log.Infof("unrealized profit: %s", USD.FormatMoneyFloat64(report.UnrealizedProfit))
2020-08-05 10:39:08 +00:00
log.Infof("currency fees:")
for currency, fee := range report.CurrencyFees {
log.Infof(" - %s: %f", currency, fee)
}
2020-07-11 03:23:48 +00:00
}
2020-07-12 14:52:37 +00:00
func (report ProfitAndLossReport) SlackAttachment() slack.Attachment {
var color = ""
2020-08-06 05:31:41 +00:00
if report.UnrealizedProfit > 0 {
2020-07-12 14:52:37 +00:00
color = slackstyle.Green
} else {
color = slackstyle.Red
}
market, ok := types.FindMarket(report.Symbol)
if !ok {
return slack.Attachment{}
}
return slack.Attachment{
2020-08-08 02:13:35 +00:00
Title: report.Symbol + " Profit and Loss report",
Text: "Profit " + USD.FormatMoney(report.Profit),
2020-07-12 14:52:37 +00:00
Color: color,
// Pretext: "",
// Text: "",
Fields: []slack.AttachmentField{
2020-08-06 15:49:47 +00:00
{Title: "Profit", Value: USD.FormatMoney(report.Profit)},
{Title: "Unrealized Profit", Value: USD.FormatMoney(report.UnrealizedProfit)},
2020-08-06 05:27:32 +00:00
{Title: "Current Price", Value: market.FormatPrice(report.CurrentPrice), Short: true},
{Title: "Average Cost", Value: market.FormatPrice(report.AverageBidCost), Short: true},
2020-08-05 10:49:32 +00:00
{Title: "Fee (USD)", Value: USD.FormatMoney(report.FeeUSD), Short: true},
2020-08-03 10:47:36 +00:00
{Title: "Stock", Value: strconv.FormatFloat(report.Stock, 'f', 8, 64), Short: true},
2020-08-06 05:27:32 +00:00
{Title: "Number of Trades", Value: strconv.Itoa(report.NumTrades), Short: true},
2020-07-12 14:52:37 +00:00
},
Footer: report.StartTime.Format(time.RFC822),
FooterIcon: "",
}
}