Merge pull request #540 from narumiruna/indicator/update

indicator: make parameters of update method consistent
This commit is contained in:
Yo-An Lin 2022-04-20 11:53:06 +08:00 committed by GitHub
commit 46015324e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 57 additions and 87 deletions

View File

@ -22,12 +22,7 @@ type AD struct {
UpdateCallbacks []func(value float64)
}
func (inc *AD) Update(kLine types.KLine) {
cloze := kLine.Close.Float64()
high := kLine.High.Float64()
low := kLine.Low.Float64()
volume := kLine.Volume.Float64()
func (inc *AD) Update(high, low, cloze, volume float64) {
var moneyFlowVolume float64
if high == low {
moneyFlowVolume = 0
@ -65,7 +60,7 @@ func (inc *AD) calculateAndUpdate(kLines []types.KLine) {
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
continue
}
inc.Update(k)
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64(), k.Volume.Float64())
}
inc.EmitUpdate(inc.Last())

View File

@ -11,25 +11,26 @@ import (
type ATR struct {
types.IntervalWindow
Values types.Float64Slice
TrueRanges types.Float64Slice
PercentageVolatility types.Float64Slice
PriviousClose float64
PriviousClose float64
RMA *RMA
EndTime time.Time
UpdateCallbacks []func(value float64)
}
func (inc *ATR) Update(kLine types.KLine) {
func (inc *ATR) Update(high, low, cloze float64) {
if inc.Window <= 0 {
panic("window must be greater than 0")
}
cloze := kLine.Close.Float64()
high := kLine.High.Float64()
low := kLine.Low.Float64()
if len(inc.Values) == 0 {
inc.RMA = &RMA{IntervalWindow: types.IntervalWindow{Window: inc.Window}}
}
if inc.PriviousClose == 0 {
inc.PriviousClose = kLine.Close.Float64()
inc.PriviousClose = cloze
return
}
@ -39,24 +40,12 @@ func (inc *ATR) Update(kLine types.KLine) {
math.Abs(high - inc.PriviousClose),
math.Abs(low - inc.PriviousClose),
}.Max()
inc.TrueRanges.Push(trueRange)
inc.PriviousClose = cloze
// apply rolling moving average
if len(inc.TrueRanges) < inc.Window {
return
}
if len(inc.TrueRanges) == inc.Window {
atr := inc.TrueRanges.Mean()
inc.Values.Push(atr)
inc.PercentageVolatility.Push(atr / cloze)
return
}
lambda := 1 / float64(inc.Window)
atr := inc.Values.Last()*(1-lambda) + inc.TrueRanges.Last()*lambda
inc.RMA.Update(trueRange)
atr := inc.RMA.Last()
inc.Values.Push(atr)
inc.PercentageVolatility.Push(atr / cloze)
}
@ -87,7 +76,7 @@ func (inc *ATR) calculateAndUpdate(kLines []types.KLine) {
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
continue
}
inc.Update(k)
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
}
inc.EmitUpdate(inc.Last())

View File

@ -29,25 +29,16 @@ type MACD struct {
UpdateCallbacks []func(value float64)
}
func (inc *MACD) calculateMACD(kLines []types.KLine, priceF KLinePriceMapper) float64 {
for _, kline := range kLines {
inc.Update(kline, priceF)
}
return inc.Values[len(inc.Values)-1]
}
func (inc *MACD) Update(kLine types.KLine, priceF KLinePriceMapper) {
func (inc *MACD) Update(x float64) {
if len(inc.Values) == 0 {
inc.FastEWMA = EWMA{IntervalWindow: types.IntervalWindow{Window: inc.ShortPeriod}}
inc.SlowEWMA = EWMA{IntervalWindow: types.IntervalWindow{Window: inc.LongPeriod}}
inc.SignalLine = EWMA{IntervalWindow: types.IntervalWindow{Window: inc.Window}}
}
price := priceF(kLine)
// update fast and slow ema
inc.FastEWMA.Update(price)
inc.SlowEWMA.Update(price)
inc.FastEWMA.Update(x)
inc.SlowEWMA.Update(x)
// update macd
macd := inc.FastEWMA.Last() - inc.SlowEWMA.Last()
@ -60,18 +51,23 @@ func (inc *MACD) Update(kLine types.KLine, priceF KLinePriceMapper) {
inc.Histogram.Push(macd - inc.SignalLine.Last())
}
func (inc *MACD) calculateMACD(kLines []types.KLine, priceF KLinePriceMapper) float64 {
for _, kline := range kLines {
inc.Update(kline.Close.Float64())
}
return inc.Values[len(inc.Values)-1]
}
func (inc *MACD) calculateAndUpdate(kLines []types.KLine) {
if len(kLines) == 0 {
return
}
var priceF = KLineClosePriceMapper
for _, k := range kLines {
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
continue
}
inc.Update(k, priceF)
inc.Update(k.Close.Float64())
}
inc.EmitUpdate(inc.Values[len(inc.Values)-1])

View File

@ -22,10 +22,7 @@ type OBV struct {
UpdateCallbacks []func(value float64)
}
func (inc *OBV) Update(kLine types.KLine, priceF KLinePriceMapper) {
price := priceF(kLine)
volume := kLine.Volume.Float64()
func (inc *OBV) Update(price, volume float64) {
if len(inc.Values) == 0 {
inc.PrePrice = price
inc.Values.Push(volume)
@ -47,17 +44,16 @@ func (inc *OBV) Last() float64 {
}
func (inc *OBV) calculateAndUpdate(kLines []types.KLine) {
var priceF = KLineClosePriceMapper
for _, k := range kLines {
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
continue
}
inc.Update(k, priceF)
inc.Update(k.Close.Float64(), k.Volume.Float64())
}
inc.EmitUpdate(inc.Last())
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
}
func (inc *OBV) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
if inc.Interval != interval {
return

View File

@ -24,8 +24,7 @@ type RSI struct {
UpdateCallbacks []func(value float64)
}
func (inc *RSI) Update(kline types.KLine, priceF KLinePriceMapper) {
price := priceF(kline)
func (inc *RSI) Update(price float64) {
inc.Prices.Push(price)
if len(inc.Prices) < inc.Window+1 {
@ -78,13 +77,11 @@ func (inc *RSI) Length() int {
var _ types.Series = &RSI{}
func (inc *RSI) calculateAndUpdate(kLines []types.KLine) {
var priceF = KLineClosePriceMapper
for _, k := range kLines {
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
continue
}
inc.Update(k, priceF)
inc.Update(k.Close.Float64())
}
inc.EmitUpdate(inc.Last())

View File

@ -20,21 +20,21 @@ type STOCH struct {
K types.Float64Slice
D types.Float64Slice
KLineWindow types.KLineWindow
HighValues types.Float64Slice
LowValues types.Float64Slice
EndTime time.Time
UpdateCallbacks []func(k float64, d float64)
}
func (inc *STOCH) update(kLine types.KLine) {
inc.KLineWindow.Add(kLine)
inc.KLineWindow.Truncate(inc.Window)
func (inc *STOCH) Update(high, low, cloze float64) {
inc.HighValues.Push(high)
inc.LowValues.Push(low)
lowest := inc.KLineWindow.GetLow().Float64()
highest := inc.KLineWindow.GetHigh().Float64()
clos := kLine.Close.Float64()
lowest := inc.LowValues.Tail(inc.Window).Min()
highest := inc.HighValues.Tail(inc.Window).Max()
k := 100.0 * (clos - lowest) / (highest - lowest)
k := 100.0 * (cloze - lowest) / (highest - lowest)
inc.K.Push(k)
d := inc.K.Tail(DPeriod).Mean()
@ -64,7 +64,7 @@ func (inc *STOCH) calculateAndUpdate(kLines []types.KLine) {
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
continue
}
inc.update(k)
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
}
inc.EmitUpdate(inc.LastK(), inc.LastD())

View File

@ -28,6 +28,23 @@ type VWAP struct {
UpdateCallbacks []func(value float64)
}
func (inc *VWAP) Update(price, volume float64) {
inc.Prices.Push(price)
inc.Volumes.Push(volume)
if inc.Window != 0 && len(inc.Prices) > inc.Window {
popIndex := len(inc.Prices) - inc.Window - 1
inc.WeightedSum -= inc.Prices[popIndex] * inc.Volumes[popIndex]
inc.VolumeSum -= inc.Volumes[popIndex]
}
inc.WeightedSum += price * volume
inc.VolumeSum += volume
vwap := inc.WeightedSum / inc.VolumeSum
inc.Values.Push(vwap)
}
func (inc *VWAP) Last() float64 {
if len(inc.Values) == 0 {
return 0.0
@ -50,26 +67,6 @@ func (inc *VWAP) Length() int {
var _ types.Series = &VWAP{}
func (inc *VWAP) Update(kLine types.KLine, priceF KLinePriceMapper) {
price := priceF(kLine)
volume := kLine.Volume.Float64()
inc.Prices.Push(price)
inc.Volumes.Push(volume)
if inc.Window != 0 && len(inc.Prices) > inc.Window {
popIndex := len(inc.Prices) - inc.Window - 1
inc.WeightedSum -= inc.Prices[popIndex] * inc.Volumes[popIndex]
inc.VolumeSum -= inc.Volumes[popIndex]
}
inc.WeightedSum += price * volume
inc.VolumeSum += volume
vwap := inc.WeightedSum / inc.VolumeSum
inc.Values.Push(vwap)
}
func (inc *VWAP) calculateAndUpdate(kLines []types.KLine) {
var priceF = KLineTypicalPriceMapper
@ -77,7 +74,7 @@ func (inc *VWAP) calculateAndUpdate(kLines []types.KLine) {
if inc.EndTime != zeroTime && !k.EndTime.After(inc.EndTime) {
continue
}
inc.Update(k, priceF)
inc.Update(priceF(k), k.Volume.Float64())
}
inc.EmitUpdate(inc.Last())
@ -99,7 +96,7 @@ func (inc *VWAP) Bind(updater KLineWindowUpdater) {
func CalculateVWAP(klines []types.KLine, priceF KLinePriceMapper, window int) float64 {
vwap := VWAP{IntervalWindow: types.IntervalWindow{Window: window}}
for _, k := range klines {
vwap.Update(k, priceF)
vwap.Update(priceF(k), k.Volume.Float64())
}
return vwap.Last()
}