mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 08:15:15 +00:00
feature: optimizer add profitFactor optimization. Optimization value use float64 instead to save memory and boost performance
This commit is contained in:
parent
7d03c69406
commit
7aaea257df
|
@ -81,6 +81,8 @@ type SessionSymbolReport struct {
|
||||||
Manifests Manifests `json:"manifests,omitempty"`
|
Manifests Manifests `json:"manifests,omitempty"`
|
||||||
Sharpe fixedpoint.Value `json:"sharpeRatio"`
|
Sharpe fixedpoint.Value `json:"sharpeRatio"`
|
||||||
Sortino fixedpoint.Value `json:"sortinoRatio"`
|
Sortino fixedpoint.Value `json:"sortinoRatio"`
|
||||||
|
ProfitFactor fixedpoint.Value `json:"profitFactor"`
|
||||||
|
WinningRatio fixedpoint.Value `json:"winningRatio"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *SessionSymbolReport) InitialEquityValue() fixedpoint.Value {
|
func (r *SessionSymbolReport) InitialEquityValue() fixedpoint.Value {
|
||||||
|
|
|
@ -4,11 +4,6 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/c9s/bbgo/pkg/cmd/cmdutil"
|
|
||||||
"github.com/c9s/bbgo/pkg/data/tsv"
|
|
||||||
"github.com/c9s/bbgo/pkg/util"
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -16,6 +11,12 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/cmd/cmdutil"
|
||||||
|
"github.com/c9s/bbgo/pkg/data/tsv"
|
||||||
|
"github.com/c9s/bbgo/pkg/util"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -523,8 +524,11 @@ var BacktestCmd = &cobra.Command{
|
||||||
|
|
||||||
for _, session := range environ.Sessions() {
|
for _, session := range environ.Sessions() {
|
||||||
for symbol, trades := range session.Trades {
|
for symbol, trades := range session.Trades {
|
||||||
intervalProfits := sessionTradeStats[session.Name][symbol].IntervalProfits[types.Interval1d]
|
tradeState := sessionTradeStats[session.Name][symbol]
|
||||||
symbolReport, err := createSymbolReport(userConfig, session, symbol, trades.Trades, intervalProfits)
|
profitFactor := tradeState.ProfitFactor
|
||||||
|
winningRatio := tradeState.WinningRatio
|
||||||
|
intervalProfits := tradeState.IntervalProfits[types.Interval1d]
|
||||||
|
symbolReport, err := createSymbolReport(userConfig, session, symbol, trades.Trades, intervalProfits, profitFactor, winningRatio)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -615,7 +619,8 @@ func collectSubscriptionIntervals(environ *bbgo.Environment) (allKLineIntervals
|
||||||
return allKLineIntervals, requiredInterval, backTestIntervals
|
return allKLineIntervals, requiredInterval, backTestIntervals
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSymbolReport(userConfig *bbgo.Config, session *bbgo.ExchangeSession, symbol string, trades []types.Trade, intervalProfit *types.IntervalProfitCollector) (
|
func createSymbolReport(userConfig *bbgo.Config, session *bbgo.ExchangeSession, symbol string, trades []types.Trade, intervalProfit *types.IntervalProfitCollector,
|
||||||
|
profitFactor, winningRatio fixedpoint.Value) (
|
||||||
*backtest.SessionSymbolReport,
|
*backtest.SessionSymbolReport,
|
||||||
error,
|
error,
|
||||||
) {
|
) {
|
||||||
|
@ -663,6 +668,8 @@ func createSymbolReport(userConfig *bbgo.Config, session *bbgo.ExchangeSession,
|
||||||
// Manifests: manifests,
|
// Manifests: manifests,
|
||||||
Sharpe: sharpeRatio,
|
Sharpe: sharpeRatio,
|
||||||
Sortino: sortinoRatio,
|
Sortino: sortinoRatio,
|
||||||
|
ProfitFactor: profitFactor,
|
||||||
|
WinningRatio: winningRatio,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range session.Subscriptions {
|
for _, s := range session.Subscriptions {
|
||||||
|
|
|
@ -79,7 +79,7 @@ func LoadConfig(yamlConfigFileName string) (*Config, error) {
|
||||||
switch objective := strings.ToLower(optConfig.Objective); objective {
|
switch objective := strings.ToLower(optConfig.Objective); objective {
|
||||||
case "", "default":
|
case "", "default":
|
||||||
optConfig.Objective = HpOptimizerObjectiveEquity
|
optConfig.Objective = HpOptimizerObjectiveEquity
|
||||||
case HpOptimizerObjectiveEquity, HpOptimizerObjectiveProfit, HpOptimizerObjectiveVolume:
|
case HpOptimizerObjectiveEquity, HpOptimizerObjectiveProfit, HpOptimizerObjectiveVolume, HpOptimizerObjectiveProfitFactor:
|
||||||
optConfig.Objective = objective
|
optConfig.Objective = objective
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf(`unknown objective "%s"`, optConfig.Objective)
|
return nil, fmt.Errorf(`unknown objective "%s"`, optConfig.Objective)
|
||||||
|
|
|
@ -14,30 +14,43 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MetricValueFunc func(summaryReport *backtest.SummaryReport) fixedpoint.Value
|
type MetricValueFunc func(summaryReport *backtest.SummaryReport) float64
|
||||||
|
|
||||||
var TotalProfitMetricValueFunc = func(summaryReport *backtest.SummaryReport) fixedpoint.Value {
|
var TotalProfitMetricValueFunc = func(summaryReport *backtest.SummaryReport) float64 {
|
||||||
return summaryReport.TotalProfit
|
return summaryReport.TotalProfit.Float64()
|
||||||
}
|
}
|
||||||
|
|
||||||
var TotalVolume = func(summaryReport *backtest.SummaryReport) fixedpoint.Value {
|
var TotalVolume = func(summaryReport *backtest.SummaryReport) float64 {
|
||||||
if len(summaryReport.SymbolReports) == 0 {
|
if len(summaryReport.SymbolReports) == 0 {
|
||||||
return fixedpoint.Zero
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
buyVolume := summaryReport.SymbolReports[0].PnL.BuyVolume
|
buyVolume := summaryReport.SymbolReports[0].PnL.BuyVolume.Float64()
|
||||||
sellVolume := summaryReport.SymbolReports[0].PnL.SellVolume
|
sellVolume := summaryReport.SymbolReports[0].PnL.SellVolume.Float64()
|
||||||
return buyVolume.Add(sellVolume)
|
return buyVolume + sellVolume
|
||||||
}
|
}
|
||||||
|
|
||||||
var TotalEquityDiff = func(summaryReport *backtest.SummaryReport) fixedpoint.Value {
|
var TotalEquityDiff = func(summaryReport *backtest.SummaryReport) float64 {
|
||||||
if len(summaryReport.SymbolReports) == 0 {
|
if len(summaryReport.SymbolReports) == 0 {
|
||||||
return fixedpoint.Zero
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
initEquity := summaryReport.InitialEquityValue
|
initEquity := summaryReport.InitialEquityValue.Float64()
|
||||||
finalEquity := summaryReport.FinalEquityValue
|
finalEquity := summaryReport.FinalEquityValue.Float64()
|
||||||
return finalEquity.Sub(initEquity)
|
return finalEquity - initEquity
|
||||||
|
}
|
||||||
|
|
||||||
|
var ProfitFactorMetricValueFunc = func(summaryReport *backtest.SummaryReport) float64 {
|
||||||
|
if len(summaryReport.SymbolReports) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if len(summaryReport.SymbolReports) > 1 {
|
||||||
|
panic("multiple symbols' profitfactor optimization not supported")
|
||||||
|
}
|
||||||
|
report := summaryReport.SymbolReports[0]
|
||||||
|
pf := report.ProfitFactor.Float64()
|
||||||
|
win := report.WinningRatio.Float64()
|
||||||
|
return pf*0.9 + win*0.1
|
||||||
}
|
}
|
||||||
|
|
||||||
type Metric struct {
|
type Metric struct {
|
||||||
|
@ -51,7 +64,7 @@ type Metric struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
|
|
||||||
// Value is the metric value of the metric
|
// Value is the metric value of the metric
|
||||||
Value fixedpoint.Value `json:"value,omitempty"`
|
Value float64 `json:"value,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyParams(params []interface{}) []interface{} {
|
func copyParams(params []interface{}) []interface{} {
|
||||||
|
@ -199,6 +212,7 @@ func (o *GridOptimizer) Run(executor Executor, configJson []byte) (map[string][]
|
||||||
"totalProfit": TotalProfitMetricValueFunc,
|
"totalProfit": TotalProfitMetricValueFunc,
|
||||||
"totalVolume": TotalVolume,
|
"totalVolume": TotalVolume,
|
||||||
"totalEquityDiff": TotalEquityDiff,
|
"totalEquityDiff": TotalEquityDiff,
|
||||||
|
"profitFactor": ProfitFactorMetricValueFunc,
|
||||||
}
|
}
|
||||||
var metrics = map[string][]Metric{}
|
var metrics = map[string][]Metric{}
|
||||||
|
|
||||||
|
@ -287,7 +301,7 @@ func (o *GridOptimizer) Run(executor Executor, configJson []byte) (map[string][]
|
||||||
sort.Slice(metrics[n], func(i, j int) bool {
|
sort.Slice(metrics[n], func(i, j int) bool {
|
||||||
a := metrics[n][i].Value
|
a := metrics[n][i].Value
|
||||||
b := metrics[n][j].Value
|
b := metrics[n][j].Value
|
||||||
return a.Compare(b) > 0
|
return a > b
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@ package optimizer
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/c-bata/goptuna"
|
"github.com/c-bata/goptuna"
|
||||||
goptunaCMAES "github.com/c-bata/goptuna/cmaes"
|
goptunaCMAES "github.com/c-bata/goptuna/cmaes"
|
||||||
goptunaSOBOL "github.com/c-bata/goptuna/sobol"
|
goptunaSOBOL "github.com/c-bata/goptuna/sobol"
|
||||||
|
@ -11,10 +14,9 @@ import (
|
||||||
"github.com/cheggaaa/pb/v3"
|
"github.com/cheggaaa/pb/v3"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"math"
|
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// WARNING: the text here could only be lower cases
|
||||||
const (
|
const (
|
||||||
// HpOptimizerObjectiveEquity optimize the parameters to maximize equity gain
|
// HpOptimizerObjectiveEquity optimize the parameters to maximize equity gain
|
||||||
HpOptimizerObjectiveEquity = "equity"
|
HpOptimizerObjectiveEquity = "equity"
|
||||||
|
@ -22,6 +24,8 @@ const (
|
||||||
HpOptimizerObjectiveProfit = "profit"
|
HpOptimizerObjectiveProfit = "profit"
|
||||||
// HpOptimizerObjectiveVolume optimize the parameters to maximize trading volume
|
// HpOptimizerObjectiveVolume optimize the parameters to maximize trading volume
|
||||||
HpOptimizerObjectiveVolume = "volume"
|
HpOptimizerObjectiveVolume = "volume"
|
||||||
|
// HpOptimizerObjectiveProfitFactor optimize the parameters to maximize profit factor
|
||||||
|
HpOptimizerObjectiveProfitFactor = "profitfactor"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -198,6 +202,8 @@ func (o *HyperparameterOptimizer) buildObjective(executor Executor, configJson [
|
||||||
metricValueFunc = TotalVolume
|
metricValueFunc = TotalVolume
|
||||||
case HpOptimizerObjectiveEquity:
|
case HpOptimizerObjectiveEquity:
|
||||||
metricValueFunc = TotalEquityDiff
|
metricValueFunc = TotalEquityDiff
|
||||||
|
case HpOptimizerObjectiveProfitFactor:
|
||||||
|
metricValueFunc = ProfitFactorMetricValueFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(trial goptuna.Trial) (float64, error) {
|
return func(trial goptuna.Trial) (float64, error) {
|
||||||
|
@ -225,7 +231,7 @@ func (o *HyperparameterOptimizer) buildObjective(executor Executor, configJson [
|
||||||
return 0.0, err
|
return 0.0, err
|
||||||
}
|
}
|
||||||
// By config, the Goptuna optimize the parameters by maximize the objective output.
|
// By config, the Goptuna optimize the parameters by maximize the objective output.
|
||||||
return metricValueFunc(summary).Float64(), nil
|
return metricValueFunc(summary), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user