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,7 +92,7 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
BuyVolume: bidVolume, BuyVolume: bidVolume,
SellVolume: askVolume, SellVolume: askVolume,
Stock: position.GetBase(), BaseAssetPosition: position.GetBase(),
Profit: totalProfit, Profit: totalProfit,
NetProfit: totalNetProfit, NetProfit: totalNetProfit,
UnrealizedProfit: unrealizedProfit, UnrealizedProfit: unrealizedProfit,

View File

@ -28,7 +28,7 @@ type AverageCostPnlReport struct {
BuyVolume fixedpoint.Value `json:"buyVolume,omitempty"` BuyVolume fixedpoint.Value `json:"buyVolume,omitempty"`
SellVolume fixedpoint.Value `json:"sellVolume,omitempty"` SellVolume fixedpoint.Value `json:"sellVolume,omitempty"`
FeeInUSD fixedpoint.Value `json:"feeInUSD"` FeeInUSD fixedpoint.Value `json:"feeInUSD"`
Stock fixedpoint.Value `json:"stock"` BaseAssetPosition fixedpoint.Value `json:"baseAssetPosition"`
CurrencyFees map[string]fixedpoint.Value `json:"currencyFees"` CurrencyFees map[string]fixedpoint.Value `json:"currencyFees"`
} }
@ -39,7 +39,10 @@ func (report *AverageCostPnlReport) JSON() ([]byte, error) {
func (report AverageCostPnlReport) Print() { func (report AverageCostPnlReport) Print() {
color.Green("TRADES SINCE: %v", report.StartTime) color.Green("TRADES SINCE: %v", report.StartTime)
color.Green("NUMBER OF TRADES: %d", report.NumTrades) color.Green("NUMBER OF TRADES: %d", report.NumTrades)
color.Green("AVERAGE COST: %s", types.USD.FormatMoney(report.AverageCost)) 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 BUY VOLUME: %v", report.BuyVolume)
color.Green("TOTAL SELL VOLUME: %v", report.SellVolume) color.Green("TOTAL SELL VOLUME: %v", report.SellVolume)
@ -83,7 +86,7 @@ func (report AverageCostPnlReport) SlackAttachment() slack.Attachment {
// FIXME: // FIXME:
// {Title: "Fee (USD)", Value: types.USD.FormatMoney(report.FeeInUSD), Short: true}, // {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}, {Title: "Number of Trades", Value: strconv.Itoa(report.NumTrades), Short: true},
}, },
Footer: report.StartTime.Format(time.RFC822), Footer: report.StartTime.Format(time.RFC822),

View File

@ -3,10 +3,8 @@ package cmd
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"time" "time"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -29,34 +27,7 @@ var depositsCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background() 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() environ := bbgo.NewEnvironment()
if err := environ.ConfigureExchangeSessions(userConfig); err != nil { if err := environ.ConfigureExchangeSessions(userConfig); err != nil {
return err return err
} }

View File

@ -2,16 +2,14 @@ package cmd
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os"
"strings" "strings"
"time" "time"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/c9s/bbgo/pkg/accounting"
"github.com/c9s/bbgo/pkg/accounting/pnl" "github.com/c9s/bbgo/pkg/accounting/pnl"
"github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/bbgo"
"github.com/c9s/bbgo/pkg/service" "github.com/c9s/bbgo/pkg/service"
@ -22,35 +20,18 @@ func init() {
PnLCmd.Flags().String("session", "", "target exchange") PnLCmd.Flags().String("session", "", "target exchange")
PnLCmd.Flags().String("symbol", "", "trading symbol") PnLCmd.Flags().String("symbol", "", "trading symbol")
PnLCmd.Flags().Bool("include-transfer", false, "convert transfer records into trades") 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) RootCmd.AddCommand(PnLCmd)
} }
var PnLCmd = &cobra.Command{ var PnLCmd = &cobra.Command{
Use: "pnl", 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, SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
ctx := context.Background() 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") sessionName, err := cmd.Flags().GetString("session")
if err != nil { if err != nil {
return err return err
@ -128,7 +109,7 @@ var PnLCmd = &cobra.Command{
// we need the backtest klines for the daily prices // we need the backtest klines for the daily prices
backtestService := &service.BacktestService{DB: environ.DatabaseService.DB} 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 return err
} }
} }
@ -140,7 +121,6 @@ var PnLCmd = &cobra.Command{
trades, err = environ.TradeService.QueryForTradingFeeCurrency(exchange.Name(), symbol, tradingFeeCurrency) trades, err = environ.TradeService.QueryForTradingFeeCurrency(exchange.Name(), symbol, tradingFeeCurrency)
} else { } else {
trades, err = environ.TradeService.Query(service.QueryTradesOptions{ trades, err = environ.TradeService.Query(service.QueryTradesOptions{
Exchange: exchange.Name(),
Symbol: symbol, Symbol: symbol,
Limit: limit, Limit: limit,
}) })
@ -150,35 +130,25 @@ var PnLCmd = &cobra.Command{
return err 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)) 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) tickers, err := exchange.QueryTickers(ctx, symbol)
if err != nil { if err != nil {
return err return err
} }
currentTick, ok := tickers[symbol] currentTick, ok := tickers[symbol]
if !ok { if !ok {
return errors.New("no ticker data for current price") return errors.New("no ticker data for current price")
} }
currentPrice := currentTick.Last currentPrice := currentTick.Last
calculator := &pnl.AverageCostCalculator{ calculator := &pnl.AverageCostCalculator{
TradingFeeCurrency: tradingFeeCurrency, TradingFeeCurrency: tradingFeeCurrency,
} }

View File

@ -146,7 +146,7 @@ func trimTrailingZero(a float64) string {
// String is for console output // String is for console output
func (trade Trade) String() string { 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.Exchange.String(),
trade.Symbol, trade.Symbol,
trade.Side, trade.Side,