mirror of
https://github.com/lukewilson2002/autotrader.git
synced 2025-06-15 08:23:51 +00:00
Hopefully finish Ichimoku()
This commit is contained in:
parent
30b6482fbf
commit
c0de28664e
@ -1,6 +1,9 @@
|
||||
package autotrader
|
||||
|
||||
import "math"
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RSI calculates the Relative Strength Index for a given Series. Typically, the input series is the Close column of a DataFrame. Returns a Series of RSI values of the same length as the input.
|
||||
//
|
||||
@ -22,6 +25,7 @@ func RSI(series *FloatSeries, periods int) *FloatSeries {
|
||||
avgLoss := &FloatSeries{delta.Copy().
|
||||
Map(func(i int, val float64) float64 { return math.Abs(math.Min(val, 0)) }).
|
||||
Rolling(periods).Average()}
|
||||
|
||||
// Calculate the RSI.
|
||||
return avgGain.Map(func(i int, val float64) float64 {
|
||||
loss := avgLoss.Float(i)
|
||||
@ -29,7 +33,7 @@ func RSI(series *FloatSeries, periods int) *FloatSeries {
|
||||
return float64(100)
|
||||
}
|
||||
return float64(100 - 100/(1+val/loss))
|
||||
})
|
||||
}).SetName("RSI")
|
||||
}
|
||||
|
||||
// Ichimoku calculates the Ichimoku Cloud for a given Series. Returns a DataFrame of the same length as the input with float64 values. The series input must contain only float64 values, which are traditionally the close prices.
|
||||
@ -45,29 +49,40 @@ func RSI(series *FloatSeries, periods int) *FloatSeries {
|
||||
// - LeadingA
|
||||
// - LeadingB
|
||||
// - Lagging
|
||||
func Ichimoku(series *FloatSeries, convPeriod, basePeriod, leadingPeriods int) *Frame {
|
||||
func Ichimoku(series *IndexedSeries[UnixTime], convPeriod, basePeriod, leadingPeriods int) *IndexedFrame[UnixTime] {
|
||||
// TODO: make this run concurrently.
|
||||
|
||||
// Calculate the Conversion Line.
|
||||
conv := series.Copy().Rolling(convPeriod).Max().Add(series.Copy().Rolling(convPeriod).Min()).
|
||||
Map(func(i int, val any) any {
|
||||
Map(func(_ UnixTime, _ int, val any) any {
|
||||
return val.(float64) / float64(2)
|
||||
})
|
||||
// Calculate the Base Line.
|
||||
base := series.Copy().Rolling(basePeriod).Max().Add(series.Copy().Rolling(basePeriod).Min()).
|
||||
Map(func(i int, val any) any {
|
||||
Map(func(_ UnixTime, _ int, val any) any {
|
||||
return val.(float64) / float64(2)
|
||||
})
|
||||
|
||||
// Calculate the Leading Span A.
|
||||
leadingA := conv.Copy().Rolling(leadingPeriods).Max().Add(base.Copy().Rolling(leadingPeriods).Max()).
|
||||
Map(func(i int, val any) any {
|
||||
Map(func(_ UnixTime, _ int, val any) any {
|
||||
return val.(float64) / float64(2)
|
||||
})
|
||||
// Calculate the Leading Span B.
|
||||
leadingB := series.Copy().Rolling(leadingPeriods).Max().Add(series.Copy().Rolling(leadingPeriods).Min()).
|
||||
Map(func(i int, val any) any {
|
||||
Map(func(_ UnixTime, _ int, val any) any {
|
||||
return val.(float64) / float64(2)
|
||||
})
|
||||
|
||||
// Calculate the Lagging Span.
|
||||
// lagging := series.Shift(-leadingPeriods)
|
||||
lagging := series.Copy().ShiftIndex(-leadingPeriods, UnixTimeStep(time.Hour))
|
||||
|
||||
// Return a DataFrame of the results.
|
||||
return NewFrame(conv, base, leadingA, leadingB)
|
||||
return NewIndexedFrame(
|
||||
conv.SetName("Conversion"),
|
||||
base.SetName("Base"),
|
||||
leadingA.SetName("LeadingA"),
|
||||
leadingB.SetName("LeadingB"),
|
||||
lagging.SetName("Lagging"),
|
||||
)
|
||||
}
|
||||
|
10
series.go
10
series.go
@ -435,14 +435,14 @@ func NewRollingSeries(series *Series, period int) *RollingSeries {
|
||||
return &RollingSeries{series, period}
|
||||
}
|
||||
|
||||
// Period returns a slice of 'any' values with a length up to the period of the RollingSeries. The last item in the slice is the item at i. If i is out of bounds, nil is returned.
|
||||
func (s *RollingSeries) Period(i int) []any {
|
||||
// Period returns a slice of 'any' values with a length up to the period of the RollingSeries. The last item in the slice is the item at row. If row is out of bounds, nil is returned.
|
||||
func (s *RollingSeries) Period(row int) []any {
|
||||
items := make([]any, 0, s.period)
|
||||
i = EasyIndex(i, s.series.Len())
|
||||
if i < 0 || i >= s.series.Len() {
|
||||
row = EasyIndex(row, s.series.Len())
|
||||
if row < 0 || row >= s.series.Len() {
|
||||
return items
|
||||
}
|
||||
for j := i; j > i-s.period && j >= 0; j-- {
|
||||
for j := row; j > row-s.period && j >= 0; j-- {
|
||||
items = slices.Insert(items, 0, s.series.Value(j))
|
||||
}
|
||||
return items
|
||||
|
@ -89,6 +89,18 @@ func (s *IndexedSeries[I]) Filter(f func(i int, val any) bool) *IndexedSeries[I]
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *IndexedSeries[I]) Float(i int) float64 {
|
||||
return s.series.Float(i)
|
||||
}
|
||||
|
||||
func (s *IndexedSeries[I]) FloatIndex(index I) float64 {
|
||||
row := s.Row(index)
|
||||
if row < 0 {
|
||||
return 0.0
|
||||
}
|
||||
return s.series.Float(row)
|
||||
}
|
||||
|
||||
func (s *IndexedSeries[I]) ForEach(f func(i int, val any)) *IndexedSeries[I] {
|
||||
_ = s.series.ForEach(f)
|
||||
return s
|
||||
@ -240,6 +252,10 @@ func (s *IndexedSeries[I]) ReverseIndexes() *IndexedSeries[I] {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *IndexedSeries[I]) Rolling(period int) *IndexedRollingSeries[I] {
|
||||
return NewIndexedRollingSeries(s, period)
|
||||
}
|
||||
|
||||
func (s *IndexedSeries[I]) SetName(name string) *IndexedSeries[I] {
|
||||
_ = s.series.SetName(name)
|
||||
return s
|
||||
@ -314,3 +330,51 @@ func (s *IndexedSeries[I]) Values() []any {
|
||||
func (s *IndexedSeries[I]) ValueRange(start, count int) []any {
|
||||
return s.series.ValueRange(start, count)
|
||||
}
|
||||
|
||||
type IndexedRollingSeries[I comparable] struct {
|
||||
rolling *RollingSeries
|
||||
series *IndexedSeries[I]
|
||||
}
|
||||
|
||||
func NewIndexedRollingSeries[I comparable](series *IndexedSeries[I], period int) *IndexedRollingSeries[I] {
|
||||
return &IndexedRollingSeries[I]{NewRollingSeries(series.series, period), series}
|
||||
}
|
||||
|
||||
func (s *IndexedRollingSeries[I]) Period(row int) []any {
|
||||
return s.rolling.Period(row)
|
||||
}
|
||||
|
||||
func (s *IndexedRollingSeries[I]) Max() *IndexedSeries[I] {
|
||||
_ = s.rolling.Max() // Mutate the underlying series.
|
||||
return s.series
|
||||
}
|
||||
|
||||
func (s *IndexedRollingSeries[I]) Min() *IndexedSeries[I] {
|
||||
_ = s.rolling.Min() // Mutate the underlying series.
|
||||
return s.series
|
||||
}
|
||||
|
||||
func (s *IndexedRollingSeries[I]) Average() *IndexedSeries[I] {
|
||||
_ = s.rolling.Average() // Mutate the underlying series.
|
||||
return s.series
|
||||
}
|
||||
|
||||
func (s *IndexedRollingSeries[I]) Mean() *IndexedSeries[I] {
|
||||
_ = s.rolling.Mean() // Mutate the underlying series.
|
||||
return s.series
|
||||
}
|
||||
|
||||
func (s *IndexedRollingSeries[I]) Median() *IndexedSeries[I] {
|
||||
_ = s.rolling.Median() // Mutate the underlying series.
|
||||
return s.series
|
||||
}
|
||||
|
||||
func (s *IndexedRollingSeries[I]) EMA() *IndexedSeries[I] {
|
||||
_ = s.rolling.EMA() // Mutate the underlying series.
|
||||
return s.series
|
||||
}
|
||||
|
||||
func (s *IndexedRollingSeries[I]) StdDev() *IndexedSeries[I] {
|
||||
_ = s.rolling.StdDev() // Mutate the underlying series.
|
||||
return s.series
|
||||
}
|
||||
|
10
utils.go
10
utils.go
@ -14,10 +14,18 @@ const float64Tolerance = float64(1e-6)
|
||||
var ErrNotASignedNumber = errors.New("not a signed number")
|
||||
|
||||
// Crossover returns true if the latest a value crosses above the latest b value, but only if it just happened. For example, if a series is [1, 2, 3, 4, 5] and b series is [1, 2, 3, 4, 3], then Crossover(a, b) returns false because the latest a value is 5 and the latest b value is 3. However, if a series is [1, 2, 3, 4, 5] and b series is [1, 2, 3, 4, 6], then Crossover(a, b) returns true because the latest a value is 5 and the latest b value is 6
|
||||
func Crossover(a, b Series) bool {
|
||||
func Crossover(a, b *Series) bool {
|
||||
return a.Float(-1) > b.Float(-1) && a.Float(-2) <= b.Float(-2)
|
||||
}
|
||||
|
||||
func CrossoverIndex[I comparable](index I, a, b *IndexedSeries[I]) bool {
|
||||
aRow, bRow := a.Row(index), b.Row(index)
|
||||
if aRow < 1 || bRow < 1 {
|
||||
return false
|
||||
}
|
||||
return a.Float(aRow) > b.Float(bRow) && a.Float(aRow-1) <= b.Float(bRow-1)
|
||||
}
|
||||
|
||||
// EasyIndex returns an index to the `n` -length object that allows for negative indexing. For example, EasyIndex(-1, 5) returns 4. This is similar to Python's negative indexing. The return value may be less than zero if (-i) > n.
|
||||
func EasyIndex(i, n int) int {
|
||||
if i < 0 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user