mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-21 22:43:52 +00:00
backtest: calculate realized Sharpe & Sortino ratios
This commit is contained in:
parent
e2774ed2b5
commit
ad1b9a53a1
|
@ -79,6 +79,8 @@ type SessionSymbolReport struct {
|
|||
InitialBalances types.BalanceMap `json:"initialBalances,omitempty"`
|
||||
FinalBalances types.BalanceMap `json:"finalBalances,omitempty"`
|
||||
Manifests Manifests `json:"manifests,omitempty"`
|
||||
Sharpe float64 `json:"sharpeRatio"`
|
||||
Sortino float64 `json:"sortinoRatio"`
|
||||
}
|
||||
|
||||
func (r *SessionSymbolReport) InitialEquityValue() fixedpoint.Value {
|
||||
|
@ -117,6 +119,18 @@ func (r *SessionSymbolReport) Print(wantBaseAssetBaseline bool) {
|
|||
color.Red("ASSET DECREASED: %v %s (%s)", finalQuoteAsset.Sub(initQuoteAsset), r.Market.QuoteCurrency, finalQuoteAsset.Sub(initQuoteAsset).Div(initQuoteAsset).FormatPercentage(2))
|
||||
}
|
||||
|
||||
if r.Sharpe > 0.0 {
|
||||
color.Green("REALIZED SHARPE RATIO: +%v", r.Sharpe)
|
||||
} else {
|
||||
color.Red("REALIZED SHARPE RATIO: +%v", r.Sharpe)
|
||||
}
|
||||
|
||||
if r.Sortino > 0.0 {
|
||||
color.Green("REALIZED SORTINO RATIO: %v", r.Sortino)
|
||||
} else {
|
||||
color.Red("REALIZED SORTINO RATIO: %v", r.Sortino)
|
||||
}
|
||||
|
||||
if wantBaseAssetBaseline {
|
||||
if r.LastPrice.Compare(r.StartPrice) > 0 {
|
||||
color.Green("%s BASE ASSET PERFORMANCE: +%s (= (%s - %s) / %s)",
|
||||
|
|
|
@ -297,6 +297,42 @@ var BacktestCmd = &cobra.Command{
|
|||
var manifests backtest.Manifests
|
||||
var runID = userConfig.GetSignature() + "_" + uuid.NewString()
|
||||
var reportDir = outputDirectory
|
||||
var sessionTradeStats = make(map[string]map[string]*types.TradeStats)
|
||||
|
||||
var tradeCollectorList []*bbgo.TradeCollector
|
||||
for _, exSource := range exchangeSources {
|
||||
sessionName := exSource.Session.Name
|
||||
tradeStatsMap := make(map[string]*types.TradeStats)
|
||||
for usedSymbol := range exSource.Session.Positions() {
|
||||
market, _ := exSource.Session.Market(usedSymbol)
|
||||
position := types.NewPositionFromMarket(market)
|
||||
orderStore := bbgo.NewOrderStore(usedSymbol)
|
||||
orderStore.AddOrderUpdate = true
|
||||
tradeCollector := bbgo.NewTradeCollector(usedSymbol, position, orderStore)
|
||||
|
||||
tradeStats := types.NewTradeStats(usedSymbol)
|
||||
tradeStats.SetIntervalProfitCollector(types.NewIntervalProfitCollector(types.Interval1d, startTime))
|
||||
tradeCollector.OnProfit(func(trade types.Trade, profit *types.Profit) {
|
||||
if profit == nil {
|
||||
return
|
||||
}
|
||||
tradeStats.Add(profit)
|
||||
})
|
||||
tradeStatsMap[usedSymbol] = tradeStats
|
||||
|
||||
orderStore.BindStream(exSource.Session.UserDataStream)
|
||||
tradeCollector.BindStream(exSource.Session.UserDataStream)
|
||||
tradeCollectorList = append(tradeCollectorList, tradeCollector)
|
||||
}
|
||||
sessionTradeStats[sessionName] = tradeStatsMap
|
||||
}
|
||||
kLineHandlers = append(kLineHandlers, func(k types.KLine, _ *backtest.ExchangeDataSource) {
|
||||
if k.Interval == types.Interval1d && k.Closed {
|
||||
for _, collector := range tradeCollectorList {
|
||||
collector.Process()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if generatingReport {
|
||||
if reportFileInSubDir {
|
||||
|
@ -494,7 +530,8 @@ var BacktestCmd = &cobra.Command{
|
|||
|
||||
for _, session := range environ.Sessions() {
|
||||
for symbol, trades := range session.Trades {
|
||||
symbolReport, err := createSymbolReport(userConfig, session, symbol, trades.Trades)
|
||||
intervalProfits := sessionTradeStats[session.Name][symbol].IntervalProfits[types.Interval1d]
|
||||
symbolReport, err := createSymbolReport(userConfig, session, symbol, trades.Trades, intervalProfits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -555,7 +592,10 @@ var BacktestCmd = &cobra.Command{
|
|||
},
|
||||
}
|
||||
|
||||
func createSymbolReport(userConfig *bbgo.Config, session *bbgo.ExchangeSession, symbol string, trades []types.Trade) (*backtest.SessionSymbolReport, error) {
|
||||
func createSymbolReport(userConfig *bbgo.Config, session *bbgo.ExchangeSession, symbol string, trades []types.Trade, intervalProfit *types.IntervalProfitCollector) (
|
||||
*backtest.SessionSymbolReport,
|
||||
error,
|
||||
) {
|
||||
backtestExchange, ok := session.Exchange.(*backtest.Exchange)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected error, exchange instance is not a backtest exchange")
|
||||
|
@ -595,6 +635,8 @@ func createSymbolReport(userConfig *bbgo.Config, session *bbgo.ExchangeSession,
|
|||
InitialBalances: initBalances,
|
||||
FinalBalances: finalBalances,
|
||||
// Manifests: manifests,
|
||||
Sharpe: intervalProfit.GetSharpe(),
|
||||
Sortino: intervalProfit.GetSortino(),
|
||||
}
|
||||
|
||||
for _, s := range session.Subscriptions {
|
||||
|
|
Loading…
Reference in New Issue
Block a user