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
|
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.
|
// 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().
|
avgLoss := &FloatSeries{delta.Copy().
|
||||||
Map(func(i int, val float64) float64 { return math.Abs(math.Min(val, 0)) }).
|
Map(func(i int, val float64) float64 { return math.Abs(math.Min(val, 0)) }).
|
||||||
Rolling(periods).Average()}
|
Rolling(periods).Average()}
|
||||||
|
|
||||||
// Calculate the RSI.
|
// Calculate the RSI.
|
||||||
return avgGain.Map(func(i int, val float64) float64 {
|
return avgGain.Map(func(i int, val float64) float64 {
|
||||||
loss := avgLoss.Float(i)
|
loss := avgLoss.Float(i)
|
||||||
@ -29,7 +33,7 @@ func RSI(series *FloatSeries, periods int) *FloatSeries {
|
|||||||
return float64(100)
|
return float64(100)
|
||||||
}
|
}
|
||||||
return float64(100 - 100/(1+val/loss))
|
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.
|
// 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
|
// - LeadingA
|
||||||
// - LeadingB
|
// - LeadingB
|
||||||
// - Lagging
|
// - 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.
|
// Calculate the Conversion Line.
|
||||||
conv := series.Copy().Rolling(convPeriod).Max().Add(series.Copy().Rolling(convPeriod).Min()).
|
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)
|
return val.(float64) / float64(2)
|
||||||
})
|
})
|
||||||
// Calculate the Base Line.
|
// Calculate the Base Line.
|
||||||
base := series.Copy().Rolling(basePeriod).Max().Add(series.Copy().Rolling(basePeriod).Min()).
|
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)
|
return val.(float64) / float64(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Calculate the Leading Span A.
|
// Calculate the Leading Span A.
|
||||||
leadingA := conv.Copy().Rolling(leadingPeriods).Max().Add(base.Copy().Rolling(leadingPeriods).Max()).
|
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)
|
return val.(float64) / float64(2)
|
||||||
})
|
})
|
||||||
// Calculate the Leading Span B.
|
// Calculate the Leading Span B.
|
||||||
leadingB := series.Copy().Rolling(leadingPeriods).Max().Add(series.Copy().Rolling(leadingPeriods).Min()).
|
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)
|
return val.(float64) / float64(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Calculate the Lagging Span.
|
// Calculate the Lagging Span.
|
||||||
// lagging := series.Shift(-leadingPeriods)
|
lagging := series.Copy().ShiftIndex(-leadingPeriods, UnixTimeStep(time.Hour))
|
||||||
|
|
||||||
// Return a DataFrame of the results.
|
// 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}
|
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.
|
// 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(i int) []any {
|
func (s *RollingSeries) Period(row int) []any {
|
||||||
items := make([]any, 0, s.period)
|
items := make([]any, 0, s.period)
|
||||||
i = EasyIndex(i, s.series.Len())
|
row = EasyIndex(row, s.series.Len())
|
||||||
if i < 0 || i >= s.series.Len() {
|
if row < 0 || row >= s.series.Len() {
|
||||||
return items
|
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))
|
items = slices.Insert(items, 0, s.series.Value(j))
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
|
@ -89,6 +89,18 @@ func (s *IndexedSeries[I]) Filter(f func(i int, val any) bool) *IndexedSeries[I]
|
|||||||
return s
|
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] {
|
func (s *IndexedSeries[I]) ForEach(f func(i int, val any)) *IndexedSeries[I] {
|
||||||
_ = s.series.ForEach(f)
|
_ = s.series.ForEach(f)
|
||||||
return s
|
return s
|
||||||
@ -240,6 +252,10 @@ func (s *IndexedSeries[I]) ReverseIndexes() *IndexedSeries[I] {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *IndexedSeries[I]) Rolling(period int) *IndexedRollingSeries[I] {
|
||||||
|
return NewIndexedRollingSeries(s, period)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *IndexedSeries[I]) SetName(name string) *IndexedSeries[I] {
|
func (s *IndexedSeries[I]) SetName(name string) *IndexedSeries[I] {
|
||||||
_ = s.series.SetName(name)
|
_ = s.series.SetName(name)
|
||||||
return s
|
return s
|
||||||
@ -314,3 +330,51 @@ func (s *IndexedSeries[I]) Values() []any {
|
|||||||
func (s *IndexedSeries[I]) ValueRange(start, count int) []any {
|
func (s *IndexedSeries[I]) ValueRange(start, count int) []any {
|
||||||
return s.series.ValueRange(start, count)
|
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")
|
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
|
// 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)
|
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.
|
// 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 {
|
func EasyIndex(i, n int) int {
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user