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