mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-24 15:55:14 +00:00
cmd: fix pnl cmd
This commit is contained in:
parent
e023d0be5b
commit
8c6331073d
|
@ -92,12 +92,12 @@ 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,
|
||||||
AverageCost: position.AverageCost,
|
AverageCost: position.AverageCost,
|
||||||
FeeInUSD: totalProfit.Sub(totalNetProfit),
|
FeeInUSD: totalProfit.Sub(totalNetProfit),
|
||||||
CurrencyFees: currencyFees,
|
CurrencyFees: currencyFees,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,16 +20,16 @@ type AverageCostPnlReport struct {
|
||||||
Symbol string `json:"symbol"`
|
Symbol string `json:"symbol"`
|
||||||
Market types.Market `json:"market"`
|
Market types.Market `json:"market"`
|
||||||
|
|
||||||
NumTrades int `json:"numTrades"`
|
NumTrades int `json:"numTrades"`
|
||||||
Profit fixedpoint.Value `json:"profit"`
|
Profit fixedpoint.Value `json:"profit"`
|
||||||
NetProfit fixedpoint.Value `json:"netProfit"`
|
NetProfit fixedpoint.Value `json:"netProfit"`
|
||||||
UnrealizedProfit fixedpoint.Value `json:"unrealizedProfit"`
|
UnrealizedProfit fixedpoint.Value `json:"unrealizedProfit"`
|
||||||
AverageCost fixedpoint.Value `json:"averageCost"`
|
AverageCost fixedpoint.Value `json:"averageCost"`
|
||||||
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (report *AverageCostPnlReport) JSON() ([]byte, error) {
|
func (report *AverageCostPnlReport) JSON() ([]byte, error) {
|
||||||
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user