Skip to content

Commit

Permalink
First, Last, and File System Repository.
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar authored Dec 19, 2023
1 parent 00fc19f commit 959094f
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 24 deletions.
22 changes: 18 additions & 4 deletions asset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ The information provided on this project is strictly for informational purposes
- [type FileSystemRepository](<#FileSystemRepository>)
- [func NewFileSystemRepository\(base string\) \*FileSystemRepository](<#NewFileSystemRepository>)
- [func \(r \*FileSystemRepository\) Get\(name string\) \(\<\-chan \*Snapshot, error\)](<#FileSystemRepository.Get>)
- [func \(r \*FileSystemRepository\) LastDate\(name string\) \(time.Time, error\)](<#FileSystemRepository.LastDate>)
- [type Repository](<#Repository>)
- [type Snapshot](<#Snapshot>)


<a name="FileSystemRepository"></a>
## type [FileSystemRepository](<https://github.com/cinar/indicator/blob/v2/asset/file_system_repository.go#L16-L21>)
## type [FileSystemRepository](<https://github.com/cinar/indicator/blob/v2/asset/file_system_repository.go#L18-L23>)

FileSystemRepository stores and retrieves asset snapshots using the local file system.

Expand All @@ -44,7 +45,7 @@ type FileSystemRepository struct {
```

<a name="NewFileSystemRepository"></a>
### func [NewFileSystemRepository](<https://github.com/cinar/indicator/blob/v2/asset/file_system_repository.go#L25>)
### func [NewFileSystemRepository](<https://github.com/cinar/indicator/blob/v2/asset/file_system_repository.go#L27>)

```go
func NewFileSystemRepository(base string) *FileSystemRepository
Expand All @@ -53,16 +54,25 @@ func NewFileSystemRepository(base string) *FileSystemRepository
NewFileSystemRepository initializes a file system repository with the given base directory.

<a name="FileSystemRepository.Get"></a>
### func \(\*FileSystemRepository\) [Get](<https://github.com/cinar/indicator/blob/v2/asset/file_system_repository.go#L32>)
### func \(\*FileSystemRepository\) [Get](<https://github.com/cinar/indicator/blob/v2/asset/file_system_repository.go#L34>)

```go
func (r *FileSystemRepository) Get(name string) (<-chan *Snapshot, error)
```

Get attempts to return a channel of snapshots fo the asset with the given name.

<a name="FileSystemRepository.LastDate"></a>
### func \(\*FileSystemRepository\) [LastDate](<https://github.com/cinar/indicator/blob/v2/asset/file_system_repository.go#L40>)

```go
func (r *FileSystemRepository) LastDate(name string) (time.Time, error)
```

LastDate returns the date of the last snapshot for the asset with the given name.

<a name="Repository"></a>
## type [Repository](<https://github.com/cinar/indicator/blob/v2/asset/repository.go#L9-L13>)
## type [Repository](<https://github.com/cinar/indicator/blob/v2/asset/repository.go#L11-L19>)

Repository serves as a centralized storage and retrieval location for asset snapshots.

Expand All @@ -71,6 +81,10 @@ type Repository interface {
// Get attempts to return a channel of snapshots for
// the asset with the given name.
Get(name string) (<-chan *Snapshot, error)

// LastDate returns the date of the last snapshot for
// the asset with the given name.
LastDate(name string) (time.Time, error)
}
```

Expand Down
20 changes: 20 additions & 0 deletions asset/file_system_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
package asset

import (
"errors"
"fmt"
"path/filepath"
"time"

"github.com/cinar/indicator/helper"
)
Expand All @@ -33,3 +35,21 @@ func (r *FileSystemRepository) Get(name string) (<-chan *Snapshot, error) {
file := filepath.Join(r.base, fmt.Sprintf("%s.csv", name))
return helper.ReadFromCsvFile[Snapshot](file, true)
}

// LastDate returns the date of the last snapshot for the asset with the given name.
func (r *FileSystemRepository) LastDate(name string) (time.Time, error) {
var last time.Time

snapshots, err := r.Get(name)
if err != nil {
return last, err
}

snapshot := helper.ChanToSlice(helper.Last(snapshots, 1))

if len(snapshot) != 1 {
return last, errors.New("empty asset")
}

return snapshot[0].Date, nil
}
34 changes: 34 additions & 0 deletions asset/file_system_repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package asset_test

import (
"testing"
"time"

"github.com/cinar/indicator/asset"
"github.com/cinar/indicator/helper"
Expand All @@ -30,3 +31,36 @@ func TestFileSystemRepositoryGetNonExisting(t *testing.T) {
t.Fatal("expected error")
}
}

func TestFileSystemRepositoryLastDate(t *testing.T) {
expeted := time.Date(2022, 12, 30, 0, 0, 0, 0, time.UTC)

repository := asset.NewFileSystemRepository("testdata")

actual, err := repository.LastDate("brk-b")
if err != nil {
t.Fatal(err)
}

if actual != expeted {
t.Fatalf("actual %v expected %v", actual, expeted)
}
}

func TestFileSystemRepositoryLastDateNonExisting(t *testing.T) {
repository := asset.NewFileSystemRepository("testdata")

_, err := repository.LastDate("brk")
if err == nil {
t.Fatal("expected error")
}
}

func TestFileSystemRepositoryLastDateEmpty(t *testing.T) {
repository := asset.NewFileSystemRepository("testdata")

_, err := repository.LastDate("empty")
if err == nil {
t.Fatal("expected error")
}
}
5 changes: 2 additions & 3 deletions asset/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ type Repository interface {
Get(name string) (<-chan *Snapshot, error)

// LastDate returns the date of the last snapshot for
// the asset with the given name, if any. Returns an
// empty value if no snapshots exist.
LastDate(name string) time.Time
// the asset with the given name.
LastDate(name string) (time.Time, error)
}
1 change: 1 addition & 0 deletions asset/testdata/empty.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Date,Open,High,Low,Close,Adj Close,Volume
54 changes: 47 additions & 7 deletions helper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ The information provided on this project is strictly for informational purposes
- [func Duplicate\[T any\]\(input \<\-chan T, count int\) \[\]\<\-chan T](<#Duplicate>)
- [func Field\[T, S any\]\(c \<\-chan \*S, name string\) \(\<\-chan T, error\)](<#Field>)
- [func Filter\[T Number\]\(c \<\-chan T, p func\(T\) bool\) \<\-chan T](<#Filter>)
- [func First\[T any\]\(c \<\-chan T, count int\) \<\-chan T](<#First>)
- [func Head\[T Number\]\(c \<\-chan T, count int\) \<\-chan T](<#Head>)
- [func IncrementBy\[T Number\]\(c \<\-chan T, i T\) \<\-chan T](<#IncrementBy>)
- [func KeepNegatives\[T Number\]\(c \<\-chan T\) \<\-chan T](<#KeepNegatives>)
- [func KeepPositives\[T Number\]\(c \<\-chan T\) \<\-chan T](<#KeepPositives>)
- [func Last\[T any\]\(c \<\-chan T, count int\) \<\-chan T](<#Last>)
- [func Map\[F, T any\]\(c \<\-chan F, f func\(F\) T\) \<\-chan T](<#Map>)
- [func Multiply\[T Number\]\(ac, bc \<\-chan T\) \<\-chan T](<#Multiply>)
- [func MultiplyBy\[T Number\]\(c \<\-chan T, m T\) \<\-chan T](<#MultiplyBy>)
Expand Down Expand Up @@ -88,7 +90,9 @@ The information provided on this project is strictly for informational purposes
- [func NewNumericReportColumn\[T Number\]\(name string, values \<\-chan T\) ReportColumn](<#NewNumericReportColumn>)
- [type Ring](<#Ring>)
- [func NewRing\[T any\]\(size int\) \*Ring\[T\]](<#NewRing>)
- [func \(r \*Ring\[T\]\) Insert\(t T\) T](<#Ring[T].Insert>)
- [func \(r \*Ring\[T\]\) Get\(\) \(T, bool\)](<#Ring[T].Get>)
- [func \(r \*Ring\[T\]\) IsEmpty\(\) bool](<#Ring[T].IsEmpty>)
- [func \(r \*Ring\[T\]\) Put\(t T\) T](<#Ring[T].Put>)


## Constants
Expand Down Expand Up @@ -344,6 +348,15 @@ even := helper.Filter(c, func(n int) bool {
})
```

<a name="First"></a>
## func [First](<https://github.com/cinar/indicator/blob/v2/helper/first.go#L8>)

```go
func First[T any](c <-chan T, count int) <-chan T
```

First takes a channel of values and returns a new channel containing the first N values.

<a name="Head"></a>
## func [Head](<https://github.com/cinar/indicator/blob/v2/helper/head.go#L16>)

Expand Down Expand Up @@ -412,6 +425,15 @@ positives := helper.KeepPositives(c)
fmt.Println(helper.ChanToSlice(positives)) // [0, 20, 4, 0]
```

<a name="Last"></a>
## func [Last](<https://github.com/cinar/indicator/blob/v2/helper/last.go#L8>)

```go
func Last[T any](c <-chan T, count int) <-chan T
```

Last takes a channel of values and returns a new channel containing the last N values.

<a name="Map"></a>
## func [Map](<https://github.com/cinar/indicator/blob/v2/helper/map.go#L17>)

Expand Down Expand Up @@ -950,7 +972,7 @@ func NewNumericReportColumn[T Number](name string, values <-chan T) ReportColumn
NewNumericReportColumn returns a new instance of a numeric data column for a report.

<a name="Ring"></a>
## type [Ring](<https://github.com/cinar/indicator/blob/v2/helper/ring.go#L18-L21>)
## type [Ring](<https://github.com/cinar/indicator/blob/v2/helper/ring.go#L18-L23>)

Ring represents a ring structure that can be instantiated using the NewRing function.

Expand All @@ -972,21 +994,39 @@ type Ring[T any] struct {
```

<a name="NewRing"></a>
### func [NewRing](<https://github.com/cinar/indicator/blob/v2/helper/ring.go#L24>)
### func [NewRing](<https://github.com/cinar/indicator/blob/v2/helper/ring.go#L26>)

```go
func NewRing[T any](size int) *Ring[T]
```

NewRing creates a new ring instance with the given size.

<a name="Ring[T].Insert"></a>
### func \(\*Ring\[T\]\) [Insert](<https://github.com/cinar/indicator/blob/v2/helper/ring.go#L33>)
<a name="Ring[T].Get"></a>
### func \(\*Ring\[T\]\) [Get](<https://github.com/cinar/indicator/blob/v2/helper/ring.go#L53>)

```go
func (r *Ring[T]) Get() (T, bool)
```

Get retrieves the available value from the ring buffer. If empty, it returns the default value \(T\) and false.

<a name="Ring[T].IsEmpty"></a>
### func \(\*Ring\[T\]\) [IsEmpty](<https://github.com/cinar/indicator/blob/v2/helper/ring.go#L71>)

```go
func (r *Ring[T]) IsEmpty() bool
```

IsEmpty checks if the current ring buffer is empty.

<a name="Ring[T].Put"></a>
### func \(\*Ring\[T\]\) [Put](<https://github.com/cinar/indicator/blob/v2/helper/ring.go#L37>)

```go
func (r *Ring[T]) Insert(t T) T
func (r *Ring[T]) Put(t T) T
```

Insert function inserts the specified value into the ring and returns the value that was previously stored at that index.
Put inserts the specified value into the ring and returns the value that was previously stored at that index.

Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)
26 changes: 26 additions & 0 deletions helper/first.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) 2023 Onur Cinar. All Rights Reserved.
// The source code is provided under MIT License.
// https://github.com/cinar/indicator

package helper

// First takes a channel of values and returns a new channel containing the first N values.
func First[T any](c <-chan T, count int) <-chan T {
result := make(chan T, cap(c))

go func() {
defer close(result)
for i := 0; i < count; i++ {
n, ok := <-c
if !ok {
return
}

result <- n
}

Drain(c)
}()

return result
}
35 changes: 35 additions & 0 deletions helper/first_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2023 Onur Cinar. All Rights Reserved.
// The source code is provided under MIT License.
// https://github.com/cinar/indicator

package helper_test

import (
"testing"

"github.com/cinar/indicator/helper"
)

func TestFirst(t *testing.T) {
input := helper.SliceToChan([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
expected := helper.SliceToChan([]int{1, 2, 3, 4})

actual := helper.First(input, 4)

err := helper.CheckEquals(actual, expected)
if err != nil {
t.Fatal(err)
}
}

func TestFirstLessValues(t *testing.T) {
input := helper.SliceToChan([]int{1, 2})
expected := helper.SliceToChan([]int{1, 2})

actual := helper.First(input, 4)

err := helper.CheckEquals(actual, expected)
if err != nil {
t.Fatal(err)
}
}
15 changes: 15 additions & 0 deletions helper/last.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,20 @@ package helper
func Last[T any](c <-chan T, count int) <-chan T {
result := make(chan T, cap(c))

go func() {
defer close(result)

ring := NewRing[T](count)

for n := range c {
ring.Put(n)
}

for !ring.IsEmpty() {
n, _ := ring.Get()
result <- n
}
}()

return result
}
Loading

0 comments on commit 959094f

Please sign in to comment.