pull out writeJsonFile function

This commit is contained in:
c9s 2022-05-10 18:27:23 +08:00
parent 24464fdcb6
commit 5f68064ac6
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
6 changed files with 113 additions and 55 deletions

View File

@ -81,6 +81,7 @@ func (c *AverageCostCalculator) Calculate(symbol string, trades []types.Trade, c
unrealizedProfit := currentPrice.Sub(position.AverageCost).
Mul(position.GetBase())
return &AverageCostPnlReport{
Symbol: symbol,
Market: c.Market,

View File

@ -5,7 +5,7 @@ import (
"strconv"
"time"
log "github.com/sirupsen/logrus"
"github.com/fatih/color"
"github.com/slack-go/slack"
"github.com/c9s/bbgo/pkg/fixedpoint"
@ -15,15 +15,15 @@ import (
)
type AverageCostPnlReport struct {
LastPrice fixedpoint.Value `json:"lastPrice"`
StartTime time.Time `json:"startTime"`
Symbol string `json:"symbol"`
Market types.Market `json:"market"`
LastPrice fixedpoint.Value `json:"lastPrice"`
StartTime time.Time `json:"startTime"`
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"`
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"`
@ -37,19 +37,29 @@ func (report *AverageCostPnlReport) JSON() ([]byte, error) {
}
func (report AverageCostPnlReport) Print() {
log.Infof("TRADES SINCE: %v", report.StartTime)
log.Infof("NUMBER OF TRADES: %d", report.NumTrades)
log.Infof("AVERAGE COST: %s", types.USD.FormatMoney(report.AverageCost))
log.Infof("TOTAL BUY VOLUME: %v", report.BuyVolume)
log.Infof("TOTAL SELL VOLUME: %v", report.SellVolume)
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("TOTAL BUY VOLUME: %v", report.BuyVolume)
color.Green("TOTAL SELL VOLUME: %v", report.SellVolume)
log.Infof("CURRENT PRICE: %s", types.USD.FormatMoney(report.LastPrice))
log.Infof("CURRENCY FEES:")
color.Green("CURRENT PRICE: %s", types.USD.FormatMoney(report.LastPrice))
color.Green("CURRENCY FEES:")
for currency, fee := range report.CurrencyFees {
log.Infof(" - %s: %s", currency, fee.String())
color.Green(" - %s: %s", currency, fee.String())
}
if report.Profit.Sign() > 0 {
color.Green("PROFIT: %s", types.USD.FormatMoney(report.Profit))
} else {
color.Red("PROFIT: %s", types.USD.FormatMoney(report.Profit))
}
if report.UnrealizedProfit.Sign() > 0 {
color.Green("UNREALIZED PROFIT: %s", types.USD.FormatMoney(report.UnrealizedProfit))
} else {
color.Red("UNREALIZED PROFIT: %s", types.USD.FormatMoney(report.UnrealizedProfit))
}
log.Infof("PROFIT: %s", types.USD.FormatMoney(report.Profit))
log.Infof("UNREALIZED PROFIT: %s", types.USD.FormatMoney(report.UnrealizedProfit))
}
func (report AverageCostPnlReport) SlackAttachment() slack.Attachment {

View File

@ -10,7 +10,18 @@ import (
"github.com/c9s/bbgo/pkg/types"
)
type Report struct {
// SummaryReport is the summary of the back-test session
type SummaryReport struct {
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Sessions []string `json:"sessions"`
InitialTotalBalances types.BalanceMap `json:"initialTotalBalances"`
FinalTotalBalances types.BalanceMap `json:"finalTotalBalances"`
}
// SessionSymbolReport is the report per exchange session
// trades are merged, collected and re-calculated
type SessionSymbolReport struct {
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Symbol string `json:"symbol,omitempty"`

View File

@ -104,13 +104,22 @@ type Backtest struct {
EndTime *types.LooseFormatTime `json:"endTime,omitempty" yaml:"endTime,omitempty"`
// RecordTrades is an option, if set to true, back-testing should record the trades into database
RecordTrades bool `json:"recordTrades,omitempty" yaml:"recordTrades,omitempty"`
RecordTrades bool `json:"recordTrades,omitempty" yaml:"recordTrades,omitempty"`
// Account is deprecated, use Accounts instead
Account map[string]BacktestAccount `json:"account" yaml:"account"`
Accounts map[string]BacktestAccount `json:"accounts" yaml:"accounts"`
Symbols []string `json:"symbols" yaml:"symbols"`
Sessions []string `json:"sessions" yaml:"sessions"`
Account map[string]BacktestAccount `json:"account" yaml:"account"`
Accounts map[string]BacktestAccount `json:"accounts" yaml:"accounts"`
Symbols []string `json:"symbols" yaml:"symbols"`
Sessions []string `json:"sessions" yaml:"sessions"`
}
func (b *Backtest) GetAccount(n string) BacktestAccount {
accountConfig, ok := b.Accounts[n]
if ok {
return accountConfig
}
return b.Account[n]
}
type BacktestAccount struct {

View File

@ -432,12 +432,43 @@ var BacktestCmd = &cobra.Command{
// put the logger back to print the pnl
log.SetLevel(log.InfoLevel)
log.Infof("BACK-TEST REPORT")
log.Infof("===============================================")
log.Infof("START TIME: %s", startTime.Format(time.RFC1123))
log.Infof("END TIME: %s", endTime.Format(time.RFC1123))
color.Green("BACK-TEST REPORT")
color.Green("===============================================\n")
color.Green("START TIME: %s\n", startTime.Format(time.RFC1123))
color.Green("END TIME: %s\n", endTime.Format(time.RFC1123))
// aggregate total balances
initTotalBalances := types.BalanceMap{}
finalTotalBalances := types.BalanceMap{}
sessionNames := []string{}
for _, session := range environ.Sessions() {
backtestExchange := session.Exchange.(*backtest.Exchange)
sessionNames = append(sessionNames, session.Name)
accountConfig := userConfig.Backtest.GetAccount(session.Name)
initBalances := accountConfig.Balances.BalanceMap()
initTotalBalances = initTotalBalances.Add(initBalances)
finalBalances := session.GetAccount().Balances()
finalTotalBalances = finalTotalBalances.Add(finalBalances)
}
color.Green("INITIAL TOTAL BALANCE: %v\n", initTotalBalances)
color.Green("FINAL TOTAL BALANCE: %v\n", finalTotalBalances)
summaryReport := &backtest.SummaryReport{
StartTime: startTime,
EndTime: endTime,
Sessions: sessionNames,
InitialTotalBalances: initTotalBalances,
FinalTotalBalances: finalTotalBalances,
}
_ = summaryReport
for _, session := range environ.Sessions() {
backtestExchange, ok := session.Exchange.(*backtest.Exchange)
if !ok {
return fmt.Errorf("unexpected error, exchange instance is not a backtest exchange")
}
// per symbol report
exchangeName := session.Exchange.Name().String()
for symbol, trades := range session.Trades {
market, ok := session.Market(symbol)
@ -466,22 +497,12 @@ var BacktestCmd = &cobra.Command{
report := calculator.Calculate(symbol, trades.Trades, lastPrice)
report.Print()
accountConfig, ok := userConfig.Backtest.Accounts[exchangeName]
if !ok {
accountConfig = userConfig.Backtest.Account[exchangeName]
}
accountConfig := userConfig.Backtest.GetAccount(exchangeName)
initBalances := accountConfig.Balances.BalanceMap()
finalBalances := session.GetAccount().Balances()
log.Infof("INITIAL BALANCES:")
initBalances.Print()
log.Infof("FINAL BALANCES:")
finalBalances.Print()
if generatingReport {
result := backtest.Report{
result := backtest.SessionSymbolReport{
StartTime: startTime,
EndTime: endTime,
Symbol: symbol,
@ -493,20 +514,15 @@ var BacktestCmd = &cobra.Command{
Manifests: manifests,
}
jsonOutput, err := json.MarshalIndent(&result, "", " ")
if err != nil {
return err
}
if err := ioutil.WriteFile(filepath.Join(outputDirectory, symbol+".json"), jsonOutput, 0644); err != nil {
if err := writeJsonFile(filepath.Join(outputDirectory, symbol+".json"), &result) ; err != nil {
return err
}
}
initQuoteAsset := inQuoteAsset(initBalances, market, startPrice)
finalQuoteAsset := inQuoteAsset(finalBalances, market, lastPrice)
log.Infof("INITIAL ASSET IN %s ~= %s %s (1 %s = %v)", market.QuoteCurrency, market.FormatQuantity(initQuoteAsset), market.QuoteCurrency, market.BaseCurrency, startPrice)
log.Infof("FINAL ASSET IN %s ~= %s %s (1 %s = %v)", market.QuoteCurrency, market.FormatQuantity(finalQuoteAsset), market.QuoteCurrency, market.BaseCurrency, lastPrice)
color.Green("INITIAL ASSET IN %s ~= %s %s (1 %s = %v)", market.QuoteCurrency, market.FormatQuantity(initQuoteAsset), market.QuoteCurrency, market.BaseCurrency, startPrice)
color.Green("FINAL ASSET IN %s ~= %s %s (1 %s = %v)", market.QuoteCurrency, market.FormatQuantity(finalQuoteAsset), market.QuoteCurrency, market.BaseCurrency, lastPrice)
if report.Profit.Sign() > 0 {
color.Green("REALIZED PROFIT: +%v %s", report.Profit, market.QuoteCurrency)
@ -577,6 +593,15 @@ func confirmation(s string) bool {
}
}
func writeJsonFile(p string, obj interface{}) error {
out, err := json.MarshalIndent(obj, "", " ")
if err != nil {
return err
}
return ioutil.WriteFile(p, out, 0644)
}
func safeMkdirAll(p string) error {
st, err := os.Stat(p)
if err == nil {

View File

@ -6,8 +6,6 @@ import (
"strings"
"time"
"github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/fixedpoint"
)
@ -193,11 +191,15 @@ func (m BalanceMap) Print() {
continue
}
fmt.Printf(" %s: %v", balance.Currency, balance.Available)
if balance.Locked.Sign() > 0 {
logrus.Infof(" %s: %v (locked %v)", balance.Currency, balance.Available, balance.Locked)
} else {
logrus.Infof(" %s: %v", balance.Currency, balance.Available)
fmt.Printf(" (locked %v)", balance.Locked)
}
if balance.Borrowed.Sign() > 0 {
fmt.Printf(" (borrowed %v)", balance.Borrowed)
}
fmt.Println()
}
}