optimizer: rename optimizeex to hoptimize

This commit is contained in:
Raphanus Lo 2022-08-02 12:44:42 +08:00
parent bad0aa31b7
commit 68af2d0ff8
5 changed files with 165 additions and 162 deletions

View File

@ -1,6 +1,6 @@
# usage: # usage:
# #
# go run ./cmd/bbgo optimizeex --config bollmaker_ethusdt.yaml --optimizer-config optimizer-hyperparam-search.yaml # go run ./cmd/bbgo hoptimize --config bollmaker_ethusdt.yaml --optimizer-config optimizer-hyperparam-search.yaml
# #
--- ---
# The search algorithm. Supports the following algorithms: # The search algorithm. Supports the following algorithms:

View File

@ -4,29 +4,27 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/c9s/bbgo/pkg/data/tsv"
"github.com/c9s/bbgo/pkg/optimizer" "github.com/c9s/bbgo/pkg/optimizer"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"time" "time"
) )
func init() { func init() {
optimizeExCmd.Flags().String("optimizer-config", "optimizer.yaml", "config file") hoptimizeCmd.Flags().String("optimizer-config", "optimizer.yaml", "config file")
optimizeExCmd.Flags().String("name", "", "assign a name to the study") hoptimizeCmd.Flags().String("name", "", "assign an optimization session name")
optimizeExCmd.Flags().Bool("json-keep-all", false, "keep all results of trials") hoptimizeCmd.Flags().Bool("json-keep-all", false, "keep all results of trials")
optimizeExCmd.Flags().String("output", "output", "backtest report output directory") hoptimizeCmd.Flags().String("output", "output", "backtest report output directory")
optimizeExCmd.Flags().Bool("json", false, "print optimizer metrics in json format") hoptimizeCmd.Flags().Bool("json", false, "print optimizer metrics in json format")
optimizeExCmd.Flags().Bool("tsv", false, "print optimizer metrics in csv format") hoptimizeCmd.Flags().Bool("tsv", false, "print optimizer metrics in csv format")
RootCmd.AddCommand(optimizeExCmd) RootCmd.AddCommand(hoptimizeCmd)
} }
var optimizeExCmd = &cobra.Command{ var hoptimizeCmd = &cobra.Command{
Use: "optimizeex", Use: "hoptimize",
Short: "run hyperparameter optimizer (experimental)", Short: "run hyperparameter optimizer (experimental)",
// SilenceUsage is an option to silence usage when an error occurs. // SilenceUsage is an option to silence usage when an error occurs.
@ -43,7 +41,7 @@ var optimizeExCmd = &cobra.Command{
return err return err
} }
studyName, err := cmd.Flags().GetString("name") optSessionName, err := cmd.Flags().GetString("name")
if err != nil { if err != nil {
return err return err
} }
@ -94,10 +92,10 @@ var optimizeExCmd = &cobra.Command{
defer cancel() defer cancel()
_ = ctx _ = ctx
if len(studyName) == 0 { if len(optSessionName) == 0 {
studyName = fmt.Sprintf("bbgo-hpopt-%v", time.Now().UnixMilli()) optSessionName = fmt.Sprintf("bbgo-hpopt-%v", time.Now().UnixMilli())
} }
tempDirNameFormat := fmt.Sprintf("%s-config-*", studyName) tempDirNameFormat := fmt.Sprintf("%s-config-*", optSessionName)
configDir, err := os.MkdirTemp("", tempDirNameFormat) configDir, err := os.MkdirTemp("", tempDirNameFormat)
if err != nil { if err != nil {
return err return err
@ -112,8 +110,8 @@ var optimizeExCmd = &cobra.Command{
} }
optz := &optimizer.HyperparameterOptimizer{ optz := &optimizer.HyperparameterOptimizer{
StudyName: studyName, SessionName: optSessionName,
Config: optConfig, Config: optConfig,
} }
if err := executor.Prepare(configJson); err != nil { if err := executor.Prepare(configJson); err != nil {
@ -137,13 +135,13 @@ var optimizeExCmd = &cobra.Command{
// print report JSON to stdout // print report JSON to stdout
fmt.Println(string(out)) fmt.Println(string(out))
} else if printTsvFormat { } else if printTsvFormat {
if err := formatResultsTsv(os.Stdout, report.Parameters, report.Trials); err != nil { if err := optimizer.FormatResultsTsv(os.Stdout, report.Parameters, report.Trials); err != nil {
return err return err
} }
} else { } else {
color.Green("OPTIMIZER REPORT") color.Green("OPTIMIZER REPORT")
color.Green("===============================================\n") color.Green("===============================================\n")
color.Green("STUDY NAME: %s\n", report.Name) color.Green("SESSION NAME: %s\n", report.Name)
color.Green("OPTIMIZE OBJECTIVE: %s\n", report.Objective) color.Green("OPTIMIZE OBJECTIVE: %s\n", report.Objective)
color.Green("BEST OBJECTIVE VALUE: %s\n", report.Best.Value) color.Green("BEST OBJECTIVE VALUE: %s\n", report.Best.Value)
color.Green("OPTIMAL PARAMETERS:") color.Green("OPTIMAL PARAMETERS:")
@ -160,44 +158,3 @@ var optimizeExCmd = &cobra.Command{
return nil return nil
}, },
} }
func formatResultsTsv(writer io.WriteCloser, labelPaths map[string]string, results []*optimizer.HyperparameterOptimizeTrialResult) error {
headerLen := len(labelPaths)
headers := make([]string, 0, headerLen)
for label := range labelPaths {
headers = append(headers, label)
}
rows := make([][]interface{}, len(labelPaths))
for ri, result := range results {
row := make([]interface{}, headerLen)
for ci, columnKey := range headers {
var ok bool
if row[ci], ok = result.Parameters[columnKey]; !ok {
return fmt.Errorf(`missing parameter "%s" from trial result (%v)`, columnKey, result.Parameters)
}
}
rows[ri] = row
}
w := tsv.NewWriter(writer)
if err := w.Write(headers); err != nil {
return err
}
for _, row := range rows {
var cells []string
for _, o := range row {
cell, err := castCellValue(o)
if err != nil {
return err
}
cells = append(cells, cell)
}
if err := w.Write(cells); err != nil {
return err
}
}
return w.Close()
}

View File

@ -4,16 +4,12 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"strconv"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/c9s/bbgo/pkg/data/tsv"
"github.com/c9s/bbgo/pkg/fixedpoint"
"github.com/c9s/bbgo/pkg/optimizer" "github.com/c9s/bbgo/pkg/optimizer"
) )
@ -119,7 +115,7 @@ var optimizeCmd = &cobra.Command{
// print metrics JSON to stdout // print metrics JSON to stdout
fmt.Println(string(out)) fmt.Println(string(out))
} else if printTsvFormat { } else if printTsvFormat {
if err := formatMetricsTsv(metrics, os.Stdout); err != nil { if err := optimizer.FormatMetricsTsv(os.Stdout, metrics); err != nil {
return err return err
} }
} else { } else {
@ -138,95 +134,3 @@ var optimizeCmd = &cobra.Command{
return nil return nil
}, },
} }
func transformMetricsToRows(metrics map[string][]optimizer.Metric) (headers []string, rows [][]interface{}) {
var metricsKeys []string
for k := range metrics {
metricsKeys = append(metricsKeys, k)
}
var numEntries int
var paramLabels []string
for _, ms := range metrics {
for _, m := range ms {
paramLabels = m.Labels
break
}
numEntries = len(ms)
break
}
headers = append(paramLabels, metricsKeys...)
rows = make([][]interface{}, numEntries)
var metricsRows = make([][]interface{}, numEntries)
// build params into the rows
for i, m := range metrics[metricsKeys[0]] {
rows[i] = m.Params
}
for _, metricKey := range metricsKeys {
for i, ms := range metrics[metricKey] {
if len(metricsRows[i]) == 0 {
metricsRows[i] = make([]interface{}, 0, len(metricsKeys))
}
metricsRows[i] = append(metricsRows[i], ms.Value)
}
}
// merge rows
for i := range rows {
rows[i] = append(rows[i], metricsRows[i]...)
}
return headers, rows
}
func formatMetricsTsv(metrics map[string][]optimizer.Metric, writer io.WriteCloser) error {
headers, rows := transformMetricsToRows(metrics)
w := tsv.NewWriter(writer)
if err := w.Write(headers); err != nil {
return err
}
for _, row := range rows {
var cells []string
for _, o := range row {
cell, err := castCellValue(o)
if err != nil {
return err
}
cells = append(cells, cell)
}
if err := w.Write(cells); err != nil {
return err
}
}
return w.Close()
}
func castCellValue(a interface{}) (string, error) {
switch tv := a.(type) {
case fixedpoint.Value:
return tv.String(), nil
case float64:
return strconv.FormatFloat(tv, 'f', -1, 64), nil
case int64:
return strconv.FormatInt(tv, 10), nil
case int32:
return strconv.FormatInt(int64(tv), 10), nil
case int:
return strconv.Itoa(tv), nil
case bool:
return strconv.FormatBool(tv), nil
case string:
return tv, nil
case []byte:
return string(tv), nil
default:
return "", fmt.Errorf("unsupported object type: %T value: %v", tv, tv)
}
}

142
pkg/optimizer/format.go Normal file
View File

@ -0,0 +1,142 @@
package optimizer
import (
"fmt"
"github.com/c9s/bbgo/pkg/data/tsv"
"github.com/c9s/bbgo/pkg/fixedpoint"
"io"
"strconv"
)
func FormatResultsTsv(writer io.WriteCloser, labelPaths map[string]string, results []*HyperparameterOptimizeTrialResult) error {
headerLen := len(labelPaths)
headers := make([]string, 0, headerLen)
for label := range labelPaths {
headers = append(headers, label)
}
rows := make([][]interface{}, len(labelPaths))
for ri, result := range results {
row := make([]interface{}, headerLen)
for ci, columnKey := range headers {
var ok bool
if row[ci], ok = result.Parameters[columnKey]; !ok {
return fmt.Errorf(`missing parameter "%s" from trial result (%v)`, columnKey, result.Parameters)
}
}
rows[ri] = row
}
w := tsv.NewWriter(writer)
if err := w.Write(headers); err != nil {
return err
}
for _, row := range rows {
var cells []string
for _, o := range row {
cell, err := castCellValue(o)
if err != nil {
return err
}
cells = append(cells, cell)
}
if err := w.Write(cells); err != nil {
return err
}
}
return w.Close()
}
func FormatMetricsTsv(writer io.WriteCloser, metrics map[string][]Metric) error {
headers, rows := transformMetricsToRows(metrics)
w := tsv.NewWriter(writer)
if err := w.Write(headers); err != nil {
return err
}
for _, row := range rows {
var cells []string
for _, o := range row {
cell, err := castCellValue(o)
if err != nil {
return err
}
cells = append(cells, cell)
}
if err := w.Write(cells); err != nil {
return err
}
}
return w.Close()
}
func transformMetricsToRows(metrics map[string][]Metric) (headers []string, rows [][]interface{}) {
var metricsKeys []string
for k := range metrics {
metricsKeys = append(metricsKeys, k)
}
var numEntries int
var paramLabels []string
for _, ms := range metrics {
for _, m := range ms {
paramLabels = m.Labels
break
}
numEntries = len(ms)
break
}
headers = append(paramLabels, metricsKeys...)
rows = make([][]interface{}, numEntries)
var metricsRows = make([][]interface{}, numEntries)
// build params into the rows
for i, m := range metrics[metricsKeys[0]] {
rows[i] = m.Params
}
for _, metricKey := range metricsKeys {
for i, ms := range metrics[metricKey] {
if len(metricsRows[i]) == 0 {
metricsRows[i] = make([]interface{}, 0, len(metricsKeys))
}
metricsRows[i] = append(metricsRows[i], ms.Value)
}
}
// merge rows
for i := range rows {
rows[i] = append(rows[i], metricsRows[i]...)
}
return headers, rows
}
func castCellValue(a interface{}) (string, error) {
switch tv := a.(type) {
case fixedpoint.Value:
return tv.String(), nil
case float64:
return strconv.FormatFloat(tv, 'f', -1, 64), nil
case int64:
return strconv.FormatInt(tv, 10), nil
case int32:
return strconv.FormatInt(int64(tv), 10), nil
case int:
return strconv.Itoa(tv), nil
case bool:
return strconv.FormatBool(tv), nil
case string:
return tv, nil
case []byte:
return string(tv), nil
default:
return "", fmt.Errorf("unsupported object type: %T value: %v", tv, tv)
}
}

View File

@ -75,8 +75,8 @@ func buildHyperparameterOptimizeTrialResults(study *goptuna.Study) []*Hyperparam
} }
type HyperparameterOptimizer struct { type HyperparameterOptimizer struct {
StudyName string SessionName string
Config *Config Config *Config
// Workaround for goptuna/tpe parameter suggestion. Remove this after fixed. // Workaround for goptuna/tpe parameter suggestion. Remove this after fixed.
// ref: https://github.com/c-bata/goptuna/issues/236 // ref: https://github.com/c-bata/goptuna/issues/236
@ -112,7 +112,7 @@ func (o *HyperparameterOptimizer) buildStudy(trialFinishChan chan goptuna.Frozen
studyOpts = append(studyOpts, goptuna.StudyOptionRelativeSampler(relativeSampler)) studyOpts = append(studyOpts, goptuna.StudyOptionRelativeSampler(relativeSampler))
} }
return goptuna.CreateStudy(o.StudyName, studyOpts...) return goptuna.CreateStudy(o.SessionName, studyOpts...)
} }
func (o *HyperparameterOptimizer) buildParamDomains() (map[string]string, []paramDomain) { func (o *HyperparameterOptimizer) buildParamDomains() (map[string]string, []paramDomain) {
@ -288,7 +288,7 @@ func (o *HyperparameterOptimizer) Run(executor Executor, configJson []byte) (*Hy
bar.Finish() bar.Finish()
return &HyperparameterOptimizeReport{ return &HyperparameterOptimizeReport{
Name: o.StudyName, Name: o.SessionName,
Objective: o.Config.Objective, Objective: o.Config.Objective,
Parameters: labelPaths, Parameters: labelPaths,
Best: buildBestHyperparameterOptimizeResult(study), Best: buildBestHyperparameterOptimizeResult(study),