cmd: fix pnl cmd

This commit is contained in:
c9s 2022-06-08 14:51:31 +08:00
parent e023d0be5b
commit 8c6331073d
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
5 changed files with 33 additions and 89 deletions

View File

@ -92,12 +92,12 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
BuyVolume: bidVolume,
SellVolume: askVolume,
Stock: position.GetBase(),
Profit: totalProfit,
NetProfit: totalNetProfit,
UnrealizedProfit: unrealizedProfit,
AverageCost: position.AverageCost,
FeeInUSD: totalProfit.Sub(totalNetProfit),
CurrencyFees: currencyFees,
BaseAssetPosition: position.GetBase(),
Profit: totalProfit,
NetProfit: totalNetProfit,
UnrealizedProfit: unrealizedProfit,
AverageCost: position.AverageCost,
FeeInUSD: totalProfit.Sub(totalNetProfit),
CurrencyFees: currencyFees,
}
}

View File

@ -20,16 +20,16 @@ type AverageCostPnlReport struct {
Symbol string `json:"symbol"`
Market types.Market `json:"market"`
NumTrades int `json:"numTrades"`
Profit fixedpoint.Value `json:"profit"`
NetProfit fixedpoint.Value `json:"netProfit"`
UnrealizedProfit fixedpoint.Value `json:"unrealizedProfit"`
AverageCost fixedpoint.Value `json:"averageCost"`
BuyVolume fixedpoint.Value `json:"buyVolume,omitempty"`
SellVolume fixedpoint.Value `json:"sellVolume,omitempty"`
FeeInUSD fixedpoint.Value `json:"feeInUSD"`
Stock fixedpoint.Value `json:"stock"`
CurrencyFees map[string]fixedpoint.Value `json:"currencyFees"`
NumTrades int `json:"numTrades"`
Profit fixedpoint.Value `json:"profit"`
NetProfit fixedpoint.Value `json:"netProfit"`
UnrealizedProfit fixedpoint.Value `json:"unrealizedProfit"`
AverageCost fixedpoint.Value `json:"averageCost"`
BuyVolume fixedpoint.Value `json:"buyVolume,omitempty"`
SellVolume fixedpoint.Value `json:"sellVolume,omitempty"`
FeeInUSD fixedpoint.Value `json:"feeInUSD"`
BaseAssetPosition fixedpoint.Value `json:"baseAssetPosition"`
CurrencyFees map[string]fixedpoint.Value `json:"currencyFees"`
}
func (report *AverageCostPnlReport) JSON() ([]byte, error) {
@ -39,7 +39,10 @@ func (report *AverageCostPnlReport) JSON() ([]byte, error) {
func (report AverageCostPnlReport) Print() {
color.Green("TRADES SINCE: %v", report.StartTime)
color.Green("NUMBER OF TRADES: %d", report.NumTrades)
color.Green("AVERAGE COST: %s", types.USD.FormatMoney(report.AverageCost))
color.Green("BASE ASSET POSITION: %s", report.BaseAssetPosition.String())
color.Green("TOTAL BUY VOLUME: %v", report.BuyVolume)
color.Green("TOTAL SELL VOLUME: %v", report.SellVolume)
@ -83,7 +86,7 @@ func (report AverageCostPnlReport) SlackAttachment() slack.Attachment {
// FIXME:
// {Title: "Fee (USD)", Value: types.USD.FormatMoney(report.FeeInUSD), Short: true},
{Title: "Stock", Value: report.Stock.String(), Short: true},
{Title: "Base Asset Position", Value: report.BaseAssetPosition.String(), Short: true},
{Title: "Number of Trades", Value: strconv.Itoa(report.NumTrades), Short: true},
},
Footer: report.StartTime.Format(time.RFC822),

View File

@ -3,10 +3,8 @@ package cmd
import (
"context"
"fmt"
"os"
"time"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@ -29,34 +27,7 @@ var depositsCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return err
}
if len(configFile) == 0 {
return errors.New("--config option is required")
}
// if config file exists, use the config loaded from the config file.
// otherwise, use a empty config object
var userConfig *bbgo.Config
if _, err := os.Stat(configFile); err == nil {
// load successfully
userConfig, err = bbgo.Load(configFile, false)
if err != nil {
return err
}
} else if os.IsNotExist(err) {
// config file doesn't exist
userConfig = &bbgo.Config{}
} else {
// other error
return err
}
environ := bbgo.NewEnvironment()
if err := environ.ConfigureExchangeSessions(userConfig); err != nil {
return err
}

View File

@ -2,16 +2,14 @@ package cmd
import (
"context"
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/c9s/bbgo/pkg/accounting"
"github.com/c9s/bbgo/pkg/accounting/pnl"
"github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/service"
@ -22,35 +20,18 @@ func init() {
PnLCmd.Flags().String("session", "", "target exchange")
PnLCmd.Flags().String("symbol", "", "trading symbol")
PnLCmd.Flags().Bool("include-transfer", false, "convert transfer records into trades")
PnLCmd.Flags().Int("limit", 500, "number of trades")
PnLCmd.Flags().Int("limit", 0, "number of trades")
RootCmd.AddCommand(PnLCmd)
}
var PnLCmd = &cobra.Command{
Use: "pnl",
Short: "pnl calculator",
Short: "Average Cost Based PnL Calculator",
Long: "This command calculates the average cost-based profit from your total trades",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background()
configFile, err := cmd.Flags().GetString("config")
if err != nil {
return err
}
if len(configFile) == 0 {
return errors.New("--config option is required")
}
if _, err := os.Stat(configFile); os.IsNotExist(err) {
return err
}
userConfig, err := bbgo.Load(configFile, false)
if err != nil {
return err
}
sessionName, err := cmd.Flags().GetString("session")
if err != nil {
return err
@ -128,7 +109,7 @@ var PnLCmd = &cobra.Command{
// we need the backtest klines for the daily prices
backtestService := &service.BacktestService{DB: environ.DatabaseService.DB}
if err := backtestService.SyncKLineByInterval(ctx, exchange, symbol, types.Interval1d, since, until); err != nil {
if err := backtestService.Sync(ctx, exchange, symbol, types.Interval1d, since, until); err != nil {
return err
}
}
@ -140,7 +121,6 @@ var PnLCmd = &cobra.Command{
trades, err = environ.TradeService.QueryForTradingFeeCurrency(exchange.Name(), symbol, tradingFeeCurrency)
} else {
trades, err = environ.TradeService.Query(service.QueryTradesOptions{
Exchange: exchange.Name(),
Symbol: symbol,
Limit: limit,
})
@ -150,35 +130,25 @@ var PnLCmd = &cobra.Command{
return err
}
if len(trades) == 0 {
return errors.New("empty trades, you need to run sync command to sync the trades from the exchange first")
}
trades = types.SortTradesAscending(trades)
log.Infof("%d trades loaded", len(trades))
stockManager := &accounting.StockDistribution{
Symbol: symbol,
TradingFeeCurrency: tradingFeeCurrency,
}
checkpoints, err := stockManager.AddTrades(trades)
if err != nil {
return err
}
log.Infof("found checkpoints: %+v", checkpoints)
log.Infof("stock: %v", stockManager.Stocks.Quantity())
tickers, err := exchange.QueryTickers(ctx, symbol)
if err != nil {
return err
}
currentTick, ok := tickers[symbol]
if !ok {
return errors.New("no ticker data for current price")
}
currentPrice := currentTick.Last
calculator := &pnl.AverageCostCalculator{
TradingFeeCurrency: tradingFeeCurrency,
}

View File

@ -146,7 +146,7 @@ func trimTrailingZero(a float64) string {
// String is for console output
func (trade Trade) String() string {
return fmt.Sprintf("TRADE %s %s %4s %s @ %s amount %s fee %s %s orderID %d %s",
return fmt.Sprintf("TRADE %s %s %4s %-4s @ %6s | amount %s | fee %s %s | orderID %d | %s",
trade.Exchange.String(),
trade.Symbol,
trade.Side,