mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 23:05:15 +00:00
105 lines
2.9 KiB
Go
105 lines
2.9 KiB
Go
package indicator
|
|
|
|
import (
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
)
|
|
|
|
// Refer: Klinger Oscillator
|
|
// Refer URL: https://www.investopedia.com/terms/k/klingeroscillator.asp
|
|
// Explanation:
|
|
// The Klinger Oscillator is a technical indicator that was developed by Stephen Klinger.
|
|
// It is based on the assumption that there is a relationship between money flow and price movement in the stock market.
|
|
// The Klinger Oscillator is calculated by taking the difference between a 34-period and 55-period moving average.
|
|
// Usually the indicator is using together with a 9-period or 13-period of moving average as the signal line.
|
|
// This indicator is often used to identify potential turning points in the market, as well as to confirm the strength of a trend.
|
|
//go:generate callbackgen -type KlingerOscillator
|
|
type KlingerOscillator struct {
|
|
types.SeriesBase
|
|
types.IntervalWindow
|
|
Fast types.UpdatableSeries
|
|
Slow types.UpdatableSeries
|
|
VF VolumeForce
|
|
|
|
updateCallbacks []func(value float64)
|
|
}
|
|
|
|
func (inc *KlingerOscillator) Length() int {
|
|
if inc.Fast == nil || inc.Slow == nil {
|
|
return 0
|
|
}
|
|
return inc.Fast.Length()
|
|
}
|
|
|
|
func (inc *KlingerOscillator) Last() float64 {
|
|
if inc.Fast == nil || inc.Slow == nil {
|
|
return 0
|
|
}
|
|
return inc.Fast.Last() - inc.Slow.Last()
|
|
}
|
|
func (inc *KlingerOscillator) Index(i int) float64 {
|
|
if inc.Fast == nil || inc.Slow == nil {
|
|
return 0
|
|
}
|
|
return inc.Fast.Index(i) - inc.Slow.Index(i)
|
|
}
|
|
|
|
func (inc *KlingerOscillator) Update(high, low, cloze, volume float64) {
|
|
if inc.Fast == nil {
|
|
inc.SeriesBase.Series = inc
|
|
inc.Fast = &EWMA{IntervalWindow: types.IntervalWindow{Window: 34, Interval: inc.Interval}}
|
|
inc.Slow = &EWMA{IntervalWindow: types.IntervalWindow{Window: 55, Interval: inc.Interval}}
|
|
}
|
|
|
|
if inc.VF.lastSum > 0 {
|
|
inc.VF.Update(high, low, cloze, volume)
|
|
inc.Fast.Update(inc.VF.Value)
|
|
inc.Slow.Update(inc.VF.Value)
|
|
} else {
|
|
inc.VF.Update(high, low, cloze, volume)
|
|
}
|
|
}
|
|
|
|
var _ types.SeriesExtend = &KlingerOscillator{}
|
|
|
|
func (inc *KlingerOscillator) PushK(k types.KLine) {
|
|
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64(), k.Volume.Float64())
|
|
}
|
|
|
|
func (inc *KlingerOscillator) BindK(target KLineClosedEmitter, symbol string, interval types.Interval) {
|
|
target.OnKLineClosed(types.KLineWith(symbol, interval, inc.PushK))
|
|
}
|
|
|
|
// Utility to hold the state of calculation
|
|
type VolumeForce struct {
|
|
dm float64
|
|
cm float64
|
|
trend float64
|
|
lastSum float64
|
|
Value float64
|
|
}
|
|
|
|
func (inc *VolumeForce) Update(high, low, cloze, volume float64) {
|
|
if inc.lastSum == 0 {
|
|
inc.dm = high - low
|
|
inc.cm = inc.dm
|
|
inc.trend = 1.
|
|
inc.lastSum = high + low + cloze
|
|
inc.Value = volume // first volume is not calculated
|
|
return
|
|
}
|
|
trend := 1.
|
|
if high+low+cloze <= inc.lastSum {
|
|
trend = -1.
|
|
}
|
|
dm := high - low
|
|
if inc.trend == trend {
|
|
inc.cm = inc.cm + dm
|
|
} else {
|
|
inc.cm = inc.dm + dm
|
|
}
|
|
inc.trend = trend
|
|
inc.lastSum = high + low + cloze
|
|
inc.dm = dm
|
|
inc.Value = volume * (2.*(inc.dm/inc.cm) - 1.) * trend
|
|
}
|