Skip to content

Commit

Permalink
APO Strategy is added.
Browse files Browse the repository at this point in the history
  • Loading branch information
cinar committed Dec 10, 2023
1 parent b658e2b commit 0c9db68
Show file tree
Hide file tree
Showing 19 changed files with 810 additions and 36 deletions.
12 changes: 11 additions & 1 deletion asset/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,21 @@ The information provided on this project is strictly for informational purposes

## Index

- [func CloseOnly\(s \<\-chan \*Snapshot\) \<\-chan float64](<#CloseOnly>)
- [type Snapshot](<#Snapshot>)


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

```go
func CloseOnly(s <-chan *Snapshot) <-chan float64
```

CloseOnly filters the given snapshot channel and returns a new channel containing only the closing values.

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

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

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

import (
"time"

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

// Snapshot captures a single observation of an asset's price
Expand Down Expand Up @@ -34,3 +36,9 @@ 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 })
}
34 changes: 23 additions & 11 deletions helper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ The information provided on this project is strictly for informational purposes
- [func Abs\[T Number\]\(c \<\-chan T\) \<\-chan T](<#Abs>)
- [func Add\[T Number\]\(ac, bc \<\-chan T\) \<\-chan T](<#Add>)
- [func Apply\[T Number\]\(c \<\-chan T, f func\(T\) T\) \<\-chan T](<#Apply>)
- [func Buffered\[T any\]\(c \<\-chan T, size int\) \<\-chan T](<#Buffered>)
- [func ChanToSlice\[T any\]\(c \<\-chan T\) \[\]T](<#ChanToSlice>)
- [func Change\[T Number\]\(c \<\-chan T, before int\) \<\-chan T](<#Change>)
- [func ChangePercent\[T Number\]\(c \<\-chan T, before int\) \<\-chan T](<#ChangePercent>)
- [func CheckEquals\[T Number\]\(inputs ...\<\-chan T\) error](<#CheckEquals>)
- [func CheckEquals\[T comparable\]\(inputs ...\<\-chan T\) error](<#CheckEquals>)
- [func DecrementBy\[T Number\]\(c \<\-chan T, d T\) \<\-chan T](<#DecrementBy>)
- [func Divide\[T Number\]\(ac, bc \<\-chan T\) \<\-chan T](<#Divide>)
- [func DivideBy\[T Number\]\(c \<\-chan T, d T\) \<\-chan T](<#DivideBy>)
Expand All @@ -46,17 +47,17 @@ The information provided on this project is strictly for informational purposes
- [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>)
- [func Operate\[T Number\]\(ac, bc \<\-chan T, o func\(T, T\) T\) \<\-chan T](<#Operate>)
- [func Pipe\[T Number\]\(f \<\-chan T, t chan\<\- T\)](<#Pipe>)
- [func Operate\[T Number, R any\]\(ac, bc \<\-chan T, o func\(T, T\) R\) \<\-chan R](<#Operate>)
- [func Pipe\[T any\]\(f \<\-chan T, t chan\<\- T\)](<#Pipe>)
- [func Pow\[T Number\]\(c \<\-chan T, y T\) \<\-chan T](<#Pow>)
- [func ReadFromCsvFile\[T any\]\(fileName string, hasHeader bool\) \(\<\-chan \*T, error\)](<#ReadFromCsvFile>)
- [func RoundDigit\[T Number\]\(n T, d int\) T](<#RoundDigit>)
- [func RoundDigits\[T Number\]\(c \<\-chan T, d int\) \<\-chan T](<#RoundDigits>)
- [func Seq\[T Number\]\(from, to, increment T\) \<\-chan T](<#Seq>)
- [func Shift\[T Number\]\(c \<\-chan T, count int, fill T\) \<\-chan T](<#Shift>)
- [func Shift\[T any\]\(c \<\-chan T, count int, fill T\) \<\-chan T](<#Shift>)
- [func Sign\[T Number\]\(c \<\-chan T\) \<\-chan T](<#Sign>)
- [func Since\[T Number\]\(c \<\-chan T\) \<\-chan T](<#Since>)
- [func Skip\[T Number\]\(c \<\-chan T, count int\) \<\-chan T](<#Skip>)
- [func Skip\[T any\]\(c \<\-chan T, count int\) \<\-chan T](<#Skip>)
- [func SliceToChan\[T any\]\(slice \[\]T\) \<\-chan T](<#SliceToChan>)
- [func Sqrt\[T Number\]\(c \<\-chan T\) \<\-chan T](<#Sqrt>)
- [func Subtract\[T Number\]\(ac, bc \<\-chan T\) \<\-chan T](<#Subtract>)
Expand Down Expand Up @@ -158,6 +159,17 @@ timesTwo := helper.Apply(c, func(n int) int {
})
```

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

```go
func Buffered[T any](c <-chan T, size int) <-chan T
```

Buffered takes a channel of any type and returns a new channel of the same type with a buffer of the specified size. This allows the original channel to continue sending data even if the receiving end is temporarily unavailable.

Example:

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

Expand Down Expand Up @@ -218,7 +230,7 @@ fmt.Println(helper.ChanToSlice(actual)) // [400, 150, 60, -60, -87.5, -50, 200,
## func [CheckEquals](<https://github.com/cinar/indicator/blob/v2/helper/check.go#L13>)

```go
func CheckEquals[T Number](inputs ...<-chan T) error
func CheckEquals[T comparable](inputs ...<-chan T) error
```

CheckEquals determines whether the two channels are equal.
Expand Down Expand Up @@ -456,10 +468,10 @@ fmt.Println(helper.ChanToSlice(twoTimes)) // [2, 4, 6, 8]
## func [Operate](<https://github.com/cinar/indicator/blob/v2/helper/operate.go#L15>)

```go
func Operate[T Number](ac, bc <-chan T, o func(T, T) T) <-chan T
func Operate[T Number, R any](ac, bc <-chan T, o func(T, T) R) <-chan R
```

Operate applies the provided operate function to corresponding values from two input channels of float64 and sends the resulting values to an output channel.
Operate applies the provided operate function to corresponding values from two numeric input channels and sends the resulting values to an output channel.

Example:

Expand All @@ -473,7 +485,7 @@ add := helper.Operate(ac, bc, func(a, b int) int {
## func [Pipe](<https://github.com/cinar/indicator/blob/v2/helper/pipe.go#L16>)

```go
func Pipe[T Number](f <-chan T, t chan<- T)
func Pipe[T any](f <-chan T, t chan<- T)
```

Pipe function takes an input channel and an output channel and copies all elements from the input channel into the output channel.
Expand Down Expand Up @@ -571,7 +583,7 @@ fmt.Println(<- s) // 4
## func [Shift](<https://github.com/cinar/indicator/blob/v2/helper/shift.go#L15>)

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

Shift takes a channel of numbers, shifts them to the right by the specified count, and fills in any missing values with the provided fill value.
Expand Down Expand Up @@ -614,7 +626,7 @@ Since counts the number of periods since the last change of value in a channel o
## func [Skip](<https://github.com/cinar/indicator/blob/v2/helper/skip.go#L15>)

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

Skip skips the specified number of elements from the given channel of float64.
Expand Down
20 changes: 20 additions & 0 deletions helper/buffered.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 helper

// Buffered takes a channel of any type and returns a new channel of the same type with
// a buffer of the specified size. This allows the original channel to continue sending
// data even if the receiving end is temporarily unavailable.
//
// Example:
func Buffered[T any](c <-chan T, size int) <-chan T {
result := make(chan T, size)

go func() {
Pipe(c, result)
}()

return result
}
23 changes: 23 additions & 0 deletions helper/buffered_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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 TestBuffered(t *testing.T) {
c := make(chan int, 1)
b := helper.Buffered(c, 4)

c <- 1
c <- 2
c <- 3
c <- 4

helper.Drain(b)
}
2 changes: 1 addition & 1 deletion helper/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
)

// CheckEquals determines whether the two channels are equal.
func CheckEquals[T Number](inputs ...<-chan T) error {
func CheckEquals[T comparable](inputs ...<-chan T) error {
if len(inputs)%2 != 0 {
return errors.New("not pairs")
}
Expand Down
8 changes: 5 additions & 3 deletions helper/operate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,29 @@
package helper

// Operate applies the provided operate function to corresponding values from two
// input channels of float64 and sends the resulting values to an output channel.
// numeric input channels and sends the resulting values to an output channel.
//
// Example:
//
// add := helper.Operate(ac, bc, func(a, b int) int {
// return a + b
// })
func Operate[T Number](ac, bc <-chan T, o func(T, T) T) <-chan T {
oc := make(chan T)
func Operate[T Number, R any](ac, bc <-chan T, o func(T, T) R) <-chan R {
oc := make(chan R)

go func() {
defer close(oc)

for {
an, ok := <-ac
if !ok {
Drain(bc)
break
}

bn, ok := <-bc
if !ok {
Drain(ac)
break
}

Expand Down
2 changes: 1 addition & 1 deletion helper/pipe.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ package helper
// output := make(chan int)
// helper.Pipe(input, output)
// fmt.println(helper.ChanToSlice(output)) // [2, 4, 6, 8]
func Pipe[T Number](f <-chan T, t chan<- T) {
func Pipe[T any](f <-chan T, t chan<- T) {
go func() {
defer close(t)
for n := range f {
Expand Down
2 changes: 1 addition & 1 deletion helper/shift.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ package helper
// input := helper.SliceToChan([]int{2, 4, 6, 8})
// output := helper.ChanToSlice(input, 4, 0))
// fmt.Println(helper.ChanToSlice(output)) // [0, 0, 0, 0, 2, 4, 6, 8]
func Shift[T Number](c <-chan T, count int, fill T) <-chan T {
func Shift[T any](c <-chan T, count int, fill T) <-chan T {
result := make(chan T, cap(c)+count)

go func() {
Expand Down
2 changes: 1 addition & 1 deletion helper/skip.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ package helper
// c := helper.SliceToChan([]int{2, 4, 6, 8})
// actual := helper.Skip(c, 2)
// fmt.Println(helper.ChanToSlice(actual)) // [6, 8]
func Skip[T Number](c <-chan T, count int) <-chan T {
func Skip[T any](c <-chan T, count int) <-chan T {
result := make(chan T, cap(c))

go func() {
Expand Down
2 changes: 1 addition & 1 deletion pre-commit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ go test -cover ./...
revive -config=revive.toml ./...
staticcheck ./...

for package in asset helper trend;
for package in asset helper strategy trend;
do
echo Package $package
gomarkdoc --repository.default-branch v2 --output $package/README.md ./$package
Expand Down
77 changes: 77 additions & 0 deletions strategy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->

# strategy

```go
import "github.com/cinar/indicator/strategy"
```

## Index

- [type Action](<#Action>)
- [type ApoStrategy](<#ApoStrategy>)
- [func NewApoStrategy\[T helper.Number\]\(\) \*ApoStrategy\[T\]](<#NewApoStrategy>)
- [func \(a \*ApoStrategy\[T\]\) Compute\(c \<\-chan T\) \<\-chan Action](<#ApoStrategy[T].Compute>)


<a name="Action"></a>
## type [Action](<https://github.com/cinar/indicator/blob/v2/strategy/action.go#L9>)

ActionType represents the different action categories that a strategy can recommend.

```go
type Action int
```

<a name="Hold"></a>

```go
const (
// Hold suggests maintaining the current position and not
// taking any actions on the asset.
Hold Action = 0

// Sell suggests disposing of the asset and exiting the current position.
// This recommendation typically indicates that the strategy believes the
// asset's price has reached its peak or is likely to decline.
Sell Action = -1

// Buy suggests acquiring the asset and entering a new position. This
// recommendation usually implies that the strategy believes the
// asset's price is undervalued.
Buy Action = 1
)
```

<a name="ApoStrategy"></a>
## type [ApoStrategy](<https://github.com/cinar/indicator/blob/v2/strategy/apo_strategy.go#L16-L20>)

ApoStrategy represents the configuration parameters for calculating the APO strategy. An APO value crossing above zero suggests a bullish trend, while crossing below zero indicates a bearish trend. Positive APO values signify an upward trend, while negative values signify a downward trend.

```go
type ApoStrategy[T helper.Number] struct {
// Apo represents the configuration parameters for calculating the
// Absolute Price Oscillator (APO).
Apo *trend.Apo[T]
}
```

<a name="NewApoStrategy"></a>
### func [NewApoStrategy](<https://github.com/cinar/indicator/blob/v2/strategy/apo_strategy.go#L23>)

```go
func NewApoStrategy[T helper.Number]() *ApoStrategy[T]
```

NewApo function initializes a new APO strategy instance with the default parameters.

<a name="ApoStrategy[T].Compute"></a>
### func \(\*ApoStrategy\[T\]\) [Compute](<https://github.com/cinar/indicator/blob/v2/strategy/apo_strategy.go#L31>)

```go
func (a *ApoStrategy[T]) Compute(c <-chan T) <-chan Action
```

Compute uses the given values as input and returns a channel of action recommendations.

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

package strategy

Check warning on line 5 in strategy/action.go

View workflow job for this annotation

GitHub Actions / build

should have a package comment

// ActionType represents the different action categories that a

Check warning on line 7 in strategy/action.go

View workflow job for this annotation

GitHub Actions / build

comment on exported type Action should be of the form "Action ..." (with optional leading article)
// strategy can recommend.
type Action int

const (
// Hold suggests maintaining the current position and not
// taking any actions on the asset.
Hold Action = 0

// Sell suggests disposing of the asset and exiting the current position.
// This recommendation typically indicates that the strategy believes the
// asset's price has reached its peak or is likely to decline.
Sell Action = -1

// Buy suggests acquiring the asset and entering a new position. This
// recommendation usually implies that the strategy believes the
// asset's price is undervalued.
Buy Action = 1
)
Loading

0 comments on commit 0c9db68

Please sign in to comment.