Skip to content

Commit

Permalink
Merge branch 'master' into issue-192
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar authored Sep 7, 2024
2 parents 86c106a + cba1c86 commit b7dd7c3
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 32 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
*.dll
*.so
*.dylib
indicator-backtest
indicator-sync

# Test binary, built with `go test -c`
*.test
Expand Down
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,12 @@ The [Sync function]() facilitates the synchronization of assets between designat
The `indicator-sync` command line tool also offers the capability of synchronizing data between the Tiingo Repository and the File System Repository. To illustrate its usage, consider the following example command:

```bash
$ indicator-sync -key $TIINGO_KEY -target /home/user/assets -days 30
$ indicator-sync \
-source-name tiingo \
-source-config $TIINGO_KEY \
-target-name filesystem \
-target-config /home/user/assets \
-days 30
```

This command effectively retrieves the most recent snapshots for assets residing within the `/home/user/assets` directory from the Tiingo Repository. In the event that the local asset file is devoid of content, it automatically extends its reach to synchronize 30 days' worth of snapshots, ensuring a comprehensive and up-to-date repository.
Expand All @@ -201,7 +206,11 @@ if err != nil {
The `indicator-backtest` command line tool empowers users to conduct comprehensive backtesting of assets residing within a specified repository. This capability encompasses the application of all currently recognized strategies, culminating in the generation of detailed reports within a designated output directory.

```bash
$ indicator-backtest -repository /home/user/assets -output /home/user/reports -workers 1
$ indicator-backtest \
-source-name filesystem \
-source-config /home/user/assets \
-output /home/user/reports \
-workers 1
```

Usage
Expand Down
45 changes: 45 additions & 0 deletions asset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The information provided on this project is strictly for informational purposes

- [Constants](<#constants>)
- [Variables](<#variables>)
- [func RegisterRepositoryBuilder\(name string, builder RepositoryBuilderFunc\)](<#RegisterRepositoryBuilder>)
- [func SnapshotsAsClosings\(snapshots \<\-chan \*Snapshot\) \<\-chan float64](<#SnapshotsAsClosings>)
- [func SnapshotsAsDates\(snapshots \<\-chan \*Snapshot\) \<\-chan time.Time](<#SnapshotsAsDates>)
- [func SnapshotsAsHighs\(snapshots \<\-chan \*Snapshot\) \<\-chan float64](<#SnapshotsAsHighs>)
Expand All @@ -47,6 +48,8 @@ The information provided on this project is strictly for informational purposes
- [func \(r \*InMemoryRepository\) GetSince\(name string, date time.Time\) \(\<\-chan \*Snapshot, error\)](<#InMemoryRepository.GetSince>)
- [func \(r \*InMemoryRepository\) LastDate\(name string\) \(time.Time, error\)](<#InMemoryRepository.LastDate>)
- [type Repository](<#Repository>)
- [func NewRepository\(name, config string\) \(Repository, error\)](<#NewRepository>)
- [type RepositoryBuilderFunc](<#RepositoryBuilderFunc>)
- [type Snapshot](<#Snapshot>)
- [type Sync](<#Sync>)
- [func NewSync\(\) \*Sync](<#NewSync>)
Expand All @@ -65,6 +68,21 @@ The information provided on this project is strictly for informational purposes

## Constants

<a name="InMemoryRepositoryBuilderName"></a>

```go
const (
// InMemoryRepositoryBuilderName is the name for the in memory repository builder.
InMemoryRepositoryBuilderName = "memory"

// FileSystemRepositoryBuilderName is the name for the file system repository builder.
FileSystemRepositoryBuilderName = "filesystem"

// TiingoRepositoryBuilderName is the name of the Tiingo repository builder.
TiingoRepositoryBuilderName = "tiingo"
)
```

<a name="DefaultSyncWorkers"></a>

```go
Expand All @@ -91,6 +109,15 @@ var ErrRepositoryAssetEmpty = errors.New("asset empty")
var ErrRepositoryAssetNotFound = errors.New("asset is not found")
```

<a name="RegisterRepositoryBuilder"></a>
## func [RegisterRepositoryBuilder](<https://github.com/cinar/indicator/blob/master/asset/repository_factory.go#L33>)

```go
func RegisterRepositoryBuilder(name string, builder RepositoryBuilderFunc)
```

RegisterRepositoryBuilder registers the given builder.

<a name="SnapshotsAsClosings"></a>
## func [SnapshotsAsClosings](<https://github.com/cinar/indicator/blob/master/asset/snapshot.go#L79>)

Expand Down Expand Up @@ -303,6 +330,24 @@ type Repository interface {
}
```

<a name="NewRepository"></a>
### func [NewRepository](<https://github.com/cinar/indicator/blob/master/asset/repository_factory.go#L38>)

```go
func NewRepository(name, config string) (Repository, error)
```

NewRepository builds a new repository by the given name type and the configuration.

<a name="RepositoryBuilderFunc"></a>
## type [RepositoryBuilderFunc](<https://github.com/cinar/indicator/blob/master/asset/repository_factory.go#L23>)

RepositoryBuilderFunc defines a function to build a new repository using the given configuration parameter.

```go
type RepositoryBuilderFunc func(config string) (Repository, error)
```

<a name="Snapshot"></a>
## type [Snapshot](<https://github.com/cinar/indicator/blob/master/asset/snapshot.go#L15-L38>)

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

package asset

import (
"fmt"
)

const (
// InMemoryRepositoryBuilderName is the name for the in memory repository builder.
InMemoryRepositoryBuilderName = "memory"

// FileSystemRepositoryBuilderName is the name for the file system repository builder.
FileSystemRepositoryBuilderName = "filesystem"

// TiingoRepositoryBuilderName is the name of the Tiingo repository builder.
TiingoRepositoryBuilderName = "tiingo"
)

// RepositoryBuilderFunc defines a function to build a new repository using the given configuration parameter.
type RepositoryBuilderFunc func(config string) (Repository, error)

// repositoryBuilders provides mapping for the repository builders.
var repositoryBuilders = map[string]RepositoryBuilderFunc{
InMemoryRepositoryBuilderName: inMemoryRepositoryBuilder,
FileSystemRepositoryBuilderName: fileSystemRepositoryBuilder,
TiingoRepositoryBuilderName: tiingoRepositoryBuilder,
}

// RegisterRepositoryBuilder registers the given builder.
func RegisterRepositoryBuilder(name string, builder RepositoryBuilderFunc) {
repositoryBuilders[name] = builder
}

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

return builder(config)
}

// inMemoryRepositoryBuilder builds a new in memory repository instance.
func inMemoryRepositoryBuilder(_ string) (Repository, error) {
return NewInMemoryRepository(), nil
}

// fileSystemRepositoryBuilder builds a new file system repository instance.
func fileSystemRepositoryBuilder(config string) (Repository, error) {
return NewFileSystemRepository(config), nil
}

// tiingoRepositoryBuilder builds a new Tiingo repository instance.
func tiingoRepositoryBuilder(config string) (Repository, error) {
return NewTiingoRepository(config), nil
}
77 changes: 77 additions & 0 deletions asset/repository_factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) 2021-2024 Onur Cinar.
// The source code is provided under GNU AGPLv3 License.
// https://github.com/cinar/indicator

package asset_test

import (
"testing"

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

func TestNewRepositoryUnknown(t *testing.T) {
repository, err := asset.NewRepository("unknown", "")
if err == nil {
t.Fatalf("unknown repository: %T", repository)
}
}

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

repository, err := asset.NewRepository(builderName, "")
if err == nil {
t.Fatalf("testbuilder is: %T", repository)
}

asset.RegisterRepositoryBuilder(builderName, func(_ string) (asset.Repository, error) {
return asset.NewInMemoryRepository(), nil
})

repository, err = asset.NewRepository(builderName, "")
if err != nil {
t.Fatalf("testbuilder is not found: %v", err)
}

_, ok := repository.(*asset.InMemoryRepository)
if !ok {
t.Fatalf("testbuilder is: %T", repository)
}
}

func TestNewRepositoryMemory(t *testing.T) {
repository, err := asset.NewRepository(asset.InMemoryRepositoryBuilderName, "")
if err != nil {
t.Fatal(err)
}

_, ok := repository.(*asset.InMemoryRepository)
if !ok {
t.Fatalf("repository not correct type: %T", repository)
}
}

func TestNewRepositoryFileSystem(t *testing.T) {
repository, err := asset.NewRepository(asset.FileSystemRepositoryBuilderName, "testdata")
if err != nil {
t.Fatal(err)
}

_, ok := repository.(*asset.FileSystemRepository)
if !ok {
t.Fatalf("repository not correct type: %T", repository)
}
}

func TestNewTiingoRepository(t *testing.T) {
repository, err := asset.NewRepository(asset.TiingoRepositoryBuilderName, "1234")
if err != nil {
t.Fatal(err)
}

_, ok := repository.(*asset.TiingoRepository)
if !ok {
t.Fatalf("repository not correct type: %T", repository)
}
}
19 changes: 11 additions & 8 deletions cmd/indicator-backtest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import (
)

func main() {
var repositoryDir string
var sourceName string
var sourceConfig string
var outputDir string
var workers int
var lastDays int
Expand All @@ -36,7 +37,8 @@ func main() {
fmt.Fprintln(os.Stderr, "https://github.com/cinar/indicator")
fmt.Fprintln(os.Stderr)

flag.StringVar(&repositoryDir, "repository", ".", "file system repository directory")
flag.StringVar(&sourceName, "source-name", "filesystem", "source repository type")
flag.StringVar(&sourceConfig, "source-config", "", "source repository config")
flag.StringVar(&outputDir, "output", ".", "output directory")
flag.IntVar(&workers, "workers", strategy.DefaultBacktestWorkers, "number of concurrent workers")
flag.IntVar(&lastDays, "last", strategy.DefaultLastDays, "number of days to do backtest")
Expand All @@ -46,11 +48,12 @@ func main() {
flag.StringVar(&dateFormat, "date-format", helper.DefaultReportDateFormat, "date format to use")
flag.Parse()

flag.Parse()

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

backtest := strategy.NewBacktest(repository, outputDir)
backtest := strategy.NewBacktest(source, outputDir)
backtest.Workers = workers
backtest.LastDays = lastDays
backtest.WriteStrategyReports = writeStrategyRerpots
Expand All @@ -70,8 +73,8 @@ func main() {
backtest.Strategies = append(backtest.Strategies, strategy.AllAndStrategies(backtest.Strategies)...)
}

err := backtest.Run()
err = backtest.Run()
if err != nil {
log.Fatal(err)
log.Fatalf("unable to run backtest: %v", err)
}
}
37 changes: 26 additions & 11 deletions cmd/indicator-sync/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ import (
)

func main() {
var tiingoKey string
var targetBase string
var sourceName string
var sourceConfig string
var targetName string
var targetConfig string
var minusDays int
var workers int
var delay int
Expand All @@ -28,29 +30,42 @@ func main() {
fmt.Fprintln(os.Stderr, "https://github.com/cinar/indicator")
fmt.Fprintln(os.Stderr)

flag.StringVar(&tiingoKey, "key", "", "tiingo service api key")
flag.StringVar(&targetBase, "target", ".", "target repository base directory")
flag.StringVar(&sourceName, "source-name", "tiingo", "source repository type")
flag.StringVar(&sourceConfig, "source-config", "", "source repository config")
flag.StringVar(&targetName, "target-name", "filesystem", "target repository type")
flag.StringVar(&targetConfig, "target-config", "", "target repository config")
flag.IntVar(&minusDays, "days", 0, "lookback period in days for the new assets")
flag.IntVar(&workers, "workers", asset.DefaultSyncWorkers, "number of concurrent workers")
flag.IntVar(&delay, "delay", asset.DefaultSyncDelay, "delay between each get")
flag.Parse()

if tiingoKey == "" {
log.Fatal("Tiingo API key required")
source, err := asset.NewRepository(sourceName, sourceConfig)
if err != nil {
log.Fatalf("unable to initialize source: %v", err)
}

target, err := asset.NewRepository(targetName, targetConfig)
if err != nil {
log.Fatalf("unable to initialize target: %v", err)
}

defaultStartDate := time.Now().AddDate(0, 0, -minusDays)

source := asset.NewTiingoRepository(tiingoKey)
target := asset.NewFileSystemRepository(targetBase)
assets := flag.Args()
if len(assets) == 0 {
assets, err = source.Assets()
if err != nil {
log.Fatalf("unable to get assets: %v", err)
}
}

sync := asset.NewSync()
sync.Workers = workers
sync.Delay = delay
sync.Assets = flag.Args()
sync.Assets = assets

err := sync.Run(source, target, defaultStartDate)
err = sync.Run(source, target, defaultStartDate)
if err != nil {
log.Fatal(err)
log.Fatalf("unable to sync repositories: %v", err)
}
}
5 changes: 4 additions & 1 deletion helper/skip.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ func Skip[T any](c <-chan T, count int) <-chan T {

go func() {
for i := 0; i < count; i++ {
<-c
_, ok := <-c
if !ok {
break
}
}

Pipe(c, result)
Expand Down
4 changes: 2 additions & 2 deletions strategy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const (
```

<a name="ActionSources"></a>
## func [ActionSources](<https://github.com/cinar/indicator/blob/master/strategy/strategy.go#L67>)
## func [ActionSources](<https://github.com/cinar/indicator/blob/master/strategy/strategy.go#L62>)

```go
func ActionSources(strategies []Strategy, snapshots <-chan *asset.Snapshot) []<-chan Action
Expand Down Expand Up @@ -555,7 +555,7 @@ func AllSplitStrategies(strategies []Strategy) []Strategy
AllSplitStrategies performs a cartesian product operation on the given strategies, resulting in a collection containing all split strategies formed by combining individual buy and sell strategies.

<a name="AllStrategies"></a>
### func [AllStrategies](<https://github.com/cinar/indicator/blob/master/strategy/strategy.go#L59>)
### func [AllStrategies](<https://github.com/cinar/indicator/blob/master/strategy/strategy.go#L54>)

```go
func AllStrategies() []Strategy
Expand Down
Loading

0 comments on commit b7dd7c3

Please sign in to comment.