feature: add mean, abs, sum, toArray, and dot operations on series. implement Float64Slice as series

This commit is contained in:
zenix 2022-04-07 12:17:51 +09:00
parent d0c3390f84
commit 339d36d61b
4 changed files with 107 additions and 109 deletions

View File

@ -41,35 +41,20 @@ type BOLL struct {
type BandType int
const (
_SMA BandType = iota
_StdDev
_UpBand
_DownBand
)
func (inc *BOLL) GetUpBand() types.Series {
return &BollSeries{
inc, _UpBand,
}
return inc.UpBand
}
func (inc *BOLL) GetDownBand() types.Series {
return &BollSeries{
inc, _DownBand,
}
return inc.DownBand
}
func (inc *BOLL) GetSMA() types.Series {
return &BollSeries{
inc, _SMA,
}
return inc.SMA
}
func (inc *BOLL) GetStdDev() types.Series {
return &BollSeries{
inc, _StdDev,
}
return inc.StdDev
}
func (inc *BOLL) LastUpBand() float64 {
@ -163,67 +148,3 @@ func (inc *BOLL) handleKLineWindowUpdate(interval types.Interval, window types.K
func (inc *BOLL) Bind(updater KLineWindowUpdater) {
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
}
type BollSeries struct {
*BOLL
bandType BandType
}
func (b *BollSeries) Last() float64 {
switch b.bandType {
case _SMA:
return b.LastSMA()
case _StdDev:
return b.LastStdDev()
case _UpBand:
return b.LastUpBand()
case _DownBand:
return b.LastDownBand()
default:
panic("bandType wrong")
}
}
func (b *BollSeries) Index(i int) float64 {
switch b.bandType {
case _SMA:
if len(b.SMA) <= i {
return 0
}
return b.SMA[len(b.SMA)-i-1]
case _StdDev:
if len(b.StdDev) <= i {
return 0
}
return b.StdDev[len(b.StdDev)-i-1]
case _UpBand:
if len(b.UpBand) <= i {
return 0
}
return b.UpBand[len(b.UpBand)-i-1]
case _DownBand:
if len(b.DownBand) <= i {
return 0
}
return b.DownBand[len(b.DownBand)-i-1]
default:
panic("bandType wrong")
}
}
func (b *BollSeries) Length() int {
switch b.bandType {
case _SMA:
return len(b.SMA)
case _StdDev:
return len(b.StdDev)
case _UpBand:
return len(b.UpBand)
case _DownBand:
return len(b.DownBand)
default:
panic("bandType wrong")
}
}
var _ types.Series = &BollSeries{}

View File

@ -117,3 +117,23 @@ func (s Float64Slice) ElementwiseProduct(other Float64Slice) Float64Slice {
func (s Float64Slice) Dot(other Float64Slice) float64 {
return s.ElementwiseProduct(other).Sum()
}
func (a Float64Slice) Last() float64 {
if len(a) > 0 {
return a[len(a)-1]
}
return 0.0
}
func (a Float64Slice) Index(i int) float64 {
if len(a)-i < 0 || i < 0 {
return 0.0
}
return a[len(a)-i-1]
}
func (a Float64Slice) Length() int {
return len(a)
}
var _ Series = Float64Slice([]float64{})

View File

@ -29,6 +29,60 @@ type BoolSeries interface {
Length() int
}
// Calculate sum of the series
// if limit is given, will only sum first limit numbers (a.Index[0..limit])
// otherwise will sum all elements
func Sum(a Series, limit ...int) (sum float64) {
l := -1
if len(limit) > 0 {
l = limit[0]
}
if l < a.Length() {
l = a.Length()
}
for i := 0; i < l; i++ {
sum += a.Index(i)
}
return sum
}
// Calculate the average value of the series
// if limit is given, will only calculate the average of first limit numbers (a.Index[0..limit])
// otherwise will operate on all elements
func Mean(a Series, limit ...int) (mean float64) {
l := -1
if len(limit) > 0 {
l = limit[0]
}
if l < a.Length() {
l = a.Length()
}
return Sum(a, l) / float64(l)
}
type AbsResult struct {
a Series
}
func (a *AbsResult) Last() float64 {
return math.Abs(a.a.Last())
}
func (a *AbsResult) Index(i int) float64 {
return math.Abs(a.a.Index(i))
}
func (a *AbsResult) Length() int {
return a.a.Length()
}
// Return series that having all the elements positive
func Abs(a Series) Series {
return &AbsResult{a}
}
var _ Series = &AbsResult{}
// This will make prediction using Linear Regression to get the next cross point
// Return (offset from latest, crossed value, could cross)
// offset from latest should always be positive
@ -164,28 +218,6 @@ func (a NumberSeries) Length() int {
var _ Series = NumberSeries(0)
type Float64ArrSeries []float64
func (a Float64ArrSeries) Last() float64 {
if len(a) > 0 {
return a[len(a)-1]
}
return 0.0
}
func (a Float64ArrSeries) Index(i int) float64 {
if len(a)-i < 0 || i < 0 {
return 0.0
}
return a[len(a)-i-1]
}
func (a Float64ArrSeries) Length() int {
return len(a)
}
var _ Series = Float64ArrSeries([]float64{})
type AddSeriesResult struct {
a Series
b Series
@ -366,3 +398,28 @@ func (a *MulSeriesResult) Length() int {
}
var _ Series = &MulSeriesResult{}
// Calculate (a dot b).
// if limit is given, will only calculate the first limit numbers (a.Index[0..limit])
// otherwise will operate on all elements
func Dot(a interface{}, b interface{}, limit ...int) float64 {
return Sum(Mul(a, b), limit...)
}
// Extract elements from the Series to a float64 array, following the order of Index(0..limit)
// if limit is given, will only take the first limit numbers (a.Index[0..limit])
// otherwise will operate on all elements
func ToArray(a Series, limit ...int) (result []float64) {
l := -1
if len(limit) > 0 {
l = limit[0]
}
if l < a.Length() {
l = a.Length()
}
result = make([]float64, l, l)
for i := 0; i < l; i++ {
result[i] = a.Index(i)
}
return
}

View File

@ -14,10 +14,10 @@ func TestFloat(t *testing.T) {
func TestNextCross(t *testing.T) {
var a Series = NumberSeries(1.2)
var b Series = Float64ArrSeries{100., 80., 60.}
// index 2 1 0
// predicted 40 20 0
// offset 1 2 3
var b Series = Float64Slice{100., 80., 60.}
// index 2 1 0
// predicted 40 20 0
// offset 1 2 3
index, value, ok := NextCross(a, b, 3)
assert.True(t, ok)