Skip to content

Commit b86e1a9

Browse files
authored
Merge pull request #1181 from c9s/feature/v2indicator
FEATURE: [indicator] add new ATR, RMA indicators with the new API design
2 parents 67fe277 + 2a074ba commit b86e1a9

25 files changed

+676
-141
lines changed

pkg/datatype/floats/funcs_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@ func TestHigher(t *testing.T) {
1515
out := Higher([]float64{10.0, 11.0, 12.0, 13.0, 15.0}, 12.0)
1616
assert.Equal(t, []float64{13.0, 15.0}, out)
1717
}
18+
19+
func TestLSM(t *testing.T) {
20+
slice := Slice{1., 2., 3., 4.}
21+
slope := LSM(slice)
22+
assert.Equal(t, 1.0, slope)
23+
}

pkg/datatype/floats/pivot.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package floats
22

33
func (s Slice) Pivot(left, right int, f func(a, pivot float64) bool) (float64, bool) {
4-
return CalculatePivot(s, left, right, f)
4+
return FindPivot(s, left, right, f)
55
}
66

7-
func CalculatePivot(values Slice, left, right int, f func(a, pivot float64) bool) (float64, bool) {
7+
func FindPivot(values Slice, left, right int, f func(a, pivot float64) bool) (float64, bool) {
88
length := len(values)
99

1010
if right == 0 {

pkg/datatype/floats/slice.go

+49-4
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ func (s Slice) Add(b Slice) (c Slice) {
7373
}
7474

7575
func (s Slice) Sum() (sum float64) {
76-
return floats.Sum(s)
76+
for _, v := range s {
77+
sum += v
78+
}
79+
return sum
7780
}
7881

7982
func (s Slice) Mean() (mean float64) {
@@ -97,6 +100,18 @@ func (s Slice) Tail(size int) Slice {
97100
return win
98101
}
99102

103+
func (s Slice) Average() float64 {
104+
if len(s) == 0 {
105+
return 0.0
106+
}
107+
108+
total := 0.0
109+
for _, value := range s {
110+
total += value
111+
}
112+
return total / float64(len(s))
113+
}
114+
100115
func (s Slice) Diff() (values Slice) {
101116
for i, v := range s {
102117
if i == 0 {
@@ -171,19 +186,49 @@ func (s Slice) Addr() *Slice {
171186
func (s Slice) Last() float64 {
172187
length := len(s)
173188
if length > 0 {
174-
return (s)[length-1]
189+
return s[length-1]
175190
}
176191
return 0.0
177192
}
178193

194+
// Index fetches the element from the end of the slice
195+
// WARNING: it does not start from 0!!!
179196
func (s Slice) Index(i int) float64 {
180197
length := len(s)
181-
if length-i <= 0 || i < 0 {
198+
if i < 0 || length-1-i < 0 {
182199
return 0.0
183200
}
184-
return (s)[length-i-1]
201+
return s[length-1-i]
185202
}
186203

187204
func (s Slice) Length() int {
188205
return len(s)
189206
}
207+
208+
func (s Slice) LSM() float64 {
209+
return LSM(s)
210+
}
211+
212+
// LSM is the least squares method for linear regression
213+
func LSM(values Slice) float64 {
214+
var sumX, sumY, sumXSqr, sumXY = .0, .0, .0, .0
215+
216+
end := len(values) - 1
217+
for i := end; i >= 0; i-- {
218+
val := values[i]
219+
per := float64(end - i + 1)
220+
sumX += per
221+
sumY += val
222+
sumXSqr += per * per
223+
sumXY += val * per
224+
}
225+
226+
length := float64(len(values))
227+
slope := (length*sumXY - sumX*sumY) / (length*sumXSqr - sumX*sumX)
228+
229+
average := sumY / length
230+
tail := average - slope*sumX/length + slope
231+
head := tail + slope*(length-1)
232+
slope2 := (tail - head) / (length - 1)
233+
return slope2
234+
}

pkg/indicator/ewmastream_callbacks.go

-5
This file was deleted.

pkg/indicator/float64updater.go

+34
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,40 @@
11
package indicator
22

3+
import (
4+
"github.com/c9s/bbgo/pkg/datatype/floats"
5+
"github.com/c9s/bbgo/pkg/types"
6+
)
7+
38
//go:generate callbackgen -type Float64Updater
49
type Float64Updater struct {
510
updateCallbacks []func(v float64)
611
}
12+
13+
type Float64Series struct {
14+
types.SeriesBase
15+
Float64Updater
16+
slice floats.Slice
17+
}
18+
19+
func NewFloat64Series(v ...float64) Float64Series {
20+
s := Float64Series{}
21+
s.slice = v
22+
s.SeriesBase.Series = s.slice
23+
return s
24+
}
25+
26+
func (f *Float64Series) Last() float64 {
27+
return f.slice.Last()
28+
}
29+
30+
func (f *Float64Series) Index(i int) float64 {
31+
length := len(f.slice)
32+
if length == 0 || length-i-1 < 0 {
33+
return 0
34+
}
35+
return f.slice[length-i-1]
36+
}
37+
38+
func (f *Float64Series) Length() int {
39+
return len(f.slice)
40+
}

pkg/indicator/float64updater_callbacks.go

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
File renamed without changes.

pkg/indicator/macd2.go

-114
This file was deleted.
File renamed without changes.

pkg/indicator/pivotlow.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ func (inc *PivotLow) PushK(k types.KLine) {
6464
}
6565

6666
func calculatePivotHigh(highs floats.Slice, left, right int) (float64, bool) {
67-
return floats.CalculatePivot(highs, left, right, func(a, pivot float64) bool {
67+
return floats.FindPivot(highs, left, right, func(a, pivot float64) bool {
6868
return a < pivot
6969
})
7070
}
7171

7272
func calculatePivotLow(lows floats.Slice, left, right int) (float64, bool) {
73-
return floats.CalculatePivot(lows, left, right, func(a, pivot float64) bool {
73+
return floats.FindPivot(lows, left, right, func(a, pivot float64) bool {
7474
return a > pivot
7575
})
7676
}

pkg/indicator/price.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package indicator
22

33
import (
4-
"github.com/c9s/bbgo/pkg/datatype/floats"
54
"github.com/c9s/bbgo/pkg/types"
65
)
76

@@ -10,10 +9,8 @@ type KLineSubscription interface {
109
}
1110

1211
type PriceStream struct {
13-
types.SeriesBase
14-
Float64Updater
12+
Float64Series
1513

16-
slice floats.Slice
1714
mapper KLineValueMapper
1815
}
1916

@@ -26,12 +23,16 @@ func Price(source KLineSubscription, mapper KLineValueMapper) *PriceStream {
2623

2724
source.AddSubscriber(func(k types.KLine) {
2825
v := s.mapper(k)
29-
s.slice.Push(v)
30-
s.EmitUpdate(v)
26+
s.PushAndEmit(v)
3127
})
3228
return s
3329
}
3430

31+
func (s *PriceStream) PushAndEmit(v float64) {
32+
s.slice.Push(v)
33+
s.EmitUpdate(v)
34+
}
35+
3536
func ClosePrices(source KLineSubscription) *PriceStream {
3637
return Price(source, KLineClosePriceMapper)
3738
}

pkg/indicator/subtract.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package indicator
2+
3+
import (
4+
"github.com/c9s/bbgo/pkg/datatype/floats"
5+
)
6+
7+
// SubtractStream subscribes 2 upstream data, and then subtract these 2 values
8+
type SubtractStream struct {
9+
Float64Series
10+
11+
a, b floats.Slice
12+
i int
13+
}
14+
15+
// Subtract creates the SubtractStream object
16+
// subtract := Subtract(longEWMA, shortEWMA)
17+
func Subtract(a, b Float64Source) *SubtractStream {
18+
s := &SubtractStream{
19+
Float64Series: NewFloat64Series(),
20+
}
21+
22+
a.OnUpdate(func(v float64) {
23+
s.a.Push(v)
24+
s.calculate()
25+
})
26+
b.OnUpdate(func(v float64) {
27+
s.b.Push(v)
28+
s.calculate()
29+
})
30+
return s
31+
}
32+
33+
func (s *SubtractStream) calculate() {
34+
if s.a.Length() != s.b.Length() {
35+
return
36+
}
37+
38+
if s.a.Length() > s.slice.Length() {
39+
var numNewElems = s.a.Length() - s.slice.Length()
40+
var tailA = s.a.Tail(numNewElems)
41+
var tailB = s.b.Tail(numNewElems)
42+
var tailC = tailA.Sub(tailB)
43+
for _, f := range tailC {
44+
s.slice.Push(f)
45+
s.EmitUpdate(f)
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)