2020-10-16 02:16:42 +00:00
|
|
|
package pnl
|
|
|
|
|
|
|
|
import (
|
2021-02-06 04:32:21 +00:00
|
|
|
"time"
|
2020-10-16 02:16:42 +00:00
|
|
|
|
2022-01-29 18:40:38 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
2021-12-04 18:16:48 +00:00
|
|
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
2020-10-16 02:16:42 +00:00
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
type AverageCostCalculator struct {
|
|
|
|
TradingFeeCurrency string
|
2021-12-04 18:16:48 +00:00
|
|
|
Market types.Market
|
2020-10-16 02:16:42 +00:00
|
|
|
}
|
|
|
|
|
2020-10-22 07:57:50 +00:00
|
|
|
func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, currentPrice float64) *AverageCostPnlReport {
|
2020-10-16 02:16:42 +00:00
|
|
|
// copy trades, so that we can truncate it.
|
|
|
|
var bidVolume = 0.0
|
|
|
|
var askVolume = 0.0
|
|
|
|
var feeUSD = 0.0
|
|
|
|
|
2020-10-24 08:32:54 +00:00
|
|
|
if len(trades) == 0 {
|
|
|
|
return &AverageCostPnlReport{
|
2021-12-05 17:05:33 +00:00
|
|
|
Symbol: symbol,
|
|
|
|
Market: c.Market,
|
|
|
|
LastPrice: currentPrice,
|
|
|
|
NumTrades: 0,
|
|
|
|
BuyVolume: bidVolume,
|
|
|
|
SellVolume: askVolume,
|
|
|
|
FeeInUSD: feeUSD,
|
2020-10-24 08:32:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-16 02:16:42 +00:00
|
|
|
var currencyFees = map[string]float64{}
|
|
|
|
|
2021-12-11 11:16:16 +00:00
|
|
|
var position = types.NewPositionFromMarket(c.Market)
|
|
|
|
position.SetFeeRate(types.ExchangeFee{
|
2021-12-04 18:16:48 +00:00
|
|
|
// binance vip 0 uses 0.075%
|
|
|
|
MakerFeeRate: fixedpoint.NewFromFloat(0.075 * 0.01),
|
|
|
|
TakerFeeRate: fixedpoint.NewFromFloat(0.075 * 0.01),
|
|
|
|
})
|
|
|
|
|
|
|
|
// TODO: configure the exchange fee rate here later
|
|
|
|
// position.SetExchangeFeeRate()
|
|
|
|
var totalProfit fixedpoint.Value
|
|
|
|
var totalNetProfit fixedpoint.Value
|
|
|
|
|
2022-01-29 18:40:38 +00:00
|
|
|
var tradeIDs = map[uint64]types.Trade{}
|
|
|
|
|
2020-10-16 02:16:42 +00:00
|
|
|
for _, trade := range trades {
|
2022-01-29 18:40:38 +00:00
|
|
|
if _, exists := tradeIDs[trade.ID]; exists {
|
|
|
|
log.Warnf("duplicated trade: %+v", trade)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if trade.Symbol != symbol {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
profit, netProfit, madeProfit := position.AddTrade(trade)
|
|
|
|
if madeProfit {
|
|
|
|
totalProfit += profit
|
|
|
|
totalNetProfit += netProfit
|
|
|
|
}
|
|
|
|
|
|
|
|
if trade.IsBuyer {
|
|
|
|
bidVolume += trade.Quantity
|
|
|
|
} else {
|
|
|
|
askVolume += trade.Quantity
|
2020-10-16 02:16:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := currencyFees[trade.FeeCurrency]; !ok {
|
2021-12-04 18:16:48 +00:00
|
|
|
currencyFees[trade.FeeCurrency] = trade.Fee
|
|
|
|
} else {
|
|
|
|
currencyFees[trade.FeeCurrency] += trade.Fee
|
2020-10-16 02:16:42 +00:00
|
|
|
}
|
2022-01-29 18:40:38 +00:00
|
|
|
|
|
|
|
tradeIDs[trade.ID] = trade
|
2020-10-16 02:16:42 +00:00
|
|
|
}
|
|
|
|
|
2022-01-08 16:35:45 +00:00
|
|
|
unrealizedProfit := (fixedpoint.NewFromFloat(currentPrice) - position.AverageCost).Mul(position.GetBase())
|
2020-10-16 02:21:37 +00:00
|
|
|
return &AverageCostPnlReport{
|
2021-12-05 17:05:33 +00:00
|
|
|
Symbol: symbol,
|
|
|
|
Market: c.Market,
|
|
|
|
LastPrice: currentPrice,
|
|
|
|
NumTrades: len(trades),
|
|
|
|
StartTime: time.Time(trades[0].Time),
|
2020-10-16 02:16:42 +00:00
|
|
|
|
2020-11-10 06:18:27 +00:00
|
|
|
BuyVolume: bidVolume,
|
|
|
|
SellVolume: askVolume,
|
2020-10-16 02:16:42 +00:00
|
|
|
|
2022-01-08 16:35:45 +00:00
|
|
|
Stock: position.GetBase().Float64(),
|
2021-12-04 18:16:48 +00:00
|
|
|
Profit: totalProfit,
|
|
|
|
NetProfit: totalNetProfit,
|
2020-10-16 02:16:42 +00:00
|
|
|
UnrealizedProfit: unrealizedProfit,
|
2021-12-05 17:05:33 +00:00
|
|
|
AverageCost: position.AverageCost.Float64(),
|
2021-12-04 18:16:48 +00:00
|
|
|
FeeInUSD: (totalProfit - totalNetProfit).Float64(),
|
2020-10-16 02:16:42 +00:00
|
|
|
CurrencyFees: currencyFees,
|
|
|
|
}
|
|
|
|
}
|