Skip to content

Commit

Permalink
Report factory is added. (#216)
Browse files Browse the repository at this point in the history
# Describe Request

Report factory is added.

# Change Type

New feature.
  • Loading branch information
cinar authored Sep 14, 2024
1 parent 3f77098 commit 6615f03
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 29 deletions.
2 changes: 1 addition & 1 deletion backtest/backtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (b *Backtest) worker(names <-chan string, wg *sync.WaitGroup) {
actions, outcomes := strategy.ComputeWithOutcome(currentStrategy, snapshotsSplice[0])
err = b.report.Write(name, currentStrategy, snapshotsSplice[1], actions, outcomes)
if err != nil {
log.Printf("Unable to report write for %s: %v", name, err)
log.Printf("Unable to write report for %s: %v", name, err)
}
}

Expand Down
2 changes: 1 addition & 1 deletion backtest/html_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ var htmlReportTmpl string
//go:embed "html_asset_report.tmpl"
var htmlAssetReportTmpl string

// HTMLReport is the backtest HTML report interface.
// HTMLReport is the backtest HTML report.
type HTMLReport struct {
Report

Expand Down
42 changes: 42 additions & 0 deletions backtest/report_factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2021-2024 Onur Cinar.
// The source code is provided under GNU AGPLv3 License.
// https://github.com/cinar/indicator

package backtest

import (
"fmt"
)

const (
// HTMLReportBuilderName is the name for the HTML report builder.
HTMLReportBuilderName = "html"
)

// ReportBuilderFunc defines a function to build a new report using the given configuration parameter.
type ReportBuilderFunc func(config string) (Report, error)

// reportBuilders provides mapping for the report builders.
var reportBuilders = map[string]ReportBuilderFunc{
HTMLReportBuilderName: htmlReportBuilder,
}

// RegisterReportBuilder registers the given builder.
func RegisterReportBuilder(name string, builder ReportBuilderFunc) {
reportBuilders[name] = builder
}

// NewReport builds a new report by the given name type and the configuration.
func NewReport(name, config string) (Report, error) {
builder, ok := reportBuilders[name]
if !ok {
return nil, fmt.Errorf("unknown report: %s", name)
}

return builder(config)
}

// htmlReportBuilder builds a new HTML report instance.
func htmlReportBuilder(config string) (Report, error) {
return NewHTMLReport(config), nil
}
53 changes: 53 additions & 0 deletions backtest/report_factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) 2021-2024 Onur Cinar.
// The source code is provided under GNU AGPLv3 License.
// https://github.com/cinar/indicator

package backtest_test

import (
"testing"

"github.com/cinar/indicator/v2/backtest"
)

func TestNewReportUnknown(t *testing.T) {
report, err := backtest.NewReport("unknown", "")
if err == nil {
t.Fatalf("unknown report: %T", report)
}
}

func TestRegisterReportBuilder(t *testing.T) {
builderName := "testbuilder"

report, err := backtest.NewReport(builderName, "")
if err == nil {
t.Fatalf("testbuilder is: %T", report)
}

backtest.RegisterReportBuilder(builderName, func(config string) (backtest.Report, error) {
return backtest.NewHTMLReport(config), nil
})

report, err = backtest.NewReport(builderName, "")
if err != nil {
t.Fatalf("testbuilder is not found: %v", err)
}

_, ok := report.(*backtest.HTMLReport)
if !ok {
t.Fatalf("testbuilder is: %T", report)
}
}

func TestNewReportMemory(t *testing.T) {
report, err := backtest.NewReport(backtest.HTMLReportBuilderName, "")
if err != nil {
t.Fatal(err)
}

_, ok := report.(*backtest.HTMLReport)
if !ok {
t.Fatalf("report not correct type: %T", report)
}
}
52 changes: 25 additions & 27 deletions cmd/indicator-backtest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (

"github.com/cinar/indicator/v2/asset"
"github.com/cinar/indicator/v2/backtest"
"github.com/cinar/indicator/v2/helper"
"github.com/cinar/indicator/v2/strategy"
"github.com/cinar/indicator/v2/strategy/compound"
"github.com/cinar/indicator/v2/strategy/momentum"
Expand All @@ -22,61 +21,60 @@ import (
)

func main() {
var sourceName string
var sourceConfig string
var outputDir string
var repositoryName string
var repositoryConfig string
var reportName string
var reportConfig string
var workers int
var lastDays int
var writeStrategyRerpots bool
var addSplits bool
var addAnds bool
var dateFormat string

fmt.Fprintln(os.Stderr, "Indicator Backtest")
fmt.Fprintln(os.Stderr, "Copyright (c) 2021-2024 Onur Cinar.")
fmt.Fprintln(os.Stderr, "The source code is provided under GNU AGPLv3 License.")
fmt.Fprintln(os.Stderr, "https://github.com/cinar/indicator")
fmt.Fprintln(os.Stderr)

flag.StringVar(&sourceName, "source-name", "filesystem", "source repository type")
flag.StringVar(&sourceConfig, "source-config", "", "source repository config")
flag.StringVar(&outputDir, "output", ".", "output directory")
flag.StringVar(&repositoryName, "repository-name", "filesystem", "repository name")
flag.StringVar(&repositoryConfig, "repository-config", "", "repository config")
flag.StringVar(&reportName, "report-name", "html", "report name")
flag.StringVar(&reportConfig, "report-config", ".", "report type")
flag.IntVar(&workers, "workers", backtest.DefaultBacktestWorkers, "number of concurrent workers")
flag.IntVar(&lastDays, "last", backtest.DefaultLastDays, "number of days to do backtest")
flag.BoolVar(&writeStrategyRerpots, "write-strategy-reports", backtest.DefaultWriteStrategyReports, "write individual strategy reports")
flag.BoolVar(&addSplits, "splits", false, "add the split strategies")
flag.BoolVar(&addAnds, "ands", false, "add the and strategies")
flag.StringVar(&dateFormat, "date-format", helper.DefaultReportDateFormat, "date format to use")
flag.Parse()

source, err := asset.NewRepository(sourceName, sourceConfig)
source, err := asset.NewRepository(repositoryName, repositoryConfig)
if err != nil {
log.Fatalf("unable to initialize source: %v", err)
}

htmlReport := backtest.NewHTMLReport(outputDir)
htmlReport.WriteStrategyReports = writeStrategyRerpots
htmlReport.DateFormat = dateFormat
report, err := backtest.NewReport(repositoryName, repositoryConfig)
if err != nil {
log.Fatalf("unable to initialize report: %v", err)
}

backtest := backtest.NewBacktest(source, htmlReport)
backtest.Workers = workers
backtest.LastDays = lastDays
backtest.Names = append(backtest.Names, flag.Args()...)
backtest.Strategies = append(backtest.Strategies, compound.AllStrategies()...)
backtest.Strategies = append(backtest.Strategies, momentum.AllStrategies()...)
backtest.Strategies = append(backtest.Strategies, strategy.AllStrategies()...)
backtest.Strategies = append(backtest.Strategies, trend.AllStrategies()...)
backtest.Strategies = append(backtest.Strategies, volatility.AllStrategies()...)
backtester := backtest.NewBacktest(source, report)
backtester.Workers = workers
backtester.LastDays = lastDays
backtester.Names = append(backtester.Names, flag.Args()...)
backtester.Strategies = append(backtester.Strategies, compound.AllStrategies()...)
backtester.Strategies = append(backtester.Strategies, momentum.AllStrategies()...)
backtester.Strategies = append(backtester.Strategies, strategy.AllStrategies()...)
backtester.Strategies = append(backtester.Strategies, trend.AllStrategies()...)
backtester.Strategies = append(backtester.Strategies, volatility.AllStrategies()...)

if addSplits {
backtest.Strategies = append(backtest.Strategies, strategy.AllSplitStrategies(backtest.Strategies)...)
backtester.Strategies = append(backtester.Strategies, strategy.AllSplitStrategies(backtester.Strategies)...)
}

if addAnds {
backtest.Strategies = append(backtest.Strategies, strategy.AllAndStrategies(backtest.Strategies)...)
backtester.Strategies = append(backtester.Strategies, strategy.AllAndStrategies(backtester.Strategies)...)
}

err = backtest.Run()
err = backtester.Run()
if err != nil {
log.Fatalf("unable to run backtest: %v", err)
}
Expand Down

0 comments on commit 6615f03

Please sign in to comment.