package report import ( "fmt" "github.com/c9s/bbgo/pkg/data/tsv" "github.com/c9s/bbgo/pkg/datatype/floats" "github.com/c9s/bbgo/pkg/fixedpoint" "github.com/c9s/bbgo/pkg/indicator" "github.com/c9s/bbgo/pkg/types" ) // AccumulatedProfitReport For accumulated profit report output type AccumulatedProfitReport struct { // ProfitMAWindow Accumulated profit SMA window ProfitMAWindow int `json:"profitMAWindow"` // ShortTermProfitWindow The window to sum up the short-term profit ShortTermProfitWindow int `json:"shortTermProfitWindow"` // TsvReportPath The path to output report to TsvReportPath string `json:"tsvReportPath"` symbol string types.IntervalWindow // Accumulated profit accumulatedProfit fixedpoint.Value accumulatedProfitPerInterval floats.Slice // Accumulated profit MA profitMA *indicator.SMA profitMAPerInterval floats.Slice // Profit of each interval ProfitPerInterval floats.Slice // Accumulated fee accumulatedFee fixedpoint.Value accumulatedFeePerInterval floats.Slice // Win ratio winRatioPerInterval floats.Slice // Profit factor profitFactorPerInterval floats.Slice // Trade number accumulatedTrades int accumulatedTradesPerInterval floats.Slice // Extra values strategyParameters [][2]string } func (r *AccumulatedProfitReport) Initialize(symbol string, interval types.Interval, window int) { r.symbol = symbol r.Interval = interval r.Window = window if r.ProfitMAWindow <= 0 { r.ProfitMAWindow = 60 } if r.Window <= 0 { r.Window = 7 } if r.ShortTermProfitWindow <= 0 { r.ShortTermProfitWindow = 7 } r.profitMA = &indicator.SMA{IntervalWindow: types.IntervalWindow{Interval: r.Interval, Window: r.ProfitMAWindow}} } func (r *AccumulatedProfitReport) AddStrategyParameter(title string, value string) { r.strategyParameters = append(r.strategyParameters, [2]string{title, value}) } func (r *AccumulatedProfitReport) AddTrade(trade types.Trade) { r.accumulatedFee = r.accumulatedFee.Add(trade.Fee) r.accumulatedTrades += 1 } func (r *AccumulatedProfitReport) Rotate(ps *types.ProfitStats, ts *types.TradeStats) { // Accumulated profit r.accumulatedProfit = r.accumulatedProfit.Add(ps.AccumulatedNetProfit) r.accumulatedProfitPerInterval.Update(r.accumulatedProfit.Float64()) // Profit of each interval r.ProfitPerInterval.Update(ps.AccumulatedNetProfit.Float64()) // Profit MA r.profitMA.Update(r.accumulatedProfit.Float64()) r.profitMAPerInterval.Update(r.profitMA.Last(0)) // Accumulated Fee r.accumulatedFeePerInterval.Update(r.accumulatedFee.Float64()) // Trades r.accumulatedTradesPerInterval.Update(float64(r.accumulatedTrades)) // Win ratio r.winRatioPerInterval.Update(ts.WinningRatio.Float64()) // Profit factor r.profitFactorPerInterval.Update(ts.ProfitFactor.Float64()) } // Output Accumulated profit report to a TSV file func (r *AccumulatedProfitReport) Output() { if r.TsvReportPath != "" { tsvwiter, err := tsv.AppendWriterFile(r.TsvReportPath) if err != nil { panic(err) } defer tsvwiter.Close() // Output title row titles := []string{ "#", "Symbol", "Total Net Profit", fmt.Sprintf("Total Net Profit %sMA%d", r.Interval, r.ProfitMAWindow), fmt.Sprintf("%s%d Net Profit", r.Interval, r.ShortTermProfitWindow), "accumulatedFee", "winRatio", "profitFactor", fmt.Sprintf("%s%d Trades", r.Interval, r.Window), } for i := 0; i < len(r.strategyParameters); i++ { titles = append(titles, r.strategyParameters[i][0]) } _ = tsvwiter.Write(titles) // Output data row for i := 0; i <= r.Window-1; i++ { values := []string{ fmt.Sprintf("%d", i+1), r.symbol, fmt.Sprintf("%f", r.accumulatedProfitPerInterval.Last(i)), fmt.Sprintf("%f", r.profitMAPerInterval.Last(i)), fmt.Sprintf("%f", r.accumulatedProfitPerInterval.Last(i)-r.accumulatedProfitPerInterval.Last(i+r.ShortTermProfitWindow)), fmt.Sprintf("%f", r.accumulatedFeePerInterval.Last(i)), fmt.Sprintf("%f", r.winRatioPerInterval.Last(i)), fmt.Sprintf("%f", r.profitFactorPerInterval.Last(i)), fmt.Sprintf("%f", r.accumulatedTradesPerInterval.Last(i)), } for j := 0; j < len(r.strategyParameters); j++ { values = append(values, r.strategyParameters[j][1]) } _ = tsvwiter.Write(values) } } }