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
|
|
|
}
|
|
|
|
|
2022-02-02 11:37:18 +00:00
|
|
|
func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, currentPrice fixedpoint.Value) *AverageCostPnlReport {
|
2020-10-16 02:16:42 +00:00
|
|
|
// copy trades, so that we can truncate it.
|
2022-02-02 11:37:18 +00:00
|
|
|
var bidVolume = fixedpoint.Zero
|
|
|
|
var askVolume = fixedpoint.Zero
|
|
|
|
var feeUSD = fixedpoint.Zero
|
2020-10-16 02:16:42 +00:00
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-02 11:37:18 +00:00
|
|
|
var currencyFees = map[string]fixedpoint.Value{}
|
2020-10-16 02:16:42 +00:00
|
|
|
|
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 {
|
2022-02-02 11:37:18 +00:00
|
|
|
totalProfit = totalProfit.Add(profit)
|
|
|
|
totalNetProfit = totalNetProfit.Add(netProfit)
|
2022-01-29 18:40:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if trade.IsBuyer {
|
2022-02-02 11:37:18 +00:00
|
|
|
bidVolume = bidVolume.Add(trade.Quantity)
|
2022-01-29 18:40:38 +00:00
|
|
|
} else {
|
2022-02-02 11:37:18 +00:00
|
|
|
askVolume = askVolume.Add(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 {
|
2022-02-02 11:37:18 +00:00
|
|
|
currencyFees[trade.FeeCurrency] = currencyFees[trade.FeeCurrency].Add(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-02-02 11:37:18 +00:00
|
|
|
unrealizedProfit := currentPrice.Sub(position.AverageCost).
|
|
|
|
Mul(position.GetBase())
|
2022-05-10 10:27:23 +00:00
|
|
|
|
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-06-08 06:51:31 +00:00
|
|
|
BaseAssetPosition: position.GetBase(),
|
|
|
|
Profit: totalProfit,
|
|
|
|
NetProfit: totalNetProfit,
|
|
|
|
UnrealizedProfit: unrealizedProfit,
|
|
|
|
AverageCost: position.AverageCost,
|
|
|
|
FeeInUSD: totalProfit.Sub(totalNetProfit),
|
|
|
|
CurrencyFees: currencyFees,
|
2020-10-16 02:16:42 +00:00
|
|
|
}
|
|
|
|
}
|