2022-08-08 04:43:38 +00:00
|
|
|
package supertrend
|
|
|
|
|
|
|
|
import (
|
2022-08-25 09:31:42 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/c9s/bbgo/pkg/datatype/floats"
|
2022-08-08 04:43:38 +00:00
|
|
|
"github.com/c9s/bbgo/pkg/indicator"
|
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
// LinReg is Linear Regression baseline
|
|
|
|
type LinReg struct {
|
|
|
|
types.SeriesBase
|
|
|
|
types.IntervalWindow
|
|
|
|
// Values are the slopes of linear regression baseline
|
2022-10-07 05:48:16 +00:00
|
|
|
Values floats.Slice
|
|
|
|
klines types.KLineWindow
|
2022-08-08 04:43:38 +00:00
|
|
|
EndTime time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// Last slope of linear regression baseline
|
2023-05-31 23:46:50 +00:00
|
|
|
func (lr *LinReg) Last(i int) float64 {
|
|
|
|
return lr.Values.Last(i)
|
2022-08-08 04:43:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Index returns the slope of specified index
|
|
|
|
func (lr *LinReg) Index(i int) float64 {
|
2023-05-31 23:46:50 +00:00
|
|
|
return lr.Last(i)
|
2022-08-08 04:43:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Length of the slope values
|
|
|
|
func (lr *LinReg) Length() int {
|
|
|
|
return lr.Values.Length()
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ types.SeriesExtend = &LinReg{}
|
|
|
|
|
|
|
|
// Update Linear Regression baseline slope
|
|
|
|
func (lr *LinReg) Update(kline types.KLine) {
|
|
|
|
lr.klines.Add(kline)
|
|
|
|
lr.klines.Truncate(lr.Window)
|
|
|
|
if len(lr.klines) < lr.Window {
|
|
|
|
lr.Values.Push(0)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var sumX, sumY, sumXSqr, sumXY float64 = 0, 0, 0, 0
|
|
|
|
end := len(lr.klines) - 1 // The last kline
|
|
|
|
for i := end; i >= end-lr.Window+1; i-- {
|
|
|
|
val := lr.klines[i].GetClose().Float64()
|
|
|
|
per := float64(end - i + 1)
|
|
|
|
sumX += per
|
|
|
|
sumY += val
|
|
|
|
sumXSqr += per * per
|
|
|
|
sumXY += val * per
|
|
|
|
}
|
|
|
|
length := float64(lr.Window)
|
|
|
|
slope := (length*sumXY - sumX*sumY) / (length*sumXSqr - sumX*sumX)
|
|
|
|
average := sumY / length
|
|
|
|
endPrice := average - slope*sumX/length + slope
|
|
|
|
startPrice := endPrice + slope*(length-1)
|
2022-10-07 05:48:16 +00:00
|
|
|
lr.Values.Push((endPrice - startPrice) / (length - 1))
|
2022-08-08 04:43:38 +00:00
|
|
|
|
2023-05-31 11:35:44 +00:00
|
|
|
log.Debugf("linear regression baseline slope: %f", lr.Last(0))
|
2022-08-08 04:43:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (lr *LinReg) BindK(target indicator.KLineClosedEmitter, symbol string, interval types.Interval) {
|
|
|
|
target.OnKLineClosed(types.KLineWith(symbol, interval, lr.PushK))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lr *LinReg) PushK(k types.KLine) {
|
|
|
|
var zeroTime = time.Time{}
|
|
|
|
if lr.EndTime != zeroTime && k.EndTime.Before(lr.EndTime) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
lr.Update(k)
|
|
|
|
lr.EndTime = k.EndTime.Time()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lr *LinReg) LoadK(allKLines []types.KLine) {
|
|
|
|
for _, k := range allKLines {
|
|
|
|
lr.PushK(k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSignal get linear regression signal
|
|
|
|
func (lr *LinReg) GetSignal() types.Direction {
|
|
|
|
var lrSignal types.Direction = types.DirectionNone
|
|
|
|
|
|
|
|
switch {
|
2023-05-31 11:35:44 +00:00
|
|
|
case lr.Last(0) > 0:
|
2022-08-08 04:43:38 +00:00
|
|
|
lrSignal = types.DirectionUp
|
2023-05-31 11:35:44 +00:00
|
|
|
case lr.Last(0) < 0:
|
2022-08-08 04:43:38 +00:00
|
|
|
lrSignal = types.DirectionDown
|
|
|
|
}
|
|
|
|
|
|
|
|
return lrSignal
|
|
|
|
}
|