mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 00:05:15 +00:00
Fix klingerOscillator, add test for it
This commit is contained in:
parent
1ca79db4e5
commit
746279d0a7
|
@ -1,6 +1,8 @@
|
||||||
package indicator
|
package indicator
|
||||||
|
|
||||||
import "github.com/c9s/bbgo/pkg/types"
|
import (
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
// Refer: Klinger Oscillator
|
// Refer: Klinger Oscillator
|
||||||
// Refer URL: https://www.investopedia.com/terms/k/klingeroscillator.asp
|
// Refer URL: https://www.investopedia.com/terms/k/klingeroscillator.asp
|
||||||
|
@ -14,8 +16,8 @@ import "github.com/c9s/bbgo/pkg/types"
|
||||||
type KlingerOscillator struct {
|
type KlingerOscillator struct {
|
||||||
types.SeriesBase
|
types.SeriesBase
|
||||||
types.IntervalWindow
|
types.IntervalWindow
|
||||||
Fast *EWMA
|
Fast types.UpdatableSeries
|
||||||
Slow *EWMA
|
Slow types.UpdatableSeries
|
||||||
VF VolumeForce
|
VF VolumeForce
|
||||||
|
|
||||||
updateCallbacks []func(value float64)
|
updateCallbacks []func(value float64)
|
||||||
|
@ -47,9 +49,14 @@ func (inc *KlingerOscillator) Update(high, low, cloze, volume float64) {
|
||||||
inc.Fast = &EWMA{IntervalWindow: types.IntervalWindow{Window: 34, Interval: inc.Interval}}
|
inc.Fast = &EWMA{IntervalWindow: types.IntervalWindow{Window: 34, Interval: inc.Interval}}
|
||||||
inc.Slow = &EWMA{IntervalWindow: types.IntervalWindow{Window: 55, Interval: inc.Interval}}
|
inc.Slow = &EWMA{IntervalWindow: types.IntervalWindow{Window: 55, Interval: inc.Interval}}
|
||||||
}
|
}
|
||||||
inc.VF.Update(high, low, cloze, volume)
|
|
||||||
inc.Fast.Update(inc.VF.Value)
|
if inc.VF.lastSum > 0 {
|
||||||
inc.Slow.Update(inc.VF.Value)
|
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{}
|
var _ types.SeriesExtend = &KlingerOscillator{}
|
||||||
|
@ -58,29 +65,8 @@ func (inc *KlingerOscillator) PushK(k types.KLine) {
|
||||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64(), k.Volume.Float64())
|
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64(), k.Volume.Float64())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *KlingerOscillator) CalculateAndUpdate(allKLines []types.KLine) {
|
func (inc *KlingerOscillator) BindK(target KLineClosedEmitter, symbol string, interval types.Interval) {
|
||||||
if inc.Fast == nil {
|
target.OnKLineClosed(types.KLineWith(symbol, interval, inc.PushK))
|
||||||
for _, k := range allKLines {
|
|
||||||
inc.PushK(k)
|
|
||||||
inc.EmitUpdate(inc.Last())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
k := allKLines[len(allKLines)-1]
|
|
||||||
inc.PushK(k)
|
|
||||||
inc.EmitUpdate(inc.Last())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (inc *KlingerOscillator) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
|
||||||
if inc.Interval != interval {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
inc.CalculateAndUpdate(window)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (inc *KlingerOscillator) Bind(updater KLineWindowUpdater) {
|
|
||||||
updater.OnKLineWindowUpdate(inc.handleKLineWindowUpdate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility to hold the state of calculation
|
// Utility to hold the state of calculation
|
||||||
|
@ -93,12 +79,12 @@ type VolumeForce struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VolumeForce) Update(high, low, cloze, volume float64) {
|
func (inc *VolumeForce) Update(high, low, cloze, volume float64) {
|
||||||
if inc.Value == 0 {
|
if inc.lastSum == 0 {
|
||||||
inc.dm = high - low
|
inc.dm = high - low
|
||||||
inc.cm = inc.dm
|
inc.cm = inc.dm
|
||||||
inc.trend = 1.
|
inc.trend = 1.
|
||||||
inc.lastSum = high + low + cloze
|
inc.lastSum = high + low + cloze
|
||||||
inc.Value = volume * 100.
|
inc.Value = volume // first volume is not calculated
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
trend := 1.
|
trend := 1.
|
||||||
|
@ -114,5 +100,5 @@ func (inc *VolumeForce) Update(high, low, cloze, volume float64) {
|
||||||
inc.trend = trend
|
inc.trend = trend
|
||||||
inc.lastSum = high + low + cloze
|
inc.lastSum = high + low + cloze
|
||||||
inc.dm = dm
|
inc.dm = dm
|
||||||
inc.Value = volume * (2.*(inc.dm/inc.cm) - 1.) * trend * 100.
|
inc.Value = volume * (2.*(inc.dm/inc.cm) - 1.) * trend
|
||||||
}
|
}
|
||||||
|
|
53
pkg/indicator/klingeroscillator_test.go
Normal file
53
pkg/indicator/klingeroscillator_test.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package indicator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
import pandas as pd
|
||||||
|
import pandas_ta as ta
|
||||||
|
high = pd.Series([1.1, 1.3, 1.5, 1.7, 1.9, 2.2, 2.4, 2.1, 1.8, 1.7])
|
||||||
|
low = pd.Series([0.9, 1.1, 1.2, 1.5, 1.7, 2.0, 2.2, 1.9, 1.6, 1.5])
|
||||||
|
close = pd.Series([1.0, 1.2, 1.4, 1.6, 1.8, 2.1, 2.3, 2.0, 1.7, 1.6])
|
||||||
|
vol = pd.Series([300., 200., 200., 150., 150., 200., 200., 150., 300., 350.])
|
||||||
|
# kvo = ta.kvo(high, low, close, vol, fast=3, slow=5, signal=1)
|
||||||
|
# print(kvo)
|
||||||
|
# # The implementation of kvo in pandas_ta is different from the one defined in investopedia
|
||||||
|
# # VF is not simply multipying trend
|
||||||
|
# # Also the value is not multiplied by 100 in pandas_ta
|
||||||
|
*/
|
||||||
|
|
||||||
|
func Test_KlingerOscillator(t *testing.T) {
|
||||||
|
var high, low, cloze, vResult, vol []fixedpoint.Value
|
||||||
|
if err := json.Unmarshal([]byte(`[1.1, 1.3, 1.5, 1.7, 1.9, 2.2, 2.4, 2.1, 1.8, 1.7]`), &high); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(`[0.9, 1.1, 1.2, 1.5, 1.7, 2.0, 2.2, 1.9, 1.6, 1.5]`), &low); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(`[1.0, 1.2, 1.4, 1.6, 1.8, 2.1, 2.3, 2.0, 1.7, 1.6]`), &cloze); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(`[300.0, 200.0, 200.0, 150.0, 150.0, 200.0, 200.0, 150.0, 300.0, 350.0]`), &vol); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(`[300.0, 0.0, -28.5, -83, -95, -138, -146.7, 0, 100, 175]`), &vResult); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
k := KlingerOscillator{
|
||||||
|
Fast: &EWMA{IntervalWindow: types.IntervalWindow{Window: 3}},
|
||||||
|
Slow: &EWMA{IntervalWindow: types.IntervalWindow{Window: 5}},
|
||||||
|
}
|
||||||
|
var Delta = 0.5
|
||||||
|
for i := 0; i < len(high); i++ {
|
||||||
|
k.Update(high[i].Float64(), low[i].Float64(), cloze[i].Float64(), vol[i].Float64())
|
||||||
|
assert.InDelta(t, k.VF.Value, vResult[i].Float64(), Delta)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user