Skip to content

Commit

Permalink
Last added.
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar committed Dec 18, 2023
1 parent 94e2768 commit 00fc19f
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 25 deletions.
49 changes: 43 additions & 6 deletions asset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,58 @@ The information provided on this project is strictly for informational purposes

## Index

- [func CloseOnly\(s \<\-chan \*Snapshot\) \<\-chan float64](<#CloseOnly>)
- [type FileSystemRepository](<#FileSystemRepository>)
- [func NewFileSystemRepository\(base string\) \*FileSystemRepository](<#NewFileSystemRepository>)
- [func \(r \*FileSystemRepository\) Get\(name string\) \(\<\-chan \*Snapshot, error\)](<#FileSystemRepository.Get>)
- [type Repository](<#Repository>)
- [type Snapshot](<#Snapshot>)


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

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

```go
func CloseOnly(s <-chan *Snapshot) <-chan float64
type FileSystemRepository struct {
Repository
// contains filtered or unexported fields
}
```

CloseOnly filters the given snapshot channel and returns a new channel containing only the closing values.
<a name="NewFileSystemRepository"></a>
### func [NewFileSystemRepository](<https://github.com/cinar/indicator/blob/v2/asset/file_system_repository.go#L25>)

```go
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>)

```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="Repository"></a>
## type [Repository](<https://github.com/cinar/indicator/blob/v2/asset/repository.go#L9-L13>)

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

```go
type Repository interface {
// Get attempts to return a channel of snapshots for
// the asset with the given name.
Get(name string) (<-chan *Snapshot, error)
}
```

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

Snapshot captures a single observation of an asset's price at a specific moment.

Expand Down
35 changes: 35 additions & 0 deletions asset/file_system_repository.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 asset

import (
"fmt"
"path/filepath"

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

// FileSystemRepository stores and retrieves asset snapshots using
// the local file system.
type FileSystemRepository struct {
Repository

// base is the root directory where asset snapshots are stored.
base string
}

// NewFileSystemRepository initializes a file system repository with
// the given base directory.
func NewFileSystemRepository(base string) *FileSystemRepository {
return &FileSystemRepository{
base: base,
}
}

// Get attempts to return a channel of snapshots fo the asset with the given name.
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)
}
32 changes: 32 additions & 0 deletions asset/file_system_repository_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) 2023 Onur Cinar. All Rights Reserved.
// The source code is provided under MIT License.
// https://github.com/cinar/indicator

package asset_test

import (
"testing"

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

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

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

helper.Drain(snapshots)
}

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

_, err := repository.Get("brk")
if err == nil {
t.Fatal("expected error")
}
}
20 changes: 20 additions & 0 deletions asset/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) 2023 Onur Cinar. All Rights Reserved.
// The source code is provided under MIT License.
// https://github.com/cinar/indicator

package asset

import "time"

// Repository serves as a centralized storage and retrieval
// location for asset snapshots.
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, if any. Returns an
// empty value if no snapshots exist.
LastDate(name string) time.Time
}
8 changes: 0 additions & 8 deletions asset/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ package asset

import (
"time"

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

// Snapshot captures a single observation of an asset's price
Expand Down Expand Up @@ -36,9 +34,3 @@ type Snapshot struct {
// the asset during the snapshot period.
Volume float64
}

// CloseOnly filters the given snapshot channel and returns a new
// channel containing only the closing values.
func CloseOnly(s <-chan *Snapshot) <-chan float64 {
return helper.Map(s, func(s *Snapshot) float64 { return s.Close })
}
22 changes: 22 additions & 0 deletions asset/testdata/brk-b.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Date,Open,High,Low,Close,Adj Close,Volume
2022-12-01,319,319.559998,313.299988,315.839996,315.839996,4351600
2022-12-02,313.48999,316.380005,312.75,316.149994,316.149994,3025700
2022-12-05,315.220001,315.660004,308.730011,310.570007,310.570007,3835800
2022-12-06,309.950012,310.290009,306.350006,307.779999,307.779999,3877400
2022-12-07,307.070007,309.380005,304.920013,305.820007,305.820007,4130800
2022-12-08,306,307.48999,305.089996,305.98999,305.98999,2351700
2022-12-09,305.320007,308.339996,304.709991,306.390015,306.390015,3326000
2022-12-12,307.549988,311.910004,305.459991,311.450012,311.450012,4366700
2022-12-13,318.399994,318.910004,310.820007,312.329987,312.329987,5042800
2022-12-14,312.73999,316.359985,308.399994,309.290009,309.290009,4056900
2022-12-15,306.429993,306.959991,299.450012,301.910004,301.910004,5103900
2022-12-16,299.049988,302.470001,297.76001,300,300,8305700
2022-12-19,300.51001,301.480011,297.149994,300.029999,300.029999,3842200
2022-12-20,300.089996,304.190002,297,302,302,3090700
2022-12-21,304.380005,308.540009,304.160004,307.820007,307.820007,3264600
2022-12-22,306.100006,306.5,297.640015,302.690002,302.690002,3560100
2022-12-23,302.880005,306.570007,300.929993,306.48999,306.48999,2460400
2022-12-27,306.450012,308.579987,304.649994,305.549988,305.549988,2730900
2022-12-28,304.769989,307.459991,303.26001,303.429993,303.429993,2628200
2022-12-29,305.940002,309.380005,305.23999,309.059998,309.059998,2846200
2022-12-30,306.950012,309.040009,305.619995,308.899994,308.899994,3298300
12 changes: 12 additions & 0 deletions helper/last.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) 2023 Onur Cinar. All Rights Reserved.
// The source code is provided under MIT License.
// https://github.com/cinar/indicator

package helper

// Last takes a channel of values and returns a new channel containing the last N values.
func Last[T any](c <-chan T, count int) <-chan T {

Check warning on line 8 in helper/last.go

View workflow job for this annotation

GitHub Actions / build

parameter 'count' seems to be unused, consider removing or renaming it as _
result := make(chan T, cap(c))

return result
}
35 changes: 35 additions & 0 deletions helper/last_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 TestLast(t *testing.T) {
input := helper.SliceToChan([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
expected := helper.SliceToChan([]int{7, 8, 9, 10})

actual := helper.Last(input, 4)

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

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

actual := helper.Last(input, 4)

err := helper.CheckEquals(actual, expected)
if err != nil {
t.Fatal(err)
}
}
52 changes: 44 additions & 8 deletions helper/ring.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package helper

import "fmt"

// Ring represents a ring structure that can be instantiated
// using the NewRing function.
//
Expand All @@ -17,24 +19,58 @@ package helper
// fmt.Println(ring.Insert(4)) // 2
type Ring[T any] struct {
buffer []T
index int
begin int
end int
}

// NewRing creates a new ring instance with the given size.
func NewRing[T any](size int) *Ring[T] {
return &Ring[T]{
buffer: make([]T, size),
index: 0,
begin: 0,
end: 0,
}
}

// Insert function inserts the specified value into the ring and
// returns the value that was previously stored at that index.
func (r *Ring[T]) Insert(t T) T {
r.index = (r.index + 1) % len(r.buffer)
// Put inserts the specified value into the ring and returns the
// value that was previously stored at that index.
func (r *Ring[T]) Put(t T) T {
o := r.buffer[r.end]
r.buffer[r.end] = t

r.end = r.nextIndex(r.end)

if r.end == r.begin {
r.begin = r.nextIndex(r.begin)
}

o := r.buffer[r.index]
r.buffer[r.index] = t
fmt.Printf("b=%d e=%d buffer=%v\n", r.begin, r.end, r.buffer)

return o
}

// Get retrieves the available value from the ring buffer. If empty,
// it returns the default value (T) and false.
func (r *Ring[T]) Get() (T, bool) {
var t T

if r.IsEmpty() {
return t, false
}

t = r.buffer[r.begin]
r.begin = r.nextIndex(r.begin)

return t, true
}

// IsEmpty checks if the current ring buffer is empty.
func (r *Ring[T]) IsEmpty() bool {
return r.end == r.begin
}

// nextIndex returns the next index in a ring buffer, wrapping
// around if it reaches the capacity.
func (r *Ring[T]) nextIndex(i int) int {
return (i + 1) % len(r.buffer)
}
34 changes: 33 additions & 1 deletion helper/ring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package helper_test

import (
"fmt"
"testing"

"github.com/cinar/indicator/helper"
Expand All @@ -17,10 +18,41 @@ func TestRing(t *testing.T) {
ring := helper.NewRing[int](4)

for i, n := range input {
actual := ring.Insert(n)
actual := ring.Put(n)
if actual != expected[i] {
t.Fatalf("actual %v expected %v", actual, expected[i])
}
}

}

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

ring := helper.NewRing[int](size)

if !ring.IsEmpty() {
t.Fatal("not empty")
}

for _, n := range input {
ring.Put(n)

if ring.IsEmpty() {
t.Fatal("is empty")
}
}

for i := 0; i < size; i++ {
n, ok := ring.Get()
if !ok {
t.Fatal("is empty")
}
fmt.Printf("%d %d\n", i, n)
}

if !ring.IsEmpty() {
t.Fatal("not empty")
}
}
4 changes: 2 additions & 2 deletions strategy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The information provided on this project is strictly for informational purposes

- [func ActionsToAnnotations\(ac \<\-chan Action\) \<\-chan string](<#ActionsToAnnotations>)
- [func NormalizeActions\(ac \<\-chan Action\) \<\-chan Action](<#NormalizeActions>)
- [func Outcome\(values \<\-chan float64, actions \<\-chan Action\) \<\-chan float64](<#Outcome>)
- [func Outcome\[T helper.Number\]\(values \<\-chan T, actions \<\-chan Action\) \<\-chan float64](<#Outcome>)
- [func SpreadActions\(ac \<\-chan Action\) \<\-chan Action](<#SpreadActions>)
- [type Action](<#Action>)
- [func \(a Action\) Annotation\(\) string](<#Action.Annotation>)
Expand Down Expand Up @@ -58,7 +58,7 @@ NormalizeActions transforms the given channel of actions to ensure a consistent
## func [Outcome](<https://github.com/cinar/indicator/blob/v2/strategy/outcome.go#L10>)

```go
func Outcome(values <-chan float64, actions <-chan Action) <-chan float64
func Outcome[T helper.Number](values <-chan T, actions <-chan Action) <-chan float64
```

Outcome simulates the potential result of executing the given actions based on the provided values.
Expand Down

0 comments on commit 00fc19f

Please sign in to comment.