mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
all: add parameter index to the Last method
This commit is contained in:
parent
2a074ba11b
commit
5515f588e3
|
@ -48,7 +48,7 @@ func (s *LowerShadowTakeProfit) Bind(session *ExchangeSession, orderExecutor *Ge
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip close price higher than the ewma
|
// skip close price higher than the ewma
|
||||||
if closePrice.Float64() > ewma.Last() {
|
if closePrice.Float64() > ewma.Last(0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ func (s *StopEMA) Bind(session *ExchangeSession, orderExecutor *GeneralOrderExec
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StopEMA) Allowed(closePrice fixedpoint.Value) bool {
|
func (s *StopEMA) Allowed(closePrice fixedpoint.Value) bool {
|
||||||
ema := fixedpoint.NewFromFloat(s.stopEWMA.Last())
|
ema := fixedpoint.NewFromFloat(s.stopEWMA.Last(0))
|
||||||
if ema.IsZero() {
|
if ema.IsZero() {
|
||||||
logrus.Infof("stopEMA protection: value is zero, skip")
|
logrus.Infof("stopEMA protection: value is zero, skip")
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -29,12 +29,12 @@ func (s *TrendEMA) Bind(session *ExchangeSession, orderExecutor *GeneralOrderExe
|
||||||
}
|
}
|
||||||
|
|
||||||
s.last = s.ewma.Values[s.ewma.Length()-2]
|
s.last = s.ewma.Values[s.ewma.Length()-2]
|
||||||
s.current = s.ewma.Last()
|
s.current = s.ewma.Last(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, s.Interval, func(kline types.KLine) {
|
session.MarketDataStream.OnKLineClosed(types.KLineWith(symbol, s.Interval, func(kline types.KLine) {
|
||||||
s.last = s.current
|
s.last = s.current
|
||||||
s.current = s.ewma.Last()
|
s.current = s.ewma.Last(0)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -183,12 +183,12 @@ func (s Slice) Addr() *Slice {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last, Index, Length implements the types.Series interface
|
// Last, Index, Length implements the types.Series interface
|
||||||
func (s Slice) Last() float64 {
|
func (s Slice) Last(i int) float64 {
|
||||||
length := len(s)
|
length := len(s)
|
||||||
if length > 0 {
|
if i < 0 || length-1-i < 0 {
|
||||||
return s[length-1]
|
return 0.0
|
||||||
}
|
}
|
||||||
return 0.0
|
return s[length-1-i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index fetches the element from the end of the slice
|
// Index fetches the element from the end of the slice
|
||||||
|
|
|
@ -35,15 +35,16 @@ func (inc *AD) Update(high, low, cloze, volume float64) {
|
||||||
moneyFlowVolume = ((2*cloze - high - low) / (high - low)) * volume
|
moneyFlowVolume = ((2*cloze - high - low) / (high - low)) * volume
|
||||||
}
|
}
|
||||||
|
|
||||||
ad := inc.Last() + moneyFlowVolume
|
ad := inc.Last(0) + moneyFlowVolume
|
||||||
inc.Values.Push(ad)
|
inc.Values.Push(ad)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *AD) Last() float64 {
|
func (inc *AD) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
length := len(inc.Values)
|
||||||
return 0.0
|
if length == 0 || length-i-1 < 0 {
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Values[len(inc.Values)-1]
|
return inc.Values[length-i-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *AD) Index(i int) float64 {
|
func (inc *AD) Index(i int) float64 {
|
||||||
|
@ -68,7 +69,7 @@ func (inc *AD) CalculateAndUpdate(kLines []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())
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
//
|
//
|
||||||
// @param offset: Gaussian applied to the combo line. 1->ema, 0->sma
|
// @param offset: Gaussian applied to the combo line. 1->ema, 0->sma
|
||||||
// @param sigma: the standard deviation applied to the combo line. This makes the combo line sharper
|
// @param sigma: the standard deviation applied to the combo line. This makes the combo line sharper
|
||||||
|
//
|
||||||
//go:generate callbackgen -type ALMA
|
//go:generate callbackgen -type ALMA
|
||||||
type ALMA struct {
|
type ALMA struct {
|
||||||
types.SeriesBase
|
types.SeriesBase
|
||||||
|
@ -64,11 +65,11 @@ func (inc *ALMA) Update(value float64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *ALMA) Last() float64 {
|
func (inc *ALMA) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
if i >= len(inc.Values) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Values[len(inc.Values)-1]
|
return inc.Values[len(inc.Values)-i-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *ALMA) Index(i int) float64 {
|
func (inc *ALMA) Index(i int) float64 {
|
||||||
|
@ -88,12 +89,12 @@ func (inc *ALMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.input == nil {
|
if inc.input == nil {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.Update(k.Close.Float64())
|
inc.Update(k.Close.Float64())
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
inc.Update(allKLines[len(allKLines)-1].Close.Float64())
|
inc.Update(allKLines[len(allKLines)-1].Close.Float64())
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *ALMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
func (inc *ALMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||||
|
|
|
@ -53,7 +53,7 @@ func Test_ALMA(t *testing.T) {
|
||||||
Sigma: 6,
|
Sigma: 6,
|
||||||
}
|
}
|
||||||
alma.CalculateAndUpdate(tt.kLines)
|
alma.CalculateAndUpdate(tt.kLines)
|
||||||
assert.InDelta(t, tt.want, alma.Last(), Delta)
|
assert.InDelta(t, tt.want, alma.Last(0), Delta)
|
||||||
assert.InDelta(t, tt.next, alma.Index(1), Delta)
|
assert.InDelta(t, tt.next, alma.Index(1), Delta)
|
||||||
assert.Equal(t, tt.all, alma.Length())
|
assert.Equal(t, tt.all, alma.Length())
|
||||||
})
|
})
|
||||||
|
|
|
@ -74,18 +74,18 @@ func (inc *ATR) Update(high, low, cloze float64) {
|
||||||
|
|
||||||
// apply rolling moving average
|
// apply rolling moving average
|
||||||
inc.RMA.Update(trueRange)
|
inc.RMA.Update(trueRange)
|
||||||
atr := inc.RMA.Last()
|
atr := inc.RMA.Last(0)
|
||||||
inc.PercentageVolatility.Push(atr / cloze)
|
inc.PercentageVolatility.Push(atr / cloze)
|
||||||
if len(inc.PercentageVolatility) > MaxNumOfATR {
|
if len(inc.PercentageVolatility) > MaxNumOfATR {
|
||||||
inc.PercentageVolatility = inc.PercentageVolatility[MaxNumOfATRTruncateSize-1:]
|
inc.PercentageVolatility = inc.PercentageVolatility[MaxNumOfATRTruncateSize-1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *ATR) Last() float64 {
|
func (inc *ATR) Last(i int) float64 {
|
||||||
if inc.RMA == nil {
|
if inc.RMA == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.RMA.Last()
|
return inc.RMA.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *ATR) Index(i int) float64 {
|
func (inc *ATR) Index(i int) float64 {
|
||||||
|
@ -110,5 +110,5 @@ func (inc *ATR) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
inc.Update(k.High.Float64(), k.Low.Float64(), k.Close.Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ func Test_calculateATR(t *testing.T) {
|
||||||
atr.PushK(k)
|
atr.PushK(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
got := atr.Last()
|
got := atr.Last(0)
|
||||||
diff := math.Trunc((got-tt.want)*100) / 100
|
diff := math.Trunc((got-tt.want)*100) / 100
|
||||||
if diff != 0 {
|
if diff != 0 {
|
||||||
t.Errorf("calculateATR() = %v, want %v", got, tt.want)
|
t.Errorf("calculateATR() = %v, want %v", got, tt.want)
|
||||||
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
//
|
//
|
||||||
// Calculation:
|
// Calculation:
|
||||||
//
|
//
|
||||||
// ATRP = (Average True Range / Close) * 100
|
// ATRP = (Average True Range / Close) * 100
|
||||||
//
|
//
|
||||||
//go:generate callbackgen -type ATRP
|
//go:generate callbackgen -type ATRP
|
||||||
type ATRP struct {
|
type ATRP struct {
|
||||||
|
@ -69,15 +69,15 @@ func (inc *ATRP) Update(high, low, cloze float64) {
|
||||||
|
|
||||||
// apply rolling moving average
|
// apply rolling moving average
|
||||||
inc.RMA.Update(trueRange)
|
inc.RMA.Update(trueRange)
|
||||||
atr := inc.RMA.Last()
|
atr := inc.RMA.Last(0)
|
||||||
inc.PercentageVolatility.Push(atr / cloze)
|
inc.PercentageVolatility.Push(atr / cloze)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *ATRP) Last() float64 {
|
func (inc *ATRP) Last(i int) float64 {
|
||||||
if inc.RMA == nil {
|
if inc.RMA == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.RMA.Last()
|
return inc.RMA.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *ATRP) Index(i int) float64 {
|
func (inc *ATRP) Index(i int) float64 {
|
||||||
|
@ -109,7 +109,7 @@ func (inc *ATRP) CalculateAndUpdate(kLines []types.KLine) {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,8 +84,8 @@ func (inc *BOLL) Update(value float64) {
|
||||||
inc.SMA.Update(value)
|
inc.SMA.Update(value)
|
||||||
inc.StdDev.Update(value)
|
inc.StdDev.Update(value)
|
||||||
|
|
||||||
var sma = inc.SMA.Last()
|
var sma = inc.SMA.Last(0)
|
||||||
var stdDev = inc.StdDev.Last()
|
var stdDev = inc.StdDev.Last(0)
|
||||||
var band = inc.K * stdDev
|
var band = inc.K * stdDev
|
||||||
|
|
||||||
var upBand = sma + band
|
var upBand = sma + band
|
||||||
|
@ -105,7 +105,7 @@ func (inc *BOLL) PushK(k types.KLine) {
|
||||||
}
|
}
|
||||||
inc.Update(k.Close.Float64())
|
inc.Update(k.Close.Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.SMA.Last(), inc.UpBand.Last(), inc.DownBand.Last())
|
inc.EmitUpdate(inc.SMA.Last(0), inc.UpBand.Last(0), inc.DownBand.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *BOLL) LoadK(allKLines []types.KLine) {
|
func (inc *BOLL) LoadK(allKLines []types.KLine) {
|
||||||
|
@ -113,7 +113,7 @@ func (inc *BOLL) LoadK(allKLines []types.KLine) {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.EmitUpdate(inc.SMA.Last(), inc.UpBand.Last(), inc.DownBand.Last())
|
inc.EmitUpdate(inc.SMA.Last(0), inc.UpBand.Last(0), inc.DownBand.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *BOLL) CalculateAndUpdate(allKLines []types.KLine) {
|
func (inc *BOLL) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
|
|
|
@ -61,8 +61,8 @@ func TestBOLL(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
boll := BOLL{IntervalWindow: types.IntervalWindow{Window: tt.window}, K: tt.k}
|
boll := BOLL{IntervalWindow: types.IntervalWindow{Window: tt.window}, K: tt.k}
|
||||||
boll.CalculateAndUpdate(tt.kLines)
|
boll.CalculateAndUpdate(tt.kLines)
|
||||||
assert.InDelta(t, tt.up, boll.UpBand.Last(), Delta)
|
assert.InDelta(t, tt.up, boll.UpBand.Last(0), Delta)
|
||||||
assert.InDelta(t, tt.down, boll.DownBand.Last(), Delta)
|
assert.InDelta(t, tt.down, boll.DownBand.Last(0), Delta)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ func (inc *CCI) Update(value float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.Input.Push(value)
|
inc.Input.Push(value)
|
||||||
tp := inc.TypicalPrice.Last() - inc.Input.Index(inc.Window) + value
|
tp := inc.TypicalPrice.Last(0) - inc.Input.Index(inc.Window) + value
|
||||||
inc.TypicalPrice.Push(tp)
|
inc.TypicalPrice.Push(tp)
|
||||||
if len(inc.Input) < inc.Window {
|
if len(inc.Input) < inc.Window {
|
||||||
return
|
return
|
||||||
|
@ -55,7 +55,7 @@ func (inc *CCI) Update(value float64) {
|
||||||
}
|
}
|
||||||
md := 0.
|
md := 0.
|
||||||
for i := 0; i < inc.Window; i++ {
|
for i := 0; i < inc.Window; i++ {
|
||||||
diff := inc.Input.Index(i) - ma
|
diff := inc.Input.Last(i) - ma
|
||||||
md += diff * diff
|
md += diff * diff
|
||||||
}
|
}
|
||||||
md = math.Sqrt(md / float64(inc.Window))
|
md = math.Sqrt(md / float64(inc.Window))
|
||||||
|
@ -68,18 +68,12 @@ func (inc *CCI) Update(value float64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *CCI) Last() float64 {
|
func (inc *CCI) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *CCI) Index(i int) float64 {
|
func (inc *CCI) Index(i int) float64 {
|
||||||
if i >= len(inc.Values) {
|
return inc.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-1-i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *CCI) Length() int {
|
func (inc *CCI) Length() int {
|
||||||
|
@ -96,12 +90,12 @@ func (inc *CCI) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.TypicalPrice.Length() == 0 {
|
if inc.TypicalPrice.Length() == 0 {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ func Test_CCI(t *testing.T) {
|
||||||
cci.Update(value)
|
cci.Update(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
last := cci.Last()
|
last := cci.Last(0)
|
||||||
assert.InDelta(t, 93.250481, last, Delta)
|
assert.InDelta(t, 93.250481, last, Delta)
|
||||||
assert.InDelta(t, 81.813449, cci.Index(1), Delta)
|
assert.InDelta(t, 81.813449, cci.Index(1), Delta)
|
||||||
assert.Equal(t, 50-16+1, cci.Length())
|
assert.Equal(t, 50-16+1, cci.Length())
|
||||||
|
|
|
@ -7,12 +7,13 @@ import (
|
||||||
|
|
||||||
// Refer: Cumulative Moving Average, Cumulative Average
|
// Refer: Cumulative Moving Average, Cumulative Average
|
||||||
// Refer: https://en.wikipedia.org/wiki/Moving_average
|
// Refer: https://en.wikipedia.org/wiki/Moving_average
|
||||||
|
//
|
||||||
//go:generate callbackgen -type CA
|
//go:generate callbackgen -type CA
|
||||||
type CA struct {
|
type CA struct {
|
||||||
types.SeriesBase
|
types.SeriesBase
|
||||||
Interval types.Interval
|
Interval types.Interval
|
||||||
Values floats.Slice
|
Values floats.Slice
|
||||||
length float64
|
length float64
|
||||||
UpdateCallbacks []func(value float64)
|
UpdateCallbacks []func(value float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ func (inc *CA) Update(x float64) {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
inc.SeriesBase.Series = inc
|
inc.SeriesBase.Series = inc
|
||||||
}
|
}
|
||||||
newVal := (inc.Values.Last()*inc.length + x) / (inc.length + 1.)
|
newVal := (inc.Values.Last(0)*inc.length + x) / (inc.length + 1.)
|
||||||
inc.length += 1
|
inc.length += 1
|
||||||
inc.Values.Push(newVal)
|
inc.Values.Push(newVal)
|
||||||
if len(inc.Values) > MaxNumOfEWMA {
|
if len(inc.Values) > MaxNumOfEWMA {
|
||||||
|
@ -29,18 +30,12 @@ func (inc *CA) Update(x float64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *CA) Last() float64 {
|
func (inc *CA) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *CA) Index(i int) float64 {
|
func (inc *CA) Index(i int) float64 {
|
||||||
if i >= len(inc.Values) {
|
return inc.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-1-i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *CA) Length() int {
|
func (inc *CA) Length() int {
|
||||||
|
@ -56,7 +51,7 @@ func (inc *CA) PushK(k types.KLine) {
|
||||||
func (inc *CA) CalculateAndUpdate(allKLines []types.KLine) {
|
func (inc *CA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,22 +51,19 @@ func (inc *DEMA) Update(value float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.a1.Update(value)
|
inc.a1.Update(value)
|
||||||
inc.a2.Update(inc.a1.Last())
|
inc.a2.Update(inc.a1.Last(0))
|
||||||
inc.Values.Push(2*inc.a1.Last() - inc.a2.Last())
|
inc.Values.Push(2*inc.a1.Last(0) - inc.a2.Last(0))
|
||||||
if len(inc.Values) > MaxNumOfEWMA {
|
if len(inc.Values) > MaxNumOfEWMA {
|
||||||
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
|
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *DEMA) Last() float64 {
|
func (inc *DEMA) Last(i int) float64 {
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *DEMA) Index(i int) float64 {
|
func (inc *DEMA) Index(i int) float64 {
|
||||||
if len(inc.Values)-i-1 >= 0 {
|
return inc.Last(i)
|
||||||
return inc.Values[len(inc.Values)-1-i]
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *DEMA) Length() int {
|
func (inc *DEMA) Length() int {
|
||||||
|
@ -83,13 +80,13 @@ func (inc *DEMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.a1 == nil {
|
if inc.a1 == nil {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// last k
|
// last k
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ func Test_DEMA(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
dema := DEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
dema := DEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
||||||
dema.CalculateAndUpdate(tt.kLines)
|
dema.CalculateAndUpdate(tt.kLines)
|
||||||
last := dema.Last()
|
last := dema.Last(0)
|
||||||
assert.InDelta(t, tt.want, last, Delta)
|
assert.InDelta(t, tt.want, last, Delta)
|
||||||
assert.InDelta(t, tt.next, dema.Index(1), Delta)
|
assert.InDelta(t, tt.next, dema.Index(1), Delta)
|
||||||
assert.Equal(t, tt.all, dema.Length())
|
assert.Equal(t, tt.all, dema.Length())
|
||||||
|
|
|
@ -72,9 +72,9 @@ func (inc *DMI) Update(high, low, cloze float64) {
|
||||||
if inc.atr.Length() < inc.Window {
|
if inc.atr.Length() < inc.Window {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
k := 100. / inc.atr.Last()
|
k := 100. / inc.atr.Last(0)
|
||||||
dmp := inc.DMP.Last()
|
dmp := inc.DMP.Last(0)
|
||||||
dmn := inc.DMN.Last()
|
dmn := inc.DMN.Last(0)
|
||||||
inc.DIPlus.Update(k * dmp)
|
inc.DIPlus.Update(k * dmp)
|
||||||
inc.DIMinus.Update(k * dmn)
|
inc.DIMinus.Update(k * dmn)
|
||||||
dx := 100. * math.Abs(dmp-dmn) / (dmp + dmn)
|
dx := 100. * math.Abs(dmp-dmn) / (dmp + dmn)
|
||||||
|
@ -108,11 +108,11 @@ func (inc *DMI) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.ADX == nil {
|
if inc.ADX == nil {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.DIPlus.Last(), inc.DIMinus.Last(), inc.ADX.Last())
|
inc.EmitUpdate(inc.DIPlus.Last(0), inc.DIMinus.Last(0), inc.ADX.Last(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
inc.PushK(last)
|
inc.PushK(last)
|
||||||
inc.EmitUpdate(inc.DIPlus.Last(), inc.DIMinus.Last(), inc.ADX.Last())
|
inc.EmitUpdate(inc.DIPlus.Last(0), inc.DIMinus.Last(0), inc.ADX.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,9 +78,9 @@ func Test_DMI(t *testing.T) {
|
||||||
ADXSmoothing: 14,
|
ADXSmoothing: 14,
|
||||||
}
|
}
|
||||||
dmi.CalculateAndUpdate(tt.klines)
|
dmi.CalculateAndUpdate(tt.klines)
|
||||||
assert.InDelta(t, dmi.GetDIPlus().Last(), tt.want.dip, Delta)
|
assert.InDelta(t, dmi.GetDIPlus().Last(0), tt.want.dip, Delta)
|
||||||
assert.InDelta(t, dmi.GetDIMinus().Last(), tt.want.dim, Delta)
|
assert.InDelta(t, dmi.GetDIMinus().Last(0), tt.want.dim, Delta)
|
||||||
assert.InDelta(t, dmi.GetADX().Last(), tt.want.adx, Delta)
|
assert.InDelta(t, dmi.GetADX().Last(0), tt.want.adx, Delta)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (inc *Drift) Update(value float64) {
|
||||||
inc.chng.Update(chng)
|
inc.chng.Update(chng)
|
||||||
if inc.chng.Length() >= inc.Window {
|
if inc.chng.Length() >= inc.Window {
|
||||||
stdev := types.Stdev(inc.chng, inc.Window)
|
stdev := types.Stdev(inc.chng, inc.Window)
|
||||||
drift := inc.MA.Last() - stdev*stdev*0.5
|
drift := inc.MA.Last(0) - stdev*stdev*0.5
|
||||||
inc.Values.Push(drift)
|
inc.Values.Push(drift)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ func (inc *Drift) ZeroPoint() float64 {
|
||||||
} else {
|
} else {
|
||||||
return N2
|
return N2
|
||||||
}*/
|
}*/
|
||||||
return inc.LastValue * math.Exp(window*(0.5*stdev*stdev)+chng-inc.MA.Last()*window)
|
return inc.LastValue * math.Exp(window*(0.5*stdev*stdev)+chng-inc.MA.Last(0)*window)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Drift) Clone() (out *Drift) {
|
func (inc *Drift) Clone() (out *Drift) {
|
||||||
|
@ -96,17 +96,11 @@ func (inc *Drift) TestUpdate(value float64) *Drift {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Drift) Index(i int) float64 {
|
func (inc *Drift) Index(i int) float64 {
|
||||||
if inc.Values == nil {
|
return inc.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Drift) Last() float64 {
|
func (inc *Drift) Last(i int) float64 {
|
||||||
if inc.Values.Length() == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values.Last()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Drift) Length() int {
|
func (inc *Drift) Length() int {
|
||||||
|
@ -126,12 +120,12 @@ func (inc *Drift) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.chng == nil {
|
if inc.chng == nil {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,11 +55,12 @@ func (inc *EMV) Index(i int) float64 {
|
||||||
return inc.Values.Index(i)
|
return inc.Values.Index(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *EMV) Last() float64 {
|
func (inc *EMV) Last(i int) float64 {
|
||||||
if inc.Values == nil {
|
if inc.Values == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Values.Last()
|
|
||||||
|
return inc.Values.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *EMV) Length() int {
|
func (inc *EMV) Length() int {
|
||||||
|
|
|
@ -16,7 +16,7 @@ func Test_EMV(t *testing.T) {
|
||||||
}
|
}
|
||||||
emv.Update(63.74, 62.63, 32178836)
|
emv.Update(63.74, 62.63, 32178836)
|
||||||
emv.Update(64.51, 63.85, 36461672)
|
emv.Update(64.51, 63.85, 36461672)
|
||||||
assert.InDelta(t, 1.8, emv.Values.rawValues.Last(), Delta)
|
assert.InDelta(t, 1.8, emv.Values.rawValues.Last(0), Delta)
|
||||||
emv.Update(64.57, 63.81, 51372680)
|
emv.Update(64.57, 63.81, 51372680)
|
||||||
emv.Update(64.31, 62.62, 42476356)
|
emv.Update(64.31, 62.62, 42476356)
|
||||||
emv.Update(63.43, 62.73, 29504176)
|
emv.Update(63.43, 62.73, 29504176)
|
||||||
|
@ -30,5 +30,5 @@ func Test_EMV(t *testing.T) {
|
||||||
emv.Update(65.25, 64.48, 37015388)
|
emv.Update(65.25, 64.48, 37015388)
|
||||||
emv.Update(64.69, 63.65, 40672116)
|
emv.Update(64.69, 63.65, 40672116)
|
||||||
emv.Update(64.26, 63.68, 35627200)
|
emv.Update(64.26, 63.68, 35627200)
|
||||||
assert.InDelta(t, -0.03, emv.Last(), Delta)
|
assert.InDelta(t, -0.03, emv.Last(0), Delta)
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,24 +50,20 @@ func (inc *EWMA) Update(value float64) {
|
||||||
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
|
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
ema := (1-multiplier)*inc.Last() + multiplier*value
|
ema := (1-multiplier)*inc.Last(0) + multiplier*value
|
||||||
inc.Values.Push(ema)
|
inc.Values.Push(ema)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *EWMA) Last() float64 {
|
func (inc *EWMA) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return inc.Values[len(inc.Values)-1]
|
return inc.Values.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *EWMA) Index(i int) float64 {
|
func (inc *EWMA) Index(i int) float64 {
|
||||||
if i >= len(inc.Values) {
|
return inc.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return inc.Values[len(inc.Values)-1-i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *EWMA) Length() int {
|
func (inc *EWMA) Length() int {
|
||||||
|
@ -81,7 +77,7 @@ func (inc *EWMA) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(k.Close.Float64())
|
inc.Update(k.Close.Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateKLinesEMA(allKLines []types.KLine, priceF KLineValueMapper, window int) float64 {
|
func CalculateKLinesEMA(allKLines []types.KLine, priceF KLineValueMapper, window int) float64 {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
// The Fisher Transform is calculated by taking the natural logarithm of the ratio of the security's current price to its moving average,
|
// The Fisher Transform is calculated by taking the natural logarithm of the ratio of the security's current price to its moving average,
|
||||||
// and then double-smoothing the result. This resulting line is called the Fisher Transform line, and can be plotted on the price chart
|
// and then double-smoothing the result. This resulting line is called the Fisher Transform line, and can be plotted on the price chart
|
||||||
// along with the security's price.
|
// along with the security's price.
|
||||||
|
//
|
||||||
//go:generate callbackgen -type FisherTransform
|
//go:generate callbackgen -type FisherTransform
|
||||||
type FisherTransform struct {
|
type FisherTransform struct {
|
||||||
types.SeriesBase
|
types.SeriesBase
|
||||||
|
@ -60,18 +61,15 @@ func (inc *FisherTransform) Update(value float64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *FisherTransform) Last() float64 {
|
func (inc *FisherTransform) Last(i int) float64 {
|
||||||
if inc.Values == nil {
|
if inc.Values == nil {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *FisherTransform) Index(i int) float64 {
|
func (inc *FisherTransform) Index(i int) float64 {
|
||||||
if inc.Values == nil {
|
return inc.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *FisherTransform) Length() int {
|
func (inc *FisherTransform) Length() int {
|
||||||
|
|
|
@ -23,16 +23,12 @@ func NewFloat64Series(v ...float64) Float64Series {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Float64Series) Last() float64 {
|
func (f *Float64Series) Last(i int) float64 {
|
||||||
return f.slice.Last()
|
return f.slice.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Float64Series) Index(i int) float64 {
|
func (f *Float64Series) Index(i int) float64 {
|
||||||
length := len(f.slice)
|
return f.slice.Last(i)
|
||||||
if length == 0 || length-i-1 < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return f.slice[length-i-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Float64Series) Length() int {
|
func (f *Float64Series) Length() int {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package indicator
|
package indicator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/datatype/floats"
|
"github.com/c9s/bbgo/pkg/datatype/floats"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
"math"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Refer: https://jamesgoulding.com/Research_II/Ehlers/Ehlers%20(Optimal%20Tracking%20Filters).doc
|
// Refer: https://jamesgoulding.com/Research_II/Ehlers/Ehlers%20(Optimal%20Tracking%20Filters).doc
|
||||||
|
@ -39,16 +40,13 @@ func (inc *GHFilter) update(value, uncertainty float64) {
|
||||||
lambda := inc.a / inc.b
|
lambda := inc.a / inc.b
|
||||||
lambda2 := lambda * lambda
|
lambda2 := lambda * lambda
|
||||||
alpha := (-lambda2 + math.Sqrt(lambda2*lambda2+16*lambda2)) / 8
|
alpha := (-lambda2 + math.Sqrt(lambda2*lambda2+16*lambda2)) / 8
|
||||||
filtered := alpha*value + (1-alpha)*inc.Values.Last()
|
filtered := alpha*value + (1-alpha)*inc.Values.Last(0)
|
||||||
inc.Values.Push(filtered)
|
inc.Values.Push(filtered)
|
||||||
inc.lastMeasurement = value
|
inc.lastMeasurement = value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *GHFilter) Index(i int) float64 {
|
func (inc *GHFilter) Index(i int) float64 {
|
||||||
if inc.Values == nil {
|
return inc.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *GHFilter) Length() int {
|
func (inc *GHFilter) Length() int {
|
||||||
|
@ -58,11 +56,11 @@ func (inc *GHFilter) Length() int {
|
||||||
return inc.Values.Length()
|
return inc.Values.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *GHFilter) Last() float64 {
|
func (inc *GHFilter) Last(i int) float64 {
|
||||||
if inc.Values == nil {
|
if inc.Values == nil {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// interfaces implementation check
|
// interfaces implementation check
|
||||||
|
|
|
@ -6058,7 +6058,7 @@ func Test_GHFilter(t *testing.T) {
|
||||||
for _, k := range klines {
|
for _, k := range klines {
|
||||||
filter.PushK(k)
|
filter.PushK(k)
|
||||||
}
|
}
|
||||||
got := filter.Last()
|
got := filter.Last(0)
|
||||||
got = math.Trunc(got*100.0) / 100.0
|
got = math.Trunc(got*100.0) / 100.0
|
||||||
if got != tt.want {
|
if got != tt.want {
|
||||||
t.Errorf("GHFilter.Last() = %v, want %v", got, tt.want)
|
t.Errorf("GHFilter.Last() = %v, want %v", got, tt.want)
|
||||||
|
@ -6125,10 +6125,10 @@ func Test_GHFilterEstimationAccurate(t *testing.T) {
|
||||||
for i, k := range klines {
|
for i, k := range klines {
|
||||||
// square error between last estimated state and current actual state
|
// square error between last estimated state and current actual state
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
filterDiff2Sum += klineSquareError(filter.Last(), k)
|
filterDiff2Sum += klineSquareError(filter.Last(0), k)
|
||||||
ewmaDiff2Sum += klineSquareError(ewma.Last(), k)
|
ewmaDiff2Sum += klineSquareError(ewma.Last(0), k)
|
||||||
filterCloseDiff2Sum += closeSquareError(filter.Last(), k)
|
filterCloseDiff2Sum += closeSquareError(filter.Last(0), k)
|
||||||
ewmaCloseDiff2Sum += closeSquareError(ewma.Last(), k)
|
ewmaCloseDiff2Sum += closeSquareError(ewma.Last(0), k)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update estimations
|
// update estimations
|
||||||
|
|
|
@ -22,18 +22,15 @@ type GMA struct {
|
||||||
UpdateCallbacks []func(value float64)
|
UpdateCallbacks []func(value float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *GMA) Last() float64 {
|
func (inc *GMA) Last(i int) float64 {
|
||||||
if inc.SMA == nil {
|
if inc.SMA == nil {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
return math.Exp(inc.SMA.Last())
|
return math.Exp(inc.SMA.Last(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *GMA) Index(i int) float64 {
|
func (inc *GMA) Index(i int) float64 {
|
||||||
if inc.SMA == nil {
|
return inc.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return math.Exp(inc.SMA.Index(i))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *GMA) Length() int {
|
func (inc *GMA) Length() int {
|
||||||
|
|
|
@ -51,10 +51,10 @@ func Test_GMA(t *testing.T) {
|
||||||
for _, k := range tt.kLines {
|
for _, k := range tt.kLines {
|
||||||
gma.PushK(k)
|
gma.PushK(k)
|
||||||
}
|
}
|
||||||
assert.InDelta(t, tt.want, gma.Last(), Delta)
|
assert.InDelta(t, tt.want, gma.Last(0), Delta)
|
||||||
assert.InDelta(t, tt.next, gma.Index(1), Delta)
|
assert.InDelta(t, tt.next, gma.Index(1), Delta)
|
||||||
gma.Update(tt.update)
|
gma.Update(tt.update)
|
||||||
assert.InDelta(t, tt.updateResult, gma.Last(), Delta)
|
assert.InDelta(t, tt.updateResult, gma.Last(0), Delta)
|
||||||
assert.Equal(t, tt.all, gma.Length())
|
assert.Equal(t, tt.all, gma.Length())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
// the weighted moving average of the input data using a weighting factor of W, where W is the square root of the length of the moving average.
|
// the weighted moving average of the input data using a weighting factor of W, where W is the square root of the length of the moving average.
|
||||||
// The result is then double-smoothed by taking the weighted moving average of this result using a weighting factor of W/2. This final average
|
// The result is then double-smoothed by taking the weighted moving average of this result using a weighting factor of W/2. This final average
|
||||||
// forms the HMA line, which can be used to make predictions about future price movements.
|
// forms the HMA line, which can be used to make predictions about future price movements.
|
||||||
|
//
|
||||||
//go:generate callbackgen -type HULL
|
//go:generate callbackgen -type HULL
|
||||||
type HULL struct {
|
type HULL struct {
|
||||||
types.SeriesBase
|
types.SeriesBase
|
||||||
|
@ -36,14 +37,14 @@ func (inc *HULL) Update(value float64) {
|
||||||
}
|
}
|
||||||
inc.ma1.Update(value)
|
inc.ma1.Update(value)
|
||||||
inc.ma2.Update(value)
|
inc.ma2.Update(value)
|
||||||
inc.result.Update(2*inc.ma1.Last() - inc.ma2.Last())
|
inc.result.Update(2*inc.ma1.Last(0) - inc.ma2.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *HULL) Last() float64 {
|
func (inc *HULL) Last(i int) float64 {
|
||||||
if inc.result == nil {
|
if inc.result == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.result.Last()
|
return inc.result.Index(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *HULL) Index(i int) float64 {
|
func (inc *HULL) Index(i int) float64 {
|
||||||
|
@ -66,5 +67,5 @@ func (inc *HULL) PushK(k types.KLine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.Update(k.Close.Float64())
|
inc.Update(k.Close.Float64())
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ func Test_HULL(t *testing.T) {
|
||||||
hull.PushK(k)
|
hull.PushK(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
last := hull.Last()
|
last := hull.Last(0)
|
||||||
assert.InDelta(t, tt.want, last, Delta)
|
assert.InDelta(t, tt.want, last, Delta)
|
||||||
assert.InDelta(t, tt.next, hull.Index(1), Delta)
|
assert.InDelta(t, tt.next, hull.Index(1), Delta)
|
||||||
assert.Equal(t, tt.all, hull.Length())
|
assert.Equal(t, tt.all, hull.Length())
|
||||||
|
|
|
@ -25,7 +25,7 @@ type KLinePusher interface {
|
||||||
// Simple is the simple indicator that only returns one float64 value
|
// Simple is the simple indicator that only returns one float64 value
|
||||||
type Simple interface {
|
type Simple interface {
|
||||||
KLinePusher
|
KLinePusher
|
||||||
Last() float64
|
Last(int) float64
|
||||||
OnUpdate(f func(value float64))
|
OnUpdate(f func(value float64))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package indicator
|
package indicator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/datatype/floats"
|
"github.com/c9s/bbgo/pkg/datatype/floats"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
"math"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Refer: https://www.kalmanfilter.net/kalman1d.html
|
// Refer: https://www.kalmanfilter.net/kalman1d.html
|
||||||
|
@ -25,7 +26,7 @@ type KalmanFilter struct {
|
||||||
func (inc *KalmanFilter) Update(value float64) {
|
func (inc *KalmanFilter) Update(value float64) {
|
||||||
var measureMove = value
|
var measureMove = value
|
||||||
if inc.measurements != nil {
|
if inc.measurements != nil {
|
||||||
measureMove = value - inc.measurements.Last()
|
measureMove = value - inc.measurements.Last(0)
|
||||||
}
|
}
|
||||||
inc.update(value, math.Abs(measureMove))
|
inc.update(value, math.Abs(measureMove))
|
||||||
}
|
}
|
||||||
|
@ -46,7 +47,7 @@ func (inc *KalmanFilter) update(value, amp float64) {
|
||||||
q := math.Sqrt(types.Mean(inc.amp2)) * float64(1+inc.AdditionalSmoothWindow)
|
q := math.Sqrt(types.Mean(inc.amp2)) * float64(1+inc.AdditionalSmoothWindow)
|
||||||
|
|
||||||
// update
|
// update
|
||||||
lastPredict := inc.Values.Last()
|
lastPredict := inc.Values.Last(0)
|
||||||
curState := value + (value - lastPredict)
|
curState := value + (value - lastPredict)
|
||||||
estimated := lastPredict + inc.k*(curState-lastPredict)
|
estimated := lastPredict + inc.k*(curState-lastPredict)
|
||||||
|
|
||||||
|
@ -57,24 +58,15 @@ func (inc *KalmanFilter) update(value, amp float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *KalmanFilter) Index(i int) float64 {
|
func (inc *KalmanFilter) Index(i int) float64 {
|
||||||
if inc.Values == nil {
|
return inc.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *KalmanFilter) Length() int {
|
func (inc *KalmanFilter) Length() int {
|
||||||
if inc.Values == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values.Length()
|
return inc.Values.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *KalmanFilter) Last() float64 {
|
func (inc *KalmanFilter) Last(i int) float64 {
|
||||||
if inc.Values == nil {
|
return inc.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values.Last()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// interfaces implementation check
|
// interfaces implementation check
|
||||||
|
|
|
@ -6065,7 +6065,7 @@ func Test_KalmanFilter(t *testing.T) {
|
||||||
for _, k := range klines {
|
for _, k := range klines {
|
||||||
filter.PushK(k)
|
filter.PushK(k)
|
||||||
}
|
}
|
||||||
got := filter.Last()
|
got := filter.Last(0)
|
||||||
got = math.Trunc(got*100.0) / 100.0
|
got = math.Trunc(got*100.0) / 100.0
|
||||||
if got != tt.want {
|
if got != tt.want {
|
||||||
t.Errorf("KalmanFilter.Last() = %v, want %v", got, tt.want)
|
t.Errorf("KalmanFilter.Last() = %v, want %v", got, tt.want)
|
||||||
|
@ -6160,10 +6160,10 @@ func Test_KalmanFilterEstimationAccurate(t *testing.T) {
|
||||||
for _, k := range klines {
|
for _, k := range klines {
|
||||||
// square error between last estimated state and current actual state
|
// square error between last estimated state and current actual state
|
||||||
if ewma.Length() > 0 {
|
if ewma.Length() > 0 {
|
||||||
filterDiff2Sum += klineSquareError(filter.Last(), k)
|
filterDiff2Sum += klineSquareError(filter.Last(0), k)
|
||||||
ewmaDiff2Sum += klineSquareError(ewma.Last(), k)
|
ewmaDiff2Sum += klineSquareError(ewma.Last(0), k)
|
||||||
filterCloseDiff2Sum += closeSquareError(filter.Last(), k)
|
filterCloseDiff2Sum += closeSquareError(filter.Last(0), k)
|
||||||
ewmaCloseDiff2Sum += closeSquareError(ewma.Last(), k)
|
ewmaCloseDiff2Sum += closeSquareError(ewma.Last(0), k)
|
||||||
numEstimations++
|
numEstimations++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
// The Klinger Oscillator is calculated by taking the difference between a 34-period and 55-period moving average.
|
// 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.
|
// 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.
|
// 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
|
//go:generate callbackgen -type KlingerOscillator
|
||||||
type KlingerOscillator struct {
|
type KlingerOscillator struct {
|
||||||
types.SeriesBase
|
types.SeriesBase
|
||||||
|
@ -30,17 +31,11 @@ func (inc *KlingerOscillator) Length() int {
|
||||||
return inc.Fast.Length()
|
return inc.Fast.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *KlingerOscillator) Last() float64 {
|
func (inc *KlingerOscillator) Last(i int) float64 {
|
||||||
if inc.Fast == nil || inc.Slow == nil {
|
if inc.Fast == nil || inc.Slow == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Fast.Last() - inc.Slow.Last()
|
return inc.Fast.Last(i) - inc.Slow.Last(i)
|
||||||
}
|
|
||||||
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) {
|
func (inc *KlingerOscillator) Update(high, low, cloze, volume float64) {
|
||||||
|
|
|
@ -38,12 +38,12 @@ func (l *Line) Bind(updater KLineWindowUpdater) {
|
||||||
updater.OnKLineWindowUpdate(l.handleKLineWindowUpdate)
|
updater.OnKLineWindowUpdate(l.handleKLineWindowUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Line) Last() float64 {
|
func (l *Line) Last(i int) float64 {
|
||||||
return (l.end-l.start)/float64(l.startIndex-l.endIndex)*float64(l.endIndex) + l.end
|
return (l.end-l.start)/float64(l.startIndex-l.endIndex)*float64(l.endIndex-i) + l.end
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Line) Index(i int) float64 {
|
func (l *Line) Index(i int) float64 {
|
||||||
return (l.end-l.start)/float64(l.startIndex-l.endIndex)*float64(l.endIndex-i) + l.end
|
return l.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Line) Length() int {
|
func (l *Line) Length() int {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package indicator
|
package indicator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/datatype/floats"
|
"github.com/c9s/bbgo/pkg/datatype/floats"
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
@ -11,6 +12,7 @@ import (
|
||||||
var logLinReg = logrus.WithField("indicator", "LinReg")
|
var logLinReg = logrus.WithField("indicator", "LinReg")
|
||||||
|
|
||||||
// LinReg is Linear Regression baseline
|
// LinReg is Linear Regression baseline
|
||||||
|
//
|
||||||
//go:generate callbackgen -type LinReg
|
//go:generate callbackgen -type LinReg
|
||||||
type LinReg struct {
|
type LinReg struct {
|
||||||
types.SeriesBase
|
types.SeriesBase
|
||||||
|
@ -28,11 +30,8 @@ type LinReg struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last slope of linear regression baseline
|
// Last slope of linear regression baseline
|
||||||
func (lr *LinReg) Last() float64 {
|
func (lr *LinReg) Last(i int) float64 {
|
||||||
if lr.Values.Length() == 0 {
|
return lr.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return lr.Values.Last()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastRatio of slope to price
|
// LastRatio of slope to price
|
||||||
|
@ -40,16 +39,12 @@ func (lr *LinReg) LastRatio() float64 {
|
||||||
if lr.ValueRatios.Length() == 0 {
|
if lr.ValueRatios.Length() == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
return lr.ValueRatios.Last()
|
return lr.ValueRatios.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Index returns the slope of specified index
|
// Index returns the slope of specified index
|
||||||
func (lr *LinReg) Index(i int) float64 {
|
func (lr *LinReg) Index(i int) float64 {
|
||||||
if i >= lr.Values.Length() {
|
return lr.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
return lr.Values.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IndexRatio returns the slope ratio
|
// IndexRatio returns the slope ratio
|
||||||
|
@ -58,7 +53,7 @@ func (lr *LinReg) IndexRatio(i int) float64 {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
return lr.ValueRatios.Index(i)
|
return lr.ValueRatios.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Length of the slope values
|
// Length of the slope values
|
||||||
|
@ -99,9 +94,9 @@ func (lr *LinReg) Update(kline types.KLine) {
|
||||||
endPrice := average - slope*sumX/length + slope
|
endPrice := average - slope*sumX/length + slope
|
||||||
startPrice := endPrice + slope*(length-1)
|
startPrice := endPrice + slope*(length-1)
|
||||||
lr.Values.Push((endPrice - startPrice) / (length - 1))
|
lr.Values.Push((endPrice - startPrice) / (length - 1))
|
||||||
lr.ValueRatios.Push(lr.Values.Last() / kline.GetClose().Float64())
|
lr.ValueRatios.Push(lr.Values.Last(0) / kline.GetClose().Float64())
|
||||||
|
|
||||||
logLinReg.Debugf("linear regression baseline slope: %f", lr.Last())
|
logLinReg.Debugf("linear regression baseline slope: %f", lr.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lr *LinReg) BindK(target KLineClosedEmitter, symbol string, interval types.Interval) {
|
func (lr *LinReg) BindK(target KLineClosedEmitter, symbol string, interval types.Interval) {
|
||||||
|
|
|
@ -33,5 +33,5 @@ func (inc *Low) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(k.Low.Float64())
|
inc.Update(k.Low.Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,14 +59,14 @@ func (inc *MACDLegacy) Update(x float64) {
|
||||||
inc.slowEWMA.Update(x)
|
inc.slowEWMA.Update(x)
|
||||||
|
|
||||||
// update MACD value, it's also the signal line
|
// update MACD value, it's also the signal line
|
||||||
fast := inc.fastEWMA.Last()
|
fast := inc.fastEWMA.Last(0)
|
||||||
slow := inc.slowEWMA.Last()
|
slow := inc.slowEWMA.Last(0)
|
||||||
macd := fast - slow
|
macd := fast - slow
|
||||||
inc.Values.Push(macd)
|
inc.Values.Push(macd)
|
||||||
|
|
||||||
// update signal line
|
// update signal line
|
||||||
inc.signalLine.Update(macd)
|
inc.signalLine.Update(macd)
|
||||||
signal := inc.signalLine.Last()
|
signal := inc.signalLine.Last(0)
|
||||||
|
|
||||||
// update histogram
|
// update histogram
|
||||||
histogram := macd - signal
|
histogram := macd - signal
|
||||||
|
@ -75,7 +75,7 @@ func (inc *MACDLegacy) Update(x float64) {
|
||||||
inc.EmitUpdate(macd, signal, histogram)
|
inc.EmitUpdate(macd, signal, histogram)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *MACDLegacy) Last() float64 {
|
func (inc *MACDLegacy) Last(int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
@ -106,21 +106,12 @@ type MACDValues struct {
|
||||||
*MACDLegacy
|
*MACDLegacy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *MACDValues) Last() float64 {
|
func (inc *MACDValues) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
return inc.Values[len(inc.Values)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *MACDValues) Index(i int) float64 {
|
func (inc *MACDValues) Index(i int) float64 {
|
||||||
length := len(inc.Values)
|
return inc.Values.Last(i)
|
||||||
if length == 0 || length-1-i < 0 {
|
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
return inc.Values[length-1+i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *MACDValues) Length() int {
|
func (inc *MACDValues) Length() int {
|
||||||
|
|
|
@ -45,7 +45,7 @@ func Test_calculateMACD(t *testing.T) {
|
||||||
macd.PushK(k)
|
macd.PushK(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
got := macd.Last()
|
got := macd.Last(0)
|
||||||
diff := math.Trunc((got-tt.want)*100) / 100
|
diff := math.Trunc((got-tt.want)*100) / 100
|
||||||
if diff != 0 {
|
if diff != 0 {
|
||||||
t.Errorf("calculateMACD() = %v, want %v", got, tt.want)
|
t.Errorf("calculateMACD() = %v, want %v", got, tt.want)
|
||||||
|
|
|
@ -40,24 +40,18 @@ func (inc *OBV) Update(price, volume float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if volume < inc.PrePrice {
|
if volume < inc.PrePrice {
|
||||||
inc.Values.Push(inc.Last() - volume)
|
inc.Values.Push(inc.Last(0) - volume)
|
||||||
} else {
|
} else {
|
||||||
inc.Values.Push(inc.Last() + volume)
|
inc.Values.Push(inc.Last(0) + volume)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *OBV) Last() float64 {
|
func (inc *OBV) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *OBV) Index(i int) float64 {
|
func (inc *OBV) Index(i int) float64 {
|
||||||
if len(inc.Values)-i <= 0 {
|
return inc.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-i-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.SeriesExtend = &OBV{}
|
var _ types.SeriesExtend = &OBV{}
|
||||||
|
@ -75,7 +69,7 @@ func (inc *OBV) CalculateAndUpdate(kLines []types.KLine) {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
//go:generate callbackgen -type Pivot
|
//go:generate callbackgen -type Pivot
|
||||||
type Pivot struct {
|
type Pivot struct {
|
||||||
types.IntervalWindow
|
types.IntervalWindow
|
||||||
|
@ -105,12 +104,12 @@ func calculatePivot(klines []types.KLine, window int, valLow KLineValueMapper, v
|
||||||
}
|
}
|
||||||
|
|
||||||
pl := 0.
|
pl := 0.
|
||||||
if lows.Min() == lows.Index(int(window/2.)-1) {
|
if lows.Min() == lows.Last(int(window/2.)-1) {
|
||||||
pl = lows.Min()
|
pl = lows.Min()
|
||||||
}
|
}
|
||||||
|
|
||||||
ph := 0.
|
ph := 0.
|
||||||
if highs.Max() == highs.Index(int(window/2.)-1) {
|
if highs.Max() == highs.Last(int(window/2.)-1) {
|
||||||
ph = highs.Max()
|
ph = highs.Max()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,12 @@ func (inc *PivotHigh) Length() int {
|
||||||
return inc.Values.Length()
|
return inc.Values.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PivotHigh) Last() float64 {
|
func (inc *PivotHigh) Last(int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PivotHigh) Update(value float64) {
|
func (inc *PivotHigh) Update(value float64) {
|
||||||
|
@ -60,5 +60,5 @@ func (inc *PivotHigh) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(k.High.Float64())
|
inc.Update(k.High.Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,12 @@ func (inc *PivotLow) Length() int {
|
||||||
return inc.Values.Length()
|
return inc.Values.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PivotLow) Last() float64 {
|
func (inc *PivotLow) Last(int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PivotLow) Update(value float64) {
|
func (inc *PivotLow) Update(value float64) {
|
||||||
|
@ -60,7 +60,7 @@ func (inc *PivotLow) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(k.Low.Float64())
|
inc.Update(k.Low.Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculatePivotHigh(highs floats.Slice, left, right int) (float64, bool) {
|
func calculatePivotHigh(highs floats.Slice, left, right int) (float64, bool) {
|
||||||
|
|
|
@ -34,11 +34,11 @@ type PSAR struct {
|
||||||
UpdateCallbacks []func(value float64)
|
UpdateCallbacks []func(value float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PSAR) Last() float64 {
|
func (inc *PSAR) Last(int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PSAR) Length() int {
|
func (inc *PSAR) Length() int {
|
||||||
|
@ -46,8 +46,8 @@ func (inc *PSAR) Length() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PSAR) falling() bool {
|
func (inc *PSAR) falling() bool {
|
||||||
up := inc.High.Last() - inc.High.Index(1)
|
up := inc.High.Last(0) - inc.High.Index(1)
|
||||||
dn := inc.Low.Index(1) - inc.Low.Last()
|
dn := inc.Low.Index(1) - inc.Low.Last(0)
|
||||||
return (dn > up) && (dn > 0)
|
return (dn > up) && (dn > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ func (inc *PSAR) Update(high, low float64) {
|
||||||
inc.High.Update(high)
|
inc.High.Update(high)
|
||||||
inc.Low.Update(low)
|
inc.Low.Update(low)
|
||||||
if !isFirst {
|
if !isFirst {
|
||||||
ppsar := inc.Values.Last()
|
ppsar := inc.Values.Last(0)
|
||||||
if inc.Falling { // falling formula
|
if inc.Falling { // falling formula
|
||||||
psar := ppsar - inc.AF*(ppsar-inc.EP)
|
psar := ppsar - inc.AF*(ppsar-inc.EP)
|
||||||
h := inc.High.Shift(1).Highest(2)
|
h := inc.High.Shift(1).Highest(2)
|
||||||
|
|
|
@ -36,5 +36,5 @@ func Test_PSAR(t *testing.T) {
|
||||||
}
|
}
|
||||||
assert.Equal(t, psar.Length(), 29)
|
assert.Equal(t, psar.Length(), 29)
|
||||||
assert.Equal(t, psar.AF, 0.04)
|
assert.Equal(t, psar.AF, 0.04)
|
||||||
assert.Equal(t, psar.Last(), 0.16)
|
assert.Equal(t, psar.Last(0), 0.16)
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,16 +78,12 @@ func (inc *RMA) Update(x float64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *RMA) Last() float64 {
|
func (inc *RMA) Last(i int) float64 {
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *RMA) Index(i int) float64 {
|
func (inc *RMA) Index(i int) float64 {
|
||||||
length := len(inc.Values)
|
return inc.Last(i)
|
||||||
if length == 0 || length-i-1 < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values[length-i-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *RMA) Length() int {
|
func (inc *RMA) Length() int {
|
||||||
|
@ -116,7 +112,7 @@ func (inc *RMA) CalculateAndUpdate(kLines []types.KLine) {
|
||||||
inc.PushK(last)
|
inc.PushK(last)
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *RMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
func (inc *RMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||||
|
|
|
@ -65,19 +65,12 @@ func (inc *RSI) Update(price float64) {
|
||||||
inc.PreviousAvgLoss = avgLoss
|
inc.PreviousAvgLoss = avgLoss
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *RSI) Last() float64 {
|
func (inc *RSI) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *RSI) Index(i int) float64 {
|
func (inc *RSI) Index(i int) float64 {
|
||||||
length := len(inc.Values)
|
return inc.Last(i)
|
||||||
if length <= 0 || length-i-1 < 0 {
|
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values[length-i-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *RSI) Length() int {
|
func (inc *RSI) Length() int {
|
||||||
|
@ -99,7 +92,7 @@ func (inc *RSI) CalculateAndUpdate(kLines []types.KLine) {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,19 +22,12 @@ type SMA struct {
|
||||||
UpdateCallbacks []func(value float64)
|
UpdateCallbacks []func(value float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *SMA) Last() float64 {
|
func (inc *SMA) Last(i int) float64 {
|
||||||
if inc.Values.Length() == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values.Last()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *SMA) Index(i int) float64 {
|
func (inc *SMA) Index(i int) float64 {
|
||||||
if i >= inc.Values.Length() {
|
return inc.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
return inc.Values.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *SMA) Length() int {
|
func (inc *SMA) Length() int {
|
||||||
|
@ -81,7 +74,7 @@ func (inc *SMA) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(k.Close.Float64())
|
inc.Update(k.Close.Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Values.Last())
|
inc.EmitUpdate(inc.Values.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *SMA) LoadK(allKLines []types.KLine) {
|
func (inc *SMA) LoadK(allKLines []types.KLine) {
|
||||||
|
|
|
@ -58,10 +58,10 @@ func Test_SMA(t *testing.T) {
|
||||||
sma.PushK(k)
|
sma.PushK(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.InDelta(t, tt.want, sma.Last(), Delta)
|
assert.InDelta(t, tt.want, sma.Last(0), Delta)
|
||||||
assert.InDelta(t, tt.next, sma.Index(1), Delta)
|
assert.InDelta(t, tt.next, sma.Index(1), Delta)
|
||||||
sma.Update(tt.update)
|
sma.Update(tt.update)
|
||||||
assert.InDelta(t, tt.updateResult, sma.Last(), Delta)
|
assert.InDelta(t, tt.updateResult, sma.Last(0), Delta)
|
||||||
assert.Equal(t, tt.all, sma.Length())
|
assert.Equal(t, tt.all, sma.Length())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,9 +50,9 @@ func (inc *SSF) Update(value float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
result := inc.c1*value +
|
result := inc.c1*value +
|
||||||
inc.c2*inc.Values.Index(0) +
|
inc.c2*inc.Values.Last(0) +
|
||||||
inc.c3*inc.Values.Index(1) +
|
inc.c3*inc.Values.Last(1) +
|
||||||
inc.c4*inc.Values.Index(2)
|
inc.c4*inc.Values.Last(2)
|
||||||
inc.Values.Push(result)
|
inc.Values.Push(result)
|
||||||
} else { // poles == 2
|
} else { // poles == 2
|
||||||
if inc.Values == nil {
|
if inc.Values == nil {
|
||||||
|
@ -65,17 +65,18 @@ func (inc *SSF) Update(value float64) {
|
||||||
inc.Values = floats.Slice{}
|
inc.Values = floats.Slice{}
|
||||||
}
|
}
|
||||||
result := inc.c1*value +
|
result := inc.c1*value +
|
||||||
inc.c2*inc.Values.Index(0) +
|
inc.c2*inc.Values.Last(0) +
|
||||||
inc.c3*inc.Values.Index(1)
|
inc.c3*inc.Values.Last(1)
|
||||||
inc.Values.Push(result)
|
inc.Values.Push(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (inc *SSF) Last(i int) float64 {
|
||||||
|
return inc.Values.Last(i)
|
||||||
|
}
|
||||||
|
|
||||||
func (inc *SSF) Index(i int) float64 {
|
func (inc *SSF) Index(i int) float64 {
|
||||||
if inc.Values == nil {
|
return inc.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *SSF) Length() int {
|
func (inc *SSF) Length() int {
|
||||||
|
@ -85,13 +86,6 @@ func (inc *SSF) Length() int {
|
||||||
return inc.Values.Length()
|
return inc.Values.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *SSF) Last() float64 {
|
|
||||||
if inc.Values == nil {
|
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values.Last()
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ types.SeriesExtend = &SSF{}
|
var _ types.SeriesExtend = &SSF{}
|
||||||
|
|
||||||
func (inc *SSF) PushK(k types.KLine) {
|
func (inc *SSF) PushK(k types.KLine) {
|
||||||
|
@ -102,12 +96,12 @@ func (inc *SSF) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.Values != nil {
|
if inc.Values != nil {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@ func Test_SSF(t *testing.T) {
|
||||||
Poles: tt.poles,
|
Poles: tt.poles,
|
||||||
}
|
}
|
||||||
ssf.CalculateAndUpdate(tt.kLines)
|
ssf.CalculateAndUpdate(tt.kLines)
|
||||||
assert.InDelta(t, tt.want, ssf.Last(), Delta)
|
assert.InDelta(t, tt.want, ssf.Last(0), Delta)
|
||||||
assert.InDelta(t, tt.next, ssf.Index(1), Delta)
|
assert.InDelta(t, tt.next, ssf.Index(1), Delta)
|
||||||
assert.Equal(t, tt.all, ssf.Length())
|
assert.Equal(t, tt.all, ssf.Length())
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,19 +21,12 @@ type StdDev struct {
|
||||||
updateCallbacks []func(value float64)
|
updateCallbacks []func(value float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *StdDev) Last() float64 {
|
func (inc *StdDev) Last(i int) float64 {
|
||||||
if inc.Values.Length() == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values.Last()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *StdDev) Index(i int) float64 {
|
func (inc *StdDev) Index(i int) float64 {
|
||||||
if i >= inc.Values.Length() {
|
return inc.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
return inc.Values.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *StdDev) Length() int {
|
func (inc *StdDev) Length() int {
|
||||||
|
@ -76,7 +69,7 @@ func (inc *StdDev) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
inc.PushK(last)
|
inc.PushK(last)
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.EmitUpdate(inc.Values.Last())
|
inc.EmitUpdate(inc.Values.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *StdDev) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
func (inc *StdDev) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||||
|
|
|
@ -50,16 +50,12 @@ type Supertrend struct {
|
||||||
UpdateCallbacks []func(value float64)
|
UpdateCallbacks []func(value float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Supertrend) Last() float64 {
|
func (inc *Supertrend) Last(i int) float64 {
|
||||||
return inc.trendPrices.Last()
|
return inc.trendPrices.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Supertrend) Index(i int) float64 {
|
func (inc *Supertrend) Index(i int) float64 {
|
||||||
length := inc.Length()
|
return inc.Last(i)
|
||||||
if length == 0 || length-i-1 < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.trendPrices[length-i-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Supertrend) Length() int {
|
func (inc *Supertrend) Length() int {
|
||||||
|
@ -94,13 +90,13 @@ func (inc *Supertrend) Update(highPrice, lowPrice, closePrice float64) {
|
||||||
src := (highPrice + lowPrice) / 2
|
src := (highPrice + lowPrice) / 2
|
||||||
|
|
||||||
// Update uptrend
|
// Update uptrend
|
||||||
inc.uptrendPrice = src - inc.AverageTrueRange.Last()*inc.ATRMultiplier
|
inc.uptrendPrice = src - inc.AverageTrueRange.Last(0)*inc.ATRMultiplier
|
||||||
if inc.previousClosePrice > inc.previousUptrendPrice {
|
if inc.previousClosePrice > inc.previousUptrendPrice {
|
||||||
inc.uptrendPrice = math.Max(inc.uptrendPrice, inc.previousUptrendPrice)
|
inc.uptrendPrice = math.Max(inc.uptrendPrice, inc.previousUptrendPrice)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update downtrend
|
// Update downtrend
|
||||||
inc.downtrendPrice = src + inc.AverageTrueRange.Last()*inc.ATRMultiplier
|
inc.downtrendPrice = src + inc.AverageTrueRange.Last(0)*inc.ATRMultiplier
|
||||||
if inc.previousClosePrice < inc.previousDowntrendPrice {
|
if inc.previousClosePrice < inc.previousDowntrendPrice {
|
||||||
inc.downtrendPrice = math.Min(inc.downtrendPrice, inc.previousDowntrendPrice)
|
inc.downtrendPrice = math.Min(inc.downtrendPrice, inc.previousDowntrendPrice)
|
||||||
}
|
}
|
||||||
|
@ -115,7 +111,7 @@ func (inc *Supertrend) Update(highPrice, lowPrice, closePrice float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update signal
|
// Update signal
|
||||||
if inc.AverageTrueRange.Last() <= 0 {
|
if inc.AverageTrueRange.Last(0) <= 0 {
|
||||||
inc.tradeSignal = types.DirectionNone
|
inc.tradeSignal = types.DirectionNone
|
||||||
} else if inc.trend == types.DirectionUp && inc.previousTrend == types.DirectionDown {
|
} else if inc.trend == types.DirectionUp && inc.previousTrend == types.DirectionDown {
|
||||||
inc.tradeSignal = types.DirectionUp
|
inc.tradeSignal = types.DirectionUp
|
||||||
|
@ -138,7 +134,7 @@ func (inc *Supertrend) Update(highPrice, lowPrice, closePrice float64) {
|
||||||
|
|
||||||
logst.Debugf("Update supertrend result: closePrice: %v, uptrendPrice: %v, downtrendPrice: %v, trend: %v,"+
|
logst.Debugf("Update supertrend result: closePrice: %v, uptrendPrice: %v, downtrendPrice: %v, trend: %v,"+
|
||||||
" tradeSignal: %v, AverageTrueRange.Last(): %v", inc.closePrice, inc.uptrendPrice, inc.downtrendPrice,
|
" tradeSignal: %v, AverageTrueRange.Last(): %v", inc.closePrice, inc.uptrendPrice, inc.downtrendPrice,
|
||||||
inc.trend, inc.tradeSignal, inc.AverageTrueRange.Last())
|
inc.trend, inc.tradeSignal, inc.AverageTrueRange.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Supertrend) GetSignal() types.Direction {
|
func (inc *Supertrend) GetSignal() types.Direction {
|
||||||
|
@ -152,12 +148,12 @@ func (inc *Supertrend) Direction() types.Direction {
|
||||||
|
|
||||||
// LastSupertrendSupport return the current supertrend support
|
// LastSupertrendSupport return the current supertrend support
|
||||||
func (inc *Supertrend) LastSupertrendSupport() float64 {
|
func (inc *Supertrend) LastSupertrendSupport() float64 {
|
||||||
return inc.supportLine.Last()
|
return inc.supportLine.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastSupertrendResistance return the current supertrend resistance
|
// LastSupertrendResistance return the current supertrend resistance
|
||||||
func (inc *Supertrend) LastSupertrendResistance() float64 {
|
func (inc *Supertrend) LastSupertrendResistance() float64 {
|
||||||
return inc.resistanceLine.Last()
|
return inc.resistanceLine.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.SeriesExtend = &Supertrend{}
|
var _ types.SeriesExtend = &Supertrend{}
|
||||||
|
@ -169,7 +165,7 @@ func (inc *Supertrend) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(k.GetHigh().Float64(), k.GetLow().Float64(), k.GetClose().Float64())
|
inc.Update(k.GetHigh().Float64(), k.GetLow().Float64(), k.GetClose().Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +188,7 @@ func (inc *Supertrend) CalculateAndUpdate(kLines []types.KLine) {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,16 +50,12 @@ type PivotSupertrend struct {
|
||||||
UpdateCallbacks []func(value float64)
|
UpdateCallbacks []func(value float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PivotSupertrend) Last() float64 {
|
func (inc *PivotSupertrend) Last(i int) float64 {
|
||||||
return inc.trendPrices.Last()
|
return inc.trendPrices.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PivotSupertrend) Index(i int) float64 {
|
func (inc *PivotSupertrend) Index(i int) float64 {
|
||||||
length := inc.Length()
|
return inc.Last(i)
|
||||||
if length == 0 || length-i-1 < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.trendPrices[length-i-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PivotSupertrend) Length() int {
|
func (inc *PivotSupertrend) Length() int {
|
||||||
|
@ -80,8 +76,8 @@ func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
|
||||||
inc.trend = types.DirectionUp
|
inc.trend = types.DirectionUp
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.previousPivotLow = inc.PivotLow.Last()
|
inc.previousPivotLow = inc.PivotLow.Last(0)
|
||||||
inc.previousPivotHigh = inc.PivotHigh.Last()
|
inc.previousPivotHigh = inc.PivotHigh.Last(0)
|
||||||
|
|
||||||
// Update High / Low pivots
|
// Update High / Low pivots
|
||||||
inc.PivotLow.Update(lowPrice)
|
inc.PivotLow.Update(lowPrice)
|
||||||
|
@ -101,9 +97,9 @@ func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
|
||||||
// Initialize lastPp as soon as pivots are made
|
// Initialize lastPp as soon as pivots are made
|
||||||
if inc.lastPp == 0 || math.IsNaN(inc.lastPp) {
|
if inc.lastPp == 0 || math.IsNaN(inc.lastPp) {
|
||||||
if inc.PivotHigh.Length() > 0 {
|
if inc.PivotHigh.Length() > 0 {
|
||||||
inc.lastPp = inc.PivotHigh.Last()
|
inc.lastPp = inc.PivotHigh.Last(0)
|
||||||
} else if inc.PivotLow.Length() > 0 {
|
} else if inc.PivotLow.Length() > 0 {
|
||||||
inc.lastPp = inc.PivotLow.Last()
|
inc.lastPp = inc.PivotLow.Last(0)
|
||||||
} else {
|
} else {
|
||||||
inc.lastPp = math.NaN()
|
inc.lastPp = math.NaN()
|
||||||
return
|
return
|
||||||
|
@ -111,28 +107,28 @@ func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set lastPp to the latest pivotPoint (only changed when new pivot is found)
|
// Set lastPp to the latest pivotPoint (only changed when new pivot is found)
|
||||||
if inc.PivotHigh.Last() != inc.previousPivotHigh {
|
if inc.PivotHigh.Last(0) != inc.previousPivotHigh {
|
||||||
inc.lastPp = inc.PivotHigh.Last()
|
inc.lastPp = inc.PivotHigh.Last(0)
|
||||||
} else if inc.PivotLow.Last() != inc.previousPivotLow {
|
} else if inc.PivotLow.Last(0) != inc.previousPivotLow {
|
||||||
inc.lastPp = inc.PivotLow.Last()
|
inc.lastPp = inc.PivotLow.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate the Center line using pivot points
|
// calculate the Center line using pivot points
|
||||||
if inc.src == 0 || math.IsNaN(inc.src) {
|
if inc.src == 0 || math.IsNaN(inc.src) {
|
||||||
inc.src = inc.lastPp
|
inc.src = inc.lastPp
|
||||||
} else {
|
} else {
|
||||||
//weighted calculation
|
// weighted calculation
|
||||||
inc.src = (inc.src*2 + inc.lastPp) / 3
|
inc.src = (inc.src*2 + inc.lastPp) / 3
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update uptrend
|
// Update uptrend
|
||||||
inc.uptrendPrice = inc.src - inc.AverageTrueRange.Last()*inc.ATRMultiplier
|
inc.uptrendPrice = inc.src - inc.AverageTrueRange.Last(0)*inc.ATRMultiplier
|
||||||
if inc.previousClosePrice > inc.previousUptrendPrice {
|
if inc.previousClosePrice > inc.previousUptrendPrice {
|
||||||
inc.uptrendPrice = math.Max(inc.uptrendPrice, inc.previousUptrendPrice)
|
inc.uptrendPrice = math.Max(inc.uptrendPrice, inc.previousUptrendPrice)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update downtrend
|
// Update downtrend
|
||||||
inc.downtrendPrice = inc.src + inc.AverageTrueRange.Last()*inc.ATRMultiplier
|
inc.downtrendPrice = inc.src + inc.AverageTrueRange.Last(0)*inc.ATRMultiplier
|
||||||
if inc.previousClosePrice < inc.previousDowntrendPrice {
|
if inc.previousClosePrice < inc.previousDowntrendPrice {
|
||||||
inc.downtrendPrice = math.Min(inc.downtrendPrice, inc.previousDowntrendPrice)
|
inc.downtrendPrice = math.Min(inc.downtrendPrice, inc.previousDowntrendPrice)
|
||||||
}
|
}
|
||||||
|
@ -147,7 +143,7 @@ func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update signal
|
// Update signal
|
||||||
if inc.AverageTrueRange.Last() <= 0 {
|
if inc.AverageTrueRange.Last(0) <= 0 {
|
||||||
inc.tradeSignal = types.DirectionNone
|
inc.tradeSignal = types.DirectionNone
|
||||||
} else if inc.trend == types.DirectionUp && inc.previousTrend == types.DirectionDown {
|
} else if inc.trend == types.DirectionUp && inc.previousTrend == types.DirectionDown {
|
||||||
inc.tradeSignal = types.DirectionUp
|
inc.tradeSignal = types.DirectionUp
|
||||||
|
@ -170,7 +166,7 @@ func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
|
||||||
|
|
||||||
logpst.Debugf("Update pivot point supertrend result: closePrice: %v, uptrendPrice: %v, downtrendPrice: %v, trend: %v,"+
|
logpst.Debugf("Update pivot point supertrend result: closePrice: %v, uptrendPrice: %v, downtrendPrice: %v, trend: %v,"+
|
||||||
" tradeSignal: %v, AverageTrueRange.Last(): %v", inc.closePrice, inc.uptrendPrice, inc.downtrendPrice,
|
" tradeSignal: %v, AverageTrueRange.Last(): %v", inc.closePrice, inc.uptrendPrice, inc.downtrendPrice,
|
||||||
inc.trend, inc.tradeSignal, inc.AverageTrueRange.Last())
|
inc.trend, inc.tradeSignal, inc.AverageTrueRange.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSignal returns signal (Down, None or Up)
|
// GetSignal returns signal (Down, None or Up)
|
||||||
|
@ -185,12 +181,12 @@ func (inc *PivotSupertrend) Direction() types.Direction {
|
||||||
|
|
||||||
// LastSupertrendSupport return the current supertrend support value
|
// LastSupertrendSupport return the current supertrend support value
|
||||||
func (inc *PivotSupertrend) LastSupertrendSupport() float64 {
|
func (inc *PivotSupertrend) LastSupertrendSupport() float64 {
|
||||||
return inc.supportLine.Last()
|
return inc.supportLine.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastSupertrendResistance return the current supertrend resistance value
|
// LastSupertrendResistance return the current supertrend resistance value
|
||||||
func (inc *PivotSupertrend) LastSupertrendResistance() float64 {
|
func (inc *PivotSupertrend) LastSupertrendResistance() float64 {
|
||||||
return inc.resistanceLine.Last()
|
return inc.resistanceLine.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ types.SeriesExtend = &PivotSupertrend{}
|
var _ types.SeriesExtend = &PivotSupertrend{}
|
||||||
|
@ -202,7 +198,7 @@ func (inc *PivotSupertrend) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(k.GetHigh().Float64(), k.GetLow().Float64(), k.GetClose().Float64())
|
inc.Update(k.GetHigh().Float64(), k.GetLow().Float64(), k.GetClose().Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PivotSupertrend) BindK(target KLineClosedEmitter, symbol string, interval types.Interval) {
|
func (inc *PivotSupertrend) BindK(target KLineClosedEmitter, symbol string, interval types.Interval) {
|
||||||
|
|
|
@ -36,26 +36,20 @@ func (inc *TEMA) Update(value float64) {
|
||||||
inc.A3 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
inc.A3 = &EWMA{IntervalWindow: inc.IntervalWindow}
|
||||||
}
|
}
|
||||||
inc.A1.Update(value)
|
inc.A1.Update(value)
|
||||||
a1 := inc.A1.Last()
|
a1 := inc.A1.Last(0)
|
||||||
inc.A2.Update(a1)
|
inc.A2.Update(a1)
|
||||||
a2 := inc.A2.Last()
|
a2 := inc.A2.Last(0)
|
||||||
inc.A3.Update(a2)
|
inc.A3.Update(a2)
|
||||||
a3 := inc.A3.Last()
|
a3 := inc.A3.Last(0)
|
||||||
inc.Values.Push(3*a1 - 3*a2 + a3)
|
inc.Values.Push(3*a1 - 3*a2 + a3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TEMA) Last() float64 {
|
func (inc *TEMA) Last(i int) float64 {
|
||||||
if len(inc.Values) > 0 {
|
return inc.Values.Last(i)
|
||||||
return inc.Values[len(inc.Values)-1]
|
|
||||||
}
|
|
||||||
return 0.0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TEMA) Index(i int) float64 {
|
func (inc *TEMA) Index(i int) float64 {
|
||||||
if i >= len(inc.Values) {
|
return inc.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-i-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TEMA) Length() int {
|
func (inc *TEMA) Length() int {
|
||||||
|
@ -72,12 +66,12 @@ func (inc *TEMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.A1 == nil {
|
if inc.A1 == nil {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ func Test_TEMA(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tema := TEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
tema := TEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
||||||
tema.CalculateAndUpdate(tt.kLines)
|
tema.CalculateAndUpdate(tt.kLines)
|
||||||
last := tema.Last()
|
last := tema.Last(0)
|
||||||
assert.InDelta(t, tt.want, last, Delta)
|
assert.InDelta(t, tt.want, last, Delta)
|
||||||
assert.InDelta(t, tt.next, tema.Index(1), Delta)
|
assert.InDelta(t, tt.next, tema.Index(1), Delta)
|
||||||
assert.Equal(t, tt.all, tema.Length())
|
assert.Equal(t, tt.all, tema.Length())
|
||||||
|
|
|
@ -57,28 +57,18 @@ func (inc *TILL) Update(value float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.e1.Update(value)
|
inc.e1.Update(value)
|
||||||
inc.e2.Update(inc.e1.Last())
|
inc.e2.Update(inc.e1.Last(0))
|
||||||
inc.e3.Update(inc.e2.Last())
|
inc.e3.Update(inc.e2.Last(0))
|
||||||
inc.e4.Update(inc.e3.Last())
|
inc.e4.Update(inc.e3.Last(0))
|
||||||
inc.e5.Update(inc.e4.Last())
|
inc.e5.Update(inc.e4.Last(0))
|
||||||
inc.e6.Update(inc.e5.Last())
|
inc.e6.Update(inc.e5.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TILL) Last() float64 {
|
func (inc *TILL) Last(i int) float64 {
|
||||||
if inc.e1 == nil || inc.e1.Length() == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
e3 := inc.e3.Last()
|
|
||||||
e4 := inc.e4.Last()
|
|
||||||
e5 := inc.e5.Last()
|
|
||||||
e6 := inc.e6.Last()
|
|
||||||
return inc.c1*e6 + inc.c2*e5 + inc.c3*e4 + inc.c4*e3
|
|
||||||
}
|
|
||||||
|
|
||||||
func (inc *TILL) Index(i int) float64 {
|
|
||||||
if inc.e1 == nil || inc.e1.Length() <= i {
|
if inc.e1 == nil || inc.e1.Length() <= i {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
e3 := inc.e3.Index(i)
|
e3 := inc.e3.Index(i)
|
||||||
e4 := inc.e4.Index(i)
|
e4 := inc.e4.Index(i)
|
||||||
e5 := inc.e5.Index(i)
|
e5 := inc.e5.Index(i)
|
||||||
|
@ -86,6 +76,10 @@ func (inc *TILL) Index(i int) float64 {
|
||||||
return inc.c1*e6 + inc.c2*e5 + inc.c3*e4 + inc.c4*e3
|
return inc.c1*e6 + inc.c2*e5 + inc.c3*e4 + inc.c4*e3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (inc *TILL) Index(i int) float64 {
|
||||||
|
return inc.Last(i)
|
||||||
|
}
|
||||||
|
|
||||||
func (inc *TILL) Length() int {
|
func (inc *TILL) Length() int {
|
||||||
if inc.e1 == nil {
|
if inc.e1 == nil {
|
||||||
return 0
|
return 0
|
||||||
|
@ -101,7 +95,7 @@ func (inc *TILL) PushK(k types.KLine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.Update(k.Close.Float64())
|
inc.Update(k.Close.Float64())
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TILL) LoadK(allKLines []types.KLine) {
|
func (inc *TILL) LoadK(allKLines []types.KLine) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ func Test_TILL(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
till := TILL{IntervalWindow: types.IntervalWindow{Window: 16}}
|
till := TILL{IntervalWindow: types.IntervalWindow{Window: 16}}
|
||||||
till.CalculateAndUpdate(tt.kLines)
|
till.CalculateAndUpdate(tt.kLines)
|
||||||
last := till.Last()
|
last := till.Last(0)
|
||||||
assert.InDelta(t, tt.want, last, Delta)
|
assert.InDelta(t, tt.want, last, Delta)
|
||||||
assert.InDelta(t, tt.next, till.Index(1), Delta)
|
assert.InDelta(t, tt.next, till.Index(1), Delta)
|
||||||
assert.Equal(t, tt.all, till.Length())
|
assert.Equal(t, tt.all, till.Length())
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
// Refer: Triangular Moving Average
|
// Refer: Triangular Moving Average
|
||||||
// Refer URL: https://ja.wikipedia.org/wiki/移動平均
|
// Refer URL: https://ja.wikipedia.org/wiki/移動平均
|
||||||
|
//
|
||||||
//go:generate callbackgen -type TMA
|
//go:generate callbackgen -type TMA
|
||||||
type TMA struct {
|
type TMA struct {
|
||||||
types.SeriesBase
|
types.SeriesBase
|
||||||
|
@ -24,21 +25,15 @@ func (inc *TMA) Update(value float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.s1.Update(value)
|
inc.s1.Update(value)
|
||||||
inc.s2.Update(inc.s1.Last())
|
inc.s2.Update(inc.s1.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TMA) Last() float64 {
|
func (inc *TMA) Last(i int) float64 {
|
||||||
if inc.s2 == nil {
|
return inc.s2.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.s2.Last()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TMA) Index(i int) float64 {
|
func (inc *TMA) Index(i int) float64 {
|
||||||
if inc.s2 == nil {
|
return inc.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.s2.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TMA) Length() int {
|
func (inc *TMA) Length() int {
|
||||||
|
@ -58,12 +53,12 @@ func (inc *TMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.s1 == nil {
|
if inc.s1 == nil {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
// Refer: True Strength Index
|
// Refer: True Strength Index
|
||||||
// Refer URL: https://www.investopedia.com/terms/t/tsi.asp
|
// Refer URL: https://www.investopedia.com/terms/t/tsi.asp
|
||||||
|
//
|
||||||
//go:generate callbackgen -type TSI
|
//go:generate callbackgen -type TSI
|
||||||
type TSI struct {
|
type TSI struct {
|
||||||
types.SeriesBase
|
types.SeriesBase
|
||||||
|
@ -66,10 +67,10 @@ func (inc *TSI) Update(value float64) {
|
||||||
apc := math.Abs(pc)
|
apc := math.Abs(pc)
|
||||||
inc.Apcs.Update(apc)
|
inc.Apcs.Update(apc)
|
||||||
|
|
||||||
inc.Pcds.Update(inc.Pcs.Last())
|
inc.Pcds.Update(inc.Pcs.Last(0))
|
||||||
inc.Apcds.Update(inc.Apcs.Last())
|
inc.Apcds.Update(inc.Apcs.Last(0))
|
||||||
|
|
||||||
tsi := (inc.Pcds.Last() / inc.Apcds.Last()) * 100.
|
tsi := (inc.Pcds.Last(0) / inc.Apcds.Last(0)) * 100.
|
||||||
inc.Values.Push(tsi)
|
inc.Values.Push(tsi)
|
||||||
if inc.Values.Length() > MaxNumOfEWMA {
|
if inc.Values.Length() > MaxNumOfEWMA {
|
||||||
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
|
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
|
||||||
|
@ -80,12 +81,12 @@ func (inc *TSI) Length() int {
|
||||||
return inc.Values.Length()
|
return inc.Values.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TSI) Last() float64 {
|
func (inc *TSI) Last(i int) float64 {
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TSI) Index(i int) float64 {
|
func (inc *TSI) Index(i int) float64 {
|
||||||
return inc.Values.Index(i)
|
return inc.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *TSI) PushK(k types.KLine) {
|
func (inc *TSI) PushK(k types.KLine) {
|
||||||
|
|
|
@ -32,5 +32,5 @@ func Test_TSI(t *testing.T) {
|
||||||
}
|
}
|
||||||
assert.Equal(t, tsi.Length(), 29)
|
assert.Equal(t, tsi.Length(), 29)
|
||||||
Delta := 1.5e-2
|
Delta := 1.5e-2
|
||||||
assert.InDelta(t, tsi.Last(), 22.89, Delta)
|
assert.InDelta(t, tsi.Last(0), 22.89, Delta)
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,20 +70,20 @@ func (inc *UtBotAlert) Update(highPrice, lowPrice, closePrice float64) {
|
||||||
// Update ATR
|
// Update ATR
|
||||||
inc.AverageTrueRange.Update(highPrice, lowPrice, closePrice)
|
inc.AverageTrueRange.Update(highPrice, lowPrice, closePrice)
|
||||||
|
|
||||||
nLoss := inc.AverageTrueRange.Last() * inc.KeyValue
|
nLoss := inc.AverageTrueRange.Last(0) * inc.KeyValue
|
||||||
|
|
||||||
// xATRTrailingStop
|
// xATRTrailingStop
|
||||||
if inc.xATRTrailingStop.Length() == 0 {
|
if inc.xATRTrailingStop.Length() == 0 {
|
||||||
// For first run
|
// For first run
|
||||||
inc.xATRTrailingStop.Update(0)
|
inc.xATRTrailingStop.Update(0)
|
||||||
|
|
||||||
} else if closePrice > inc.xATRTrailingStop.Index(1) && inc.previousClosePrice > inc.xATRTrailingStop.Index(1) {
|
} else if closePrice > inc.xATRTrailingStop.Last(1) && inc.previousClosePrice > inc.xATRTrailingStop.Last(1) {
|
||||||
inc.xATRTrailingStop.Update(math.Max(inc.xATRTrailingStop.Index(1), closePrice-nLoss))
|
inc.xATRTrailingStop.Update(math.Max(inc.xATRTrailingStop.Last(1), closePrice-nLoss))
|
||||||
|
|
||||||
} else if closePrice < inc.xATRTrailingStop.Index(1) && inc.previousClosePrice < inc.xATRTrailingStop.Index(1) {
|
} else if closePrice < inc.xATRTrailingStop.Last(1) && inc.previousClosePrice < inc.xATRTrailingStop.Last(1) {
|
||||||
inc.xATRTrailingStop.Update(math.Min(inc.xATRTrailingStop.Index(1), closePrice+nLoss))
|
inc.xATRTrailingStop.Update(math.Min(inc.xATRTrailingStop.Last(1), closePrice+nLoss))
|
||||||
|
|
||||||
} else if closePrice > inc.xATRTrailingStop.Index(1) {
|
} else if closePrice > inc.xATRTrailingStop.Last(1) {
|
||||||
inc.xATRTrailingStop.Update(closePrice - nLoss)
|
inc.xATRTrailingStop.Update(closePrice - nLoss)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -91,19 +91,19 @@ func (inc *UtBotAlert) Update(highPrice, lowPrice, closePrice float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// pos
|
// pos
|
||||||
if inc.previousClosePrice < inc.xATRTrailingStop.Index(1) && closePrice > inc.xATRTrailingStop.Index(1) {
|
if inc.previousClosePrice < inc.xATRTrailingStop.Last(1) && closePrice > inc.xATRTrailingStop.Last(1) {
|
||||||
inc.pos = types.DirectionUp
|
inc.pos = types.DirectionUp
|
||||||
} else if inc.previousClosePrice > inc.xATRTrailingStop.Index(1) && closePrice < inc.xATRTrailingStop.Index(1) {
|
} else if inc.previousClosePrice > inc.xATRTrailingStop.Last(1) && closePrice < inc.xATRTrailingStop.Last(1) {
|
||||||
inc.pos = types.DirectionDown
|
inc.pos = types.DirectionDown
|
||||||
} else {
|
} else {
|
||||||
inc.pos = inc.previousPos
|
inc.pos = inc.previousPos
|
||||||
}
|
}
|
||||||
|
|
||||||
above := closePrice > inc.xATRTrailingStop.Last() && inc.previousClosePrice < inc.xATRTrailingStop.Index(1)
|
above := closePrice > inc.xATRTrailingStop.Last(0) && inc.previousClosePrice < inc.xATRTrailingStop.Last(1)
|
||||||
below := closePrice < inc.xATRTrailingStop.Last() && inc.previousClosePrice > inc.xATRTrailingStop.Index(1)
|
below := closePrice < inc.xATRTrailingStop.Last(0) && inc.previousClosePrice > inc.xATRTrailingStop.Last(1)
|
||||||
|
|
||||||
buy := closePrice > inc.xATRTrailingStop.Last() && above // buy
|
buy := closePrice > inc.xATRTrailingStop.Last(0) && above // buy
|
||||||
sell := closePrice < inc.xATRTrailingStop.Last() && below // sell
|
sell := closePrice < inc.xATRTrailingStop.Last(0) && below // sell
|
||||||
|
|
||||||
inc.buyValue.Push(buy)
|
inc.buyValue.Push(buy)
|
||||||
inc.sellValue.Push(sell)
|
inc.sellValue.Push(sell)
|
||||||
|
|
|
@ -71,7 +71,7 @@ func Test_ATR2(t *testing.T) {
|
||||||
stream.EmitKLineClosed(k)
|
stream.EmitKLineClosed(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
got := atr.Last()
|
got := atr.Last(0)
|
||||||
diff := math.Trunc((got-tt.want)*100) / 100
|
diff := math.Trunc((got-tt.want)*100) / 100
|
||||||
if diff != 0 {
|
if diff != 0 {
|
||||||
t.Errorf("ATR2() = %v, want %v", got, tt.want)
|
t.Errorf("ATR2() = %v, want %v", got, tt.want)
|
||||||
|
|
|
@ -30,7 +30,7 @@ func (s *EWMAStream) calculateAndPush(v float64) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EWMAStream) calculate(v float64) float64 {
|
func (s *EWMAStream) calculate(v float64) float64 {
|
||||||
last := s.slice.Last()
|
last := s.slice.Last(0)
|
||||||
m := s.multiplier
|
m := s.multiplier
|
||||||
return (1.0-m)*last + m*v
|
return (1.0-m)*last + m*v
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@ func (s *RSIStream) calculate(_ float64) float64 {
|
||||||
var sourceLen = s.source.Length()
|
var sourceLen = s.source.Length()
|
||||||
var limit = min(s.window, sourceLen)
|
var limit = min(s.window, sourceLen)
|
||||||
for i := 0; i < limit; i++ {
|
for i := 0; i < limit; i++ {
|
||||||
value := s.source.Index(i)
|
value := s.source.Last(i)
|
||||||
prev := s.source.Index(i + 1)
|
prev := s.source.Last(i + 1)
|
||||||
change := value - prev
|
change := value - prev
|
||||||
if change >= 0 {
|
if change >= 0 {
|
||||||
gainSum += change
|
gainSum += change
|
||||||
|
|
|
@ -72,7 +72,7 @@ func Test_TR_and_RMA(t *testing.T) {
|
||||||
stream.EmitKLineClosed(k)
|
stream.EmitKLineClosed(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
got := rma.Last()
|
got := rma.Last(0)
|
||||||
diff := math.Trunc((got-tt.want)*100) / 100
|
diff := math.Trunc((got-tt.want)*100) / 100
|
||||||
if diff != 0 {
|
if diff != 0 {
|
||||||
t.Errorf("RMA(TR()) = %v, want %v", got, tt.want)
|
t.Errorf("RMA(TR()) = %v, want %v", got, tt.want)
|
||||||
|
|
|
@ -58,18 +58,18 @@ func (inc *VIDYA) Update(value float64) {
|
||||||
change := types.Change(&inc.input)
|
change := types.Change(&inc.input)
|
||||||
CMO := math.Abs(types.Sum(change, inc.Window) / types.Sum(types.Abs(change), inc.Window))
|
CMO := math.Abs(types.Sum(change, inc.Window) / types.Sum(types.Abs(change), inc.Window))
|
||||||
alpha := 2. / float64(inc.Window+1)
|
alpha := 2. / float64(inc.Window+1)
|
||||||
inc.Values.Push(value*alpha*CMO + inc.Values.Last()*(1.-alpha*CMO))
|
inc.Values.Push(value*alpha*CMO + inc.Values.Last(0)*(1.-alpha*CMO))
|
||||||
if inc.Values.Length() > MaxNumOfEWMA {
|
if inc.Values.Length() > MaxNumOfEWMA {
|
||||||
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
|
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VIDYA) Last() float64 {
|
func (inc *VIDYA) Last(i int) float64 {
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VIDYA) Index(i int) float64 {
|
func (inc *VIDYA) Index(i int) float64 {
|
||||||
return inc.Values.Index(i)
|
return inc.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VIDYA) Length() int {
|
func (inc *VIDYA) Length() int {
|
||||||
|
@ -86,12 +86,12 @@ func (inc *VIDYA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.input.Length() == 0 {
|
if inc.input.Length() == 0 {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,10 @@ import (
|
||||||
func Test_VIDYA(t *testing.T) {
|
func Test_VIDYA(t *testing.T) {
|
||||||
vidya := &VIDYA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
vidya := &VIDYA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
||||||
vidya.Update(1)
|
vidya.Update(1)
|
||||||
assert.Equal(t, vidya.Last(), 1.)
|
assert.Equal(t, vidya.Last(0), 1.)
|
||||||
vidya.Update(2)
|
vidya.Update(2)
|
||||||
newV := 2./17.*2. + 1.*(1.-2./17.)
|
newV := 2./17.*2. + 1.*(1.-2./17.)
|
||||||
assert.Equal(t, vidya.Last(), newV)
|
assert.Equal(t, vidya.Last(0), newV)
|
||||||
vidya.Update(1)
|
vidya.Update(1)
|
||||||
assert.Equal(t, vidya.Last(), vidya.Index(1))
|
assert.Equal(t, vidya.Last(0), vidya.Index(1))
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,18 +26,12 @@ type Volatility struct {
|
||||||
UpdateCallbacks []func(value float64)
|
UpdateCallbacks []func(value float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Volatility) Last() float64 {
|
func (inc *Volatility) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Volatility) Index(i int) float64 {
|
func (inc *Volatility) Index(i int) float64 {
|
||||||
if len(inc.Values)-i <= 0 {
|
return inc.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-i-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *Volatility) Length() int {
|
func (inc *Volatility) Length() int {
|
||||||
|
|
|
@ -56,20 +56,12 @@ func (inc *VWAP) Update(price, volume float64) {
|
||||||
inc.Values.Push(vwap)
|
inc.Values.Push(vwap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWAP) Last() float64 {
|
func (inc *VWAP) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWAP) Index(i int) float64 {
|
func (inc *VWAP) Index(i int) float64 {
|
||||||
length := len(inc.Values)
|
return inc.Last(i)
|
||||||
if length == 0 || length-i-1 < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return inc.Values[length-i-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWAP) Length() int {
|
func (inc *VWAP) Length() int {
|
||||||
|
@ -91,7 +83,7 @@ func (inc *VWAP) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
inc.EndTime = allKLines[len(allKLines)-1].EndTime.Time()
|
inc.EndTime = allKLines[len(allKLines)-1].EndTime.Time()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,5 +104,5 @@ func calculateVWAP(klines []types.KLine, priceF KLineValueMapper, window int) fl
|
||||||
for _, k := range klines {
|
for _, k := range klines {
|
||||||
vwap.Update(priceF(k), k.Volume.Float64())
|
vwap.Update(priceF(k), k.Volume.Float64())
|
||||||
}
|
}
|
||||||
return vwap.Last()
|
return vwap.Last(0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
// VWMA = SMA(pv, window) / SMA(volumes, window)
|
// VWMA = SMA(pv, window) / SMA(volumes, window)
|
||||||
//
|
//
|
||||||
// Volume Weighted Moving Average
|
// Volume Weighted Moving Average
|
||||||
//- https://www.motivewave.com/studies/volume_weighted_moving_average.htm
|
// - https://www.motivewave.com/studies/volume_weighted_moving_average.htm
|
||||||
//
|
//
|
||||||
// The Volume Weighted Moving Average (VWMA) is a technical analysis indicator that is used to smooth price data and reduce the lag
|
// The Volume Weighted Moving Average (VWMA) is a technical analysis indicator that is used to smooth price data and reduce the lag
|
||||||
// associated with traditional moving averages. It is calculated by taking the weighted moving average of the input data, with the
|
// associated with traditional moving averages. It is calculated by taking the weighted moving average of the input data, with the
|
||||||
|
@ -36,19 +36,12 @@ type VWMA struct {
|
||||||
updateCallbacks []func(value float64)
|
updateCallbacks []func(value float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWMA) Last() float64 {
|
func (inc *VWMA) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
return inc.Values[len(inc.Values)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWMA) Index(i int) float64 {
|
func (inc *VWMA) Index(i int) float64 {
|
||||||
length := len(inc.Values)
|
return inc.Last(i)
|
||||||
if length == 0 || length-i-1 < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values[length-i-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWMA) Length() int {
|
func (inc *VWMA) Length() int {
|
||||||
|
@ -70,8 +63,8 @@ func (inc *VWMA) Update(price, volume float64) {
|
||||||
inc.PriceVolumeSMA.Update(price * volume)
|
inc.PriceVolumeSMA.Update(price * volume)
|
||||||
inc.VolumeSMA.Update(volume)
|
inc.VolumeSMA.Update(volume)
|
||||||
|
|
||||||
pv := inc.PriceVolumeSMA.Last()
|
pv := inc.PriceVolumeSMA.Last(0)
|
||||||
v := inc.VolumeSMA.Last()
|
v := inc.VolumeSMA.Last(0)
|
||||||
vwma := pv / v
|
vwma := pv / v
|
||||||
inc.Values.Push(vwma)
|
inc.Values.Push(vwma)
|
||||||
}
|
}
|
||||||
|
@ -104,7 +97,7 @@ func (inc *VWMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inc.EndTime = last.EndTime.Time()
|
inc.EndTime = last.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Values.Last())
|
inc.EmitUpdate(inc.Values.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
func (inc *VWMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
// Refer: https://tradingview.com/script/aDymGrFx-Drift-Study-Inspired-by-Monte-Carlo-Simulations-with-BM-KL/
|
// Refer: https://tradingview.com/script/aDymGrFx-Drift-Study-Inspired-by-Monte-Carlo-Simulations-with-BM-KL/
|
||||||
// Brownian Motion's drift factor
|
// Brownian Motion's drift factor
|
||||||
// could be used in Monte Carlo Simulations
|
// could be used in Monte Carlo Simulations
|
||||||
|
//
|
||||||
//go:generate callbackgen -type WeightedDrift
|
//go:generate callbackgen -type WeightedDrift
|
||||||
type WeightedDrift struct {
|
type WeightedDrift struct {
|
||||||
types.SeriesBase
|
types.SeriesBase
|
||||||
|
@ -54,7 +55,7 @@ func (inc *WeightedDrift) Update(value float64, weight float64) {
|
||||||
}
|
}
|
||||||
if inc.chng.Length() >= inc.Window {
|
if inc.chng.Length() >= inc.Window {
|
||||||
stdev := types.Stdev(inc.chng, inc.Window)
|
stdev := types.Stdev(inc.chng, inc.Window)
|
||||||
drift := inc.MA.Last() - stdev*stdev*0.5
|
drift := inc.MA.Last(0) - stdev*stdev*0.5
|
||||||
inc.Values.Push(drift)
|
inc.Values.Push(drift)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +78,7 @@ func (inc *WeightedDrift) ZeroPoint() float64 {
|
||||||
} else {
|
} else {
|
||||||
return N2
|
return N2
|
||||||
}*/
|
}*/
|
||||||
return inc.LastValue * math.Exp(window*(0.5*stdev*stdev)+chng-inc.MA.Last()*window)
|
return inc.LastValue * math.Exp(window*(0.5*stdev*stdev)+chng-inc.MA.Last(0)*window)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *WeightedDrift) Clone() (out *WeightedDrift) {
|
func (inc *WeightedDrift) Clone() (out *WeightedDrift) {
|
||||||
|
@ -100,17 +101,11 @@ func (inc *WeightedDrift) TestUpdate(value float64, weight float64) *WeightedDri
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *WeightedDrift) Index(i int) float64 {
|
func (inc *WeightedDrift) Index(i int) float64 {
|
||||||
if inc.Values == nil {
|
return inc.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *WeightedDrift) Last() float64 {
|
func (inc *WeightedDrift) Last(i int) float64 {
|
||||||
if inc.Values.Length() == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.Values.Last()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *WeightedDrift) Length() int {
|
func (inc *WeightedDrift) Length() int {
|
||||||
|
@ -130,12 +125,12 @@ func (inc *WeightedDrift) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.chng == nil {
|
if inc.chng == nil {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,25 +33,17 @@ func (inc *WWMA) Update(value float64) {
|
||||||
inc.Values = inc.Values[MaxNumOfWWMATruncateSize-1:]
|
inc.Values = inc.Values[MaxNumOfWWMATruncateSize-1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
last := inc.Last()
|
last := inc.Last(0)
|
||||||
wma := last + (value-last)/float64(inc.Window)
|
wma := last + (value-last)/float64(inc.Window)
|
||||||
inc.Values.Push(wma)
|
inc.Values.Push(wma)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *WWMA) Last() float64 {
|
func (inc *WWMA) Last(i int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
return inc.Values.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return inc.Values[len(inc.Values)-1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *WWMA) Index(i int) float64 {
|
func (inc *WWMA) Index(i int) float64 {
|
||||||
if i >= len(inc.Values) {
|
return inc.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return inc.Values[len(inc.Values)-1-i]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *WWMA) Length() int {
|
func (inc *WWMA) Length() int {
|
||||||
|
@ -76,7 +68,7 @@ func (inc *WWMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if doable {
|
if doable {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.LastOpenTime = k.StartTime.Time()
|
inc.LastOpenTime = k.StartTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,17 +27,14 @@ type ZLEMA struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *ZLEMA) Index(i int) float64 {
|
func (inc *ZLEMA) Index(i int) float64 {
|
||||||
if inc.zlema == nil {
|
return inc.Last(i)
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return inc.zlema.Index(i)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *ZLEMA) Last() float64 {
|
func (inc *ZLEMA) Last(i int) float64 {
|
||||||
if inc.zlema == nil {
|
if inc.zlema == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.zlema.Last()
|
return inc.zlema.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *ZLEMA) Length() int {
|
func (inc *ZLEMA) Length() int {
|
||||||
|
@ -74,12 +71,12 @@ func (inc *ZLEMA) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
if inc.zlema == nil {
|
if inc.zlema == nil {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ func Test_ZLEMA(t *testing.T) {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
zlema := ZLEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
zlema := ZLEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
|
||||||
zlema.CalculateAndUpdate(tt.kLines)
|
zlema.CalculateAndUpdate(tt.kLines)
|
||||||
last := zlema.Last()
|
last := zlema.Last(0)
|
||||||
assert.InDelta(t, tt.want, last, Delta)
|
assert.InDelta(t, tt.want, last, Delta)
|
||||||
assert.InDelta(t, tt.next, zlema.Index(1), Delta)
|
assert.InDelta(t, tt.next, zlema.Index(1), Delta)
|
||||||
assert.Equal(t, tt.all, zlema.Length())
|
assert.Equal(t, tt.all, zlema.Length())
|
||||||
|
|
|
@ -59,9 +59,9 @@ func (d *DynamicExposureBollBand) initialize(symbol string, session *bbgo.Exchan
|
||||||
|
|
||||||
// getMaxExposure returns the max exposure
|
// getMaxExposure returns the max exposure
|
||||||
func (d *DynamicExposureBollBand) getMaxExposure(price float64, trend types.Direction) (fixedpoint.Value, error) {
|
func (d *DynamicExposureBollBand) getMaxExposure(price float64, trend types.Direction) (fixedpoint.Value, error) {
|
||||||
downBand := d.dynamicExposureBollBand.DownBand.Last()
|
downBand := d.dynamicExposureBollBand.DownBand.Last(0)
|
||||||
upBand := d.dynamicExposureBollBand.UpBand.Last()
|
upBand := d.dynamicExposureBollBand.UpBand.Last(0)
|
||||||
sma := d.dynamicExposureBollBand.SMA.Last()
|
sma := d.dynamicExposureBollBand.SMA.Last(0)
|
||||||
log.Infof("dynamicExposureBollBand bollinger band: up %f sma %f down %f", upBand, sma, downBand)
|
log.Infof("dynamicExposureBollBand bollinger band: up %f sma %f down %f", upBand, sma, downBand)
|
||||||
|
|
||||||
bandPercentage := 0.0
|
bandPercentage := 0.0
|
||||||
|
|
|
@ -114,7 +114,7 @@ func (ds *DynamicAmpSpread) update(kline types.KLine) {
|
||||||
|
|
||||||
func (ds *DynamicAmpSpread) getAskSpread() (askSpread float64, err error) {
|
func (ds *DynamicAmpSpread) getAskSpread() (askSpread float64, err error) {
|
||||||
if ds.AskSpreadScale != nil && ds.dynamicAskSpread.Length() >= ds.Window {
|
if ds.AskSpreadScale != nil && ds.dynamicAskSpread.Length() >= ds.Window {
|
||||||
askSpread, err = ds.AskSpreadScale.Scale(ds.dynamicAskSpread.Last())
|
askSpread, err = ds.AskSpreadScale.Scale(ds.dynamicAskSpread.Last(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Errorf("can not calculate dynamicAskSpread")
|
log.WithError(err).Errorf("can not calculate dynamicAskSpread")
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -128,7 +128,7 @@ func (ds *DynamicAmpSpread) getAskSpread() (askSpread float64, err error) {
|
||||||
|
|
||||||
func (ds *DynamicAmpSpread) getBidSpread() (bidSpread float64, err error) {
|
func (ds *DynamicAmpSpread) getBidSpread() (bidSpread float64, err error) {
|
||||||
if ds.BidSpreadScale != nil && ds.dynamicBidSpread.Length() >= ds.Window {
|
if ds.BidSpreadScale != nil && ds.dynamicBidSpread.Length() >= ds.Window {
|
||||||
bidSpread, err = ds.BidSpreadScale.Scale(ds.dynamicBidSpread.Last())
|
bidSpread, err = ds.BidSpreadScale.Scale(ds.dynamicBidSpread.Last(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Errorf("can not calculate dynamicBidSpread")
|
log.WithError(err).Errorf("can not calculate dynamicBidSpread")
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -224,12 +224,12 @@ func (ds *DynamicSpreadBollWidthRatio) getWeightedBBWidthRatio(positiveSigmoid b
|
||||||
// - To ask spread, the higher neutral band get greater ratio
|
// - To ask spread, the higher neutral band get greater ratio
|
||||||
// - To bid spread, the lower neutral band get greater ratio
|
// - To bid spread, the lower neutral band get greater ratio
|
||||||
|
|
||||||
defaultMid := ds.defaultBoll.SMA.Last()
|
defaultMid := ds.defaultBoll.SMA.Last(0)
|
||||||
defaultUpper := ds.defaultBoll.UpBand.Last()
|
defaultUpper := ds.defaultBoll.UpBand.Last(0)
|
||||||
defaultLower := ds.defaultBoll.DownBand.Last()
|
defaultLower := ds.defaultBoll.DownBand.Last(0)
|
||||||
defaultWidth := defaultUpper - defaultLower
|
defaultWidth := defaultUpper - defaultLower
|
||||||
neutralUpper := ds.neutralBoll.UpBand.Last()
|
neutralUpper := ds.neutralBoll.UpBand.Last(0)
|
||||||
neutralLower := ds.neutralBoll.DownBand.Last()
|
neutralLower := ds.neutralBoll.DownBand.Last(0)
|
||||||
factor := defaultWidth / ds.Sensitivity
|
factor := defaultWidth / ds.Sensitivity
|
||||||
var weightedUpper, weightedLower, weightedDivUpper, weightedDivLower float64
|
var weightedUpper, weightedLower, weightedDivUpper, weightedDivLower float64
|
||||||
if positiveSigmoid {
|
if positiveSigmoid {
|
||||||
|
|
|
@ -92,7 +92,7 @@ func (s *PerTrade) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
|
||||||
// min-max scaling
|
// min-max scaling
|
||||||
ofsMax := orderFlowSize.Tail(100).Max()
|
ofsMax := orderFlowSize.Tail(100).Max()
|
||||||
ofsMin := orderFlowSize.Tail(100).Min()
|
ofsMin := orderFlowSize.Tail(100).Min()
|
||||||
ofsMinMax := (orderFlowSize.Last() - ofsMin) / (ofsMax - ofsMin)
|
ofsMinMax := (orderFlowSize.Last(0) - ofsMin) / (ofsMax - ofsMin)
|
||||||
// preserves temporal dependency via polar encoded angles
|
// preserves temporal dependency via polar encoded angles
|
||||||
orderFlowSizeMinMax.Push(ofsMinMax)
|
orderFlowSizeMinMax.Push(ofsMinMax)
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ func (s *PerTrade) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
|
||||||
// min-max scaling
|
// min-max scaling
|
||||||
ofnMax := orderFlowNumber.Tail(100).Max()
|
ofnMax := orderFlowNumber.Tail(100).Max()
|
||||||
ofnMin := orderFlowNumber.Tail(100).Min()
|
ofnMin := orderFlowNumber.Tail(100).Min()
|
||||||
ofnMinMax := (orderFlowNumber.Last() - ofnMin) / (ofnMax - ofnMin)
|
ofnMinMax := (orderFlowNumber.Last(0) - ofnMin) / (ofnMax - ofnMin)
|
||||||
// preserves temporal dependency via polar encoded angles
|
// preserves temporal dependency via polar encoded angles
|
||||||
orderFlowNumberMinMax.Push(ofnMinMax)
|
orderFlowNumberMinMax.Push(ofnMinMax)
|
||||||
}
|
}
|
||||||
|
@ -167,9 +167,9 @@ func (s *PerTrade) placeTrade(ctx context.Context, side types.SideType, quantity
|
||||||
|
|
||||||
func outlier(fs floats.Slice, multiplier float64) int {
|
func outlier(fs floats.Slice, multiplier float64) int {
|
||||||
stddev := stat.StdDev(fs, nil)
|
stddev := stat.StdDev(fs, nil)
|
||||||
if fs.Last() > fs.Mean()+multiplier*stddev {
|
if fs.Last(0) > fs.Mean()+multiplier*stddev {
|
||||||
return 1
|
return 1
|
||||||
} else if fs.Last() < fs.Mean()-multiplier*stddev {
|
} else if fs.Last(0) < fs.Mean()-multiplier*stddev {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -135,7 +135,7 @@ func (s *Strategy) generateGridBuyOrders(session *bbgo.ExchangeSession) ([]types
|
||||||
ema99 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 99})
|
ema99 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 99})
|
||||||
ema25 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 25})
|
ema25 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 25})
|
||||||
ema7 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 7})
|
ema7 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 7})
|
||||||
if ema7.Last() > ema25.Last()*1.001 && ema25.Last() > ema99.Last()*1.0005 {
|
if ema7.Last(0) > ema25.Last(0)*1.001 && ema25.Last(0) > ema99.Last(0)*1.0005 {
|
||||||
log.Infof("all ema lines trend up, skip buy")
|
log.Infof("all ema lines trend up, skip buy")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,7 @@ func (s *Strategy) generateGridSellOrders(session *bbgo.ExchangeSession) ([]type
|
||||||
ema99 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 99})
|
ema99 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 99})
|
||||||
ema25 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 25})
|
ema25 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 25})
|
||||||
ema7 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 7})
|
ema7 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 7})
|
||||||
if ema7.Last() < ema25.Last()*(1-0.004) && ema25.Last() < ema99.Last()*(1-0.0005) {
|
if ema7.Last(0) < ema25.Last(0)*(1-0.004) && ema25.Last(0) < ema99.Last(0)*(1-0.0005) {
|
||||||
log.Infof("all ema lines trend down, skip sell")
|
log.Infof("all ema lines trend down, skip sell")
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ func (ds *DynamicSpreadAmpSettings) update(kline types.KLine) {
|
||||||
|
|
||||||
func (ds *DynamicSpreadAmpSettings) getAskSpread() (askSpread float64, err error) {
|
func (ds *DynamicSpreadAmpSettings) getAskSpread() (askSpread float64, err error) {
|
||||||
if ds.AskSpreadScale != nil && ds.dynamicAskSpread.Length() >= ds.Window {
|
if ds.AskSpreadScale != nil && ds.dynamicAskSpread.Length() >= ds.Window {
|
||||||
askSpread, err = ds.AskSpreadScale.Scale(ds.dynamicAskSpread.Last())
|
askSpread, err = ds.AskSpreadScale.Scale(ds.dynamicAskSpread.Last(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Errorf("can not calculate dynamicAskSpread")
|
log.WithError(err).Errorf("can not calculate dynamicAskSpread")
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -140,7 +140,7 @@ func (ds *DynamicSpreadAmpSettings) getAskSpread() (askSpread float64, err error
|
||||||
|
|
||||||
func (ds *DynamicSpreadAmpSettings) getBidSpread() (bidSpread float64, err error) {
|
func (ds *DynamicSpreadAmpSettings) getBidSpread() (bidSpread float64, err error) {
|
||||||
if ds.BidSpreadScale != nil && ds.dynamicBidSpread.Length() >= ds.Window {
|
if ds.BidSpreadScale != nil && ds.dynamicBidSpread.Length() >= ds.Window {
|
||||||
bidSpread, err = ds.BidSpreadScale.Scale(ds.dynamicBidSpread.Last())
|
bidSpread, err = ds.BidSpreadScale.Scale(ds.dynamicBidSpread.Last(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Errorf("can not calculate dynamicBidSpread")
|
log.WithError(err).Errorf("can not calculate dynamicBidSpread")
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -224,12 +224,12 @@ func (ds *DynamicSpreadBollWidthRatioSettings) getWeightedBBWidthRatio(positiveS
|
||||||
// - To ask spread, the higher neutral band get greater ratio
|
// - To ask spread, the higher neutral band get greater ratio
|
||||||
// - To bid spread, the lower neutral band get greater ratio
|
// - To bid spread, the lower neutral band get greater ratio
|
||||||
|
|
||||||
defaultMid := ds.defaultBoll.SMA.Last()
|
defaultMid := ds.defaultBoll.SMA.Last(0)
|
||||||
defaultUpper := ds.defaultBoll.UpBand.Last()
|
defaultUpper := ds.defaultBoll.UpBand.Last(0)
|
||||||
defaultLower := ds.defaultBoll.DownBand.Last()
|
defaultLower := ds.defaultBoll.DownBand.Last(0)
|
||||||
defaultWidth := defaultUpper - defaultLower
|
defaultWidth := defaultUpper - defaultLower
|
||||||
neutralUpper := ds.neutralBoll.UpBand.Last()
|
neutralUpper := ds.neutralBoll.UpBand.Last(0)
|
||||||
neutralLower := ds.neutralBoll.DownBand.Last()
|
neutralLower := ds.neutralBoll.DownBand.Last(0)
|
||||||
factor := defaultWidth / ds.Sensitivity
|
factor := defaultWidth / ds.Sensitivity
|
||||||
var weightedUpper, weightedLower, weightedDivUpper, weightedDivLower float64
|
var weightedUpper, weightedLower, weightedDivUpper, weightedDivLower float64
|
||||||
if positiveSigmoid {
|
if positiveSigmoid {
|
||||||
|
|
|
@ -278,9 +278,9 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k
|
||||||
baseBalance, hasBaseBalance := balances[s.Market.BaseCurrency]
|
baseBalance, hasBaseBalance := balances[s.Market.BaseCurrency]
|
||||||
quoteBalance, hasQuoteBalance := balances[s.Market.QuoteCurrency]
|
quoteBalance, hasQuoteBalance := balances[s.Market.QuoteCurrency]
|
||||||
|
|
||||||
downBand := s.defaultBoll.DownBand.Last()
|
downBand := s.defaultBoll.DownBand.Last(0)
|
||||||
upBand := s.defaultBoll.UpBand.Last()
|
upBand := s.defaultBoll.UpBand.Last(0)
|
||||||
sma := s.defaultBoll.SMA.Last()
|
sma := s.defaultBoll.SMA.Last(0)
|
||||||
log.Infof("%s bollinger band: up %f sma %f down %f", s.Symbol, upBand, sma, downBand)
|
log.Infof("%s bollinger band: up %f sma %f down %f", s.Symbol, upBand, sma, downBand)
|
||||||
|
|
||||||
bandPercentage := calculateBandPercentage(upBand, downBand, sma, midPrice.Float64())
|
bandPercentage := calculateBandPercentage(upBand, downBand, sma, midPrice.Float64())
|
||||||
|
@ -351,7 +351,7 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k
|
||||||
// WHEN: price breaks the upper band (price > window 2) == strongUpTrend
|
// WHEN: price breaks the upper band (price > window 2) == strongUpTrend
|
||||||
// THEN: we apply strongUpTrend skew
|
// THEN: we apply strongUpTrend skew
|
||||||
if s.TradeInBand {
|
if s.TradeInBand {
|
||||||
if !inBetween(midPrice.Float64(), s.neutralBoll.DownBand.Last(), s.neutralBoll.UpBand.Last()) {
|
if !inBetween(midPrice.Float64(), s.neutralBoll.DownBand.Last(0), s.neutralBoll.UpBand.Last(0)) {
|
||||||
log.Infof("tradeInBand is set, skip placing orders when the price is outside of the band")
|
log.Infof("tradeInBand is set, skip placing orders when the price is outside of the band")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -410,7 +410,7 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k
|
||||||
canSell = false
|
canSell = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.BuyBelowNeutralSMA && midPrice.Float64() > s.neutralBoll.SMA.Last() {
|
if s.BuyBelowNeutralSMA && midPrice.Float64() > s.neutralBoll.SMA.Last(0) {
|
||||||
canBuy = false
|
canBuy = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func detectPriceTrend(inc *indicator.BOLL, price float64) PriceTrend {
|
func detectPriceTrend(inc *indicator.BOLL, price float64) PriceTrend {
|
||||||
if inBetween(price, inc.DownBand.Last(), inc.UpBand.Last()) {
|
if inBetween(price, inc.DownBand.Last(0), inc.UpBand.Last(0)) {
|
||||||
return NeutralTrend
|
return NeutralTrend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,19 +17,19 @@ func (s *DriftMA) Update(value, weight float64) {
|
||||||
if s.ma1.Length() == 0 {
|
if s.ma1.Length() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.drift.Update(s.ma1.Last(), weight)
|
s.drift.Update(s.ma1.Last(0), weight)
|
||||||
if s.drift.Length() == 0 {
|
if s.drift.Length() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.ma2.Update(s.drift.Last())
|
s.ma2.Update(s.drift.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DriftMA) Last() float64 {
|
func (s *DriftMA) Last(int) float64 {
|
||||||
return s.ma2.Last()
|
return s.ma2.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DriftMA) Index(i int) float64 {
|
func (s *DriftMA) Index(i int) float64 {
|
||||||
return s.ma2.Index(i)
|
return s.ma2.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DriftMA) Length() int {
|
func (s *DriftMA) Length() int {
|
||||||
|
|
|
@ -9,7 +9,7 @@ func (s *Strategy) CheckStopLoss() bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.UseAtr {
|
if s.UseAtr {
|
||||||
atr := s.atr.Last()
|
atr := s.atr.Last(0)
|
||||||
if s.sellPrice > 0 && s.sellPrice+atr <= s.highestPrice ||
|
if s.sellPrice > 0 && s.sellPrice+atr <= s.highestPrice ||
|
||||||
s.buyPrice > 0 && s.buyPrice-atr >= s.lowestPrice {
|
s.buyPrice > 0 && s.buyPrice-atr >= s.lowestPrice {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -261,8 +261,8 @@ func (s *Strategy) initIndicators(store *bbgo.SerialMarketDataStore) error {
|
||||||
high := kline.High.Float64()
|
high := kline.High.Float64()
|
||||||
low := kline.Low.Float64()
|
low := kline.Low.Float64()
|
||||||
s.ma.Update(source)
|
s.ma.Update(source)
|
||||||
s.stdevHigh.Update(high - s.ma.Last())
|
s.stdevHigh.Update(high - s.ma.Last(0))
|
||||||
s.stdevLow.Update(s.ma.Last() - low)
|
s.stdevLow.Update(s.ma.Last(0) - low)
|
||||||
s.drift.Update(source, kline.Volume.Abs().Float64())
|
s.drift.Update(source, kline.Volume.Abs().Float64())
|
||||||
s.trendLine.Update(source)
|
s.trendLine.Update(source)
|
||||||
s.atr.PushK(kline)
|
s.atr.PushK(kline)
|
||||||
|
@ -485,7 +485,7 @@ func (s *Strategy) klineHandlerMin(ctx context.Context, kline types.KLine, count
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// for doing the trailing stoploss during backtesting
|
// for doing the trailing stoploss during backtesting
|
||||||
atr := s.atr.Last()
|
atr := s.atr.Last(0)
|
||||||
price := s.getLastPrice()
|
price := s.getLastPrice()
|
||||||
pricef := price.Float64()
|
pricef := price.Float64()
|
||||||
|
|
||||||
|
@ -538,7 +538,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine, counter
|
||||||
|
|
||||||
s.drift.Update(sourcef, kline.Volume.Abs().Float64())
|
s.drift.Update(sourcef, kline.Volume.Abs().Float64())
|
||||||
s.atr.PushK(kline)
|
s.atr.PushK(kline)
|
||||||
atr := s.atr.Last()
|
atr := s.atr.Last(0)
|
||||||
|
|
||||||
price := kline.Close // s.getLastPrice()
|
price := kline.Close // s.getLastPrice()
|
||||||
pricef := price.Float64()
|
pricef := price.Float64()
|
||||||
|
@ -563,7 +563,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine, counter
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("highdiff: %3.2f open: %8v, close: %8v, high: %8v, low: %8v, time: %v %v", s.stdevHigh.Last(), kline.Open, kline.Close, kline.High, kline.Low, kline.StartTime, kline.EndTime)
|
log.Infof("highdiff: %3.2f open: %8v, close: %8v, high: %8v, low: %8v, time: %v %v", s.stdevHigh.Last(0), kline.Open, kline.Close, kline.High, kline.Low, kline.StartTime, kline.EndTime)
|
||||||
|
|
||||||
s.positionLock.Lock()
|
s.positionLock.Lock()
|
||||||
if s.lowestPrice > 0 && lowf < s.lowestPrice {
|
if s.lowestPrice > 0 && lowf < s.lowestPrice {
|
||||||
|
@ -596,7 +596,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine, counter
|
||||||
shortCondition := drift[1] >= 0 && drift[0] <= 0 || (drift[1] >= drift[0] && drift[1] <= 0) || ddrift[1] >= 0 && ddrift[0] <= 0 || (ddrift[1] >= ddrift[0] && ddrift[1] <= 0)
|
shortCondition := drift[1] >= 0 && drift[0] <= 0 || (drift[1] >= drift[0] && drift[1] <= 0) || ddrift[1] >= 0 && ddrift[0] <= 0 || (ddrift[1] >= ddrift[0] && ddrift[1] <= 0)
|
||||||
longCondition := drift[1] <= 0 && drift[0] >= 0 || (drift[1] <= drift[0] && drift[1] >= 0) || ddrift[1] <= 0 && ddrift[0] >= 0 || (ddrift[1] <= ddrift[0] && ddrift[1] >= 0)
|
longCondition := drift[1] <= 0 && drift[0] >= 0 || (drift[1] <= drift[0] && drift[1] >= 0) || ddrift[1] <= 0 && ddrift[0] >= 0 || (ddrift[1] <= ddrift[0] && ddrift[1] >= 0)
|
||||||
if shortCondition && longCondition {
|
if shortCondition && longCondition {
|
||||||
if s.priceLines.Index(1) > s.priceLines.Last() {
|
if s.priceLines.Index(1) > s.priceLines.Last(0) {
|
||||||
longCondition = false
|
longCondition = false
|
||||||
} else {
|
} else {
|
||||||
shortCondition = false
|
shortCondition = false
|
||||||
|
@ -625,7 +625,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine, counter
|
||||||
}
|
}
|
||||||
|
|
||||||
if longCondition {
|
if longCondition {
|
||||||
source = source.Sub(fixedpoint.NewFromFloat(s.stdevLow.Last() * s.HighLowVarianceMultiplier))
|
source = source.Sub(fixedpoint.NewFromFloat(s.stdevLow.Last(0) * s.HighLowVarianceMultiplier))
|
||||||
if source.Compare(price) > 0 {
|
if source.Compare(price) > 0 {
|
||||||
source = price
|
source = price
|
||||||
}
|
}
|
||||||
|
@ -654,7 +654,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine, counter
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("source in long %v %v %f", source, price, s.stdevLow.Last())
|
log.Infof("source in long %v %v %f", source, price, s.stdevLow.Last(0))
|
||||||
|
|
||||||
o, err := s.SubmitOrder(ctx, *submitOrder)
|
o, err := s.SubmitOrder(ctx, *submitOrder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -675,12 +675,12 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine, counter
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if shortCondition {
|
if shortCondition {
|
||||||
source = source.Add(fixedpoint.NewFromFloat(s.stdevHigh.Last() * s.HighLowVarianceMultiplier))
|
source = source.Add(fixedpoint.NewFromFloat(s.stdevHigh.Last(0) * s.HighLowVarianceMultiplier))
|
||||||
if source.Compare(price) < 0 {
|
if source.Compare(price) < 0 {
|
||||||
source = price
|
source = price
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("source in short: %v %v %f", source, price, s.stdevLow.Last())
|
log.Infof("source in short: %v %v %f", source, price, s.stdevLow.Last(0))
|
||||||
|
|
||||||
opt := s.OpenPositionOptions
|
opt := s.OpenPositionOptions
|
||||||
opt.Short = true
|
opt.Short = true
|
||||||
|
|
|
@ -11,8 +11,8 @@ func (s *ElliottWave) Index(i int) float64 {
|
||||||
return s.maQuick.Index(i)/s.maSlow.Index(i) - 1.0
|
return s.maQuick.Index(i)/s.maSlow.Index(i) - 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ElliottWave) Last() float64 {
|
func (s *ElliottWave) Last(int) float64 {
|
||||||
return s.maQuick.Last()/s.maSlow.Last() - 1.0
|
return s.maQuick.Last(0)/s.maSlow.Last(0) - 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ElliottWave) Length() int {
|
func (s *ElliottWave) Length() int {
|
||||||
|
|
|
@ -199,12 +199,12 @@ func (s *Strategy) smartCancel(ctx context.Context, pricef float64) int {
|
||||||
if s.counter-s.orderPendingCounter[order.OrderID] >= s.PendingMinInterval {
|
if s.counter-s.orderPendingCounter[order.OrderID] >= s.PendingMinInterval {
|
||||||
toCancel = true
|
toCancel = true
|
||||||
} else if order.Side == types.SideTypeBuy {
|
} else if order.Side == types.SideTypeBuy {
|
||||||
if order.Price.Float64()+s.atr.Last()*2 <= pricef {
|
if order.Price.Float64()+s.atr.Last(0)*2 <= pricef {
|
||||||
toCancel = true
|
toCancel = true
|
||||||
}
|
}
|
||||||
} else if order.Side == types.SideTypeSell {
|
} else if order.Side == types.SideTypeSell {
|
||||||
// 75% of the probability
|
// 75% of the probability
|
||||||
if order.Price.Float64()-s.atr.Last()*2 >= pricef {
|
if order.Price.Float64()-s.atr.Last(0)*2 >= pricef {
|
||||||
toCancel = true
|
toCancel = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -425,7 +425,7 @@ func (s *Strategy) klineHandlerMin(ctx context.Context, kline types.KLine) {
|
||||||
stoploss := s.Stoploss.Float64()
|
stoploss := s.Stoploss.Float64()
|
||||||
price := s.getLastPrice()
|
price := s.getLastPrice()
|
||||||
pricef := price.Float64()
|
pricef := price.Float64()
|
||||||
atr := s.atr.Last()
|
atr := s.atr.Last(0)
|
||||||
|
|
||||||
numPending := s.smartCancel(ctx, pricef)
|
numPending := s.smartCancel(ctx, pricef)
|
||||||
if numPending > 0 {
|
if numPending > 0 {
|
||||||
|
@ -476,7 +476,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
|
|
||||||
s.smartCancel(ctx, pricef)
|
s.smartCancel(ctx, pricef)
|
||||||
|
|
||||||
atr := s.atr.Last()
|
atr := s.atr.Last(0)
|
||||||
ewo := types.Array(s.ewo, 4)
|
ewo := types.Array(s.ewo, 4)
|
||||||
if len(ewo) < 4 {
|
if len(ewo) < 4 {
|
||||||
return
|
return
|
||||||
|
|
|
@ -93,7 +93,7 @@ func (s *Strategy) clear(ctx context.Context, orderExecutor bbgo.OrderExecutor)
|
||||||
|
|
||||||
func (s *Strategy) place(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession, indicator types.Float64Indicator, closePrice fixedpoint.Value) {
|
func (s *Strategy) place(ctx context.Context, orderExecutor bbgo.OrderExecutor, session *bbgo.ExchangeSession, indicator types.Float64Indicator, closePrice fixedpoint.Value) {
|
||||||
closePriceF := closePrice.Float64()
|
closePriceF := closePrice.Float64()
|
||||||
movingAveragePriceF := indicator.Last()
|
movingAveragePriceF := indicator.Last(0)
|
||||||
|
|
||||||
// skip it if it's near zero because it's not loaded yet
|
// skip it if it's near zero because it's not loaded yet
|
||||||
if movingAveragePriceF < 0.0001 {
|
if movingAveragePriceF < 0.0001 {
|
||||||
|
|
|
@ -27,11 +27,11 @@ func NewHeikinAshi(size int) *HeikinAshi {
|
||||||
|
|
||||||
func (s *HeikinAshi) Print() string {
|
func (s *HeikinAshi) Print() string {
|
||||||
return fmt.Sprintf("Heikin c: %.3f, o: %.3f, h: %.3f, l: %.3f, v: %.3f",
|
return fmt.Sprintf("Heikin c: %.3f, o: %.3f, h: %.3f, l: %.3f, v: %.3f",
|
||||||
s.Close.Last(),
|
s.Close.Last(0),
|
||||||
s.Open.Last(),
|
s.Open.Last(0),
|
||||||
s.High.Last(),
|
s.High.Last(0),
|
||||||
s.Low.Last(),
|
s.Low.Last(0),
|
||||||
s.Volume.Last())
|
s.Volume.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *HeikinAshi) Update(kline types.KLine) {
|
func (inc *HeikinAshi) Update(kline types.KLine) {
|
||||||
|
@ -40,7 +40,7 @@ func (inc *HeikinAshi) Update(kline types.KLine) {
|
||||||
high := kline.High.Float64()
|
high := kline.High.Float64()
|
||||||
low := kline.Low.Float64()
|
low := kline.Low.Float64()
|
||||||
newClose := (open + high + low + cloze) / 4.
|
newClose := (open + high + low + cloze) / 4.
|
||||||
newOpen := (inc.Open.Last() + inc.Close.Last()) / 2.
|
newOpen := (inc.Open.Last(0) + inc.Close.Last(0)) / 2.
|
||||||
inc.Close.Update(newClose)
|
inc.Close.Update(newClose)
|
||||||
inc.Open.Update(newOpen)
|
inc.Open.Update(newOpen)
|
||||||
inc.High.Update(math.Max(math.Max(high, newOpen), newClose))
|
inc.High.Update(math.Max(math.Max(high, newOpen), newClose))
|
||||||
|
|
|
@ -139,7 +139,7 @@ func NewCCISTOCH(i types.Interval, filterHigh, filterLow float64) *CCISTOCH {
|
||||||
|
|
||||||
func (inc *CCISTOCH) Update(cloze float64) {
|
func (inc *CCISTOCH) Update(cloze float64) {
|
||||||
inc.cci.Update(cloze)
|
inc.cci.Update(cloze)
|
||||||
inc.stoch.Update(inc.cci.Last(), inc.cci.Last(), inc.cci.Last())
|
inc.stoch.Update(inc.cci.Last(0), inc.cci.Last(0), inc.cci.Last(0))
|
||||||
inc.ma.Update(inc.stoch.LastD())
|
inc.ma.Update(inc.stoch.LastD())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,19 +180,19 @@ type VWEMA struct {
|
||||||
V types.UpdatableSeries
|
V types.UpdatableSeries
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWEMA) Last() float64 {
|
func (inc *VWEMA) Last(int) float64 {
|
||||||
return inc.PV.Last() / inc.V.Last()
|
return inc.PV.Last(0) / inc.V.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWEMA) Index(i int) float64 {
|
func (inc *VWEMA) Index(i int) float64 {
|
||||||
if i >= inc.PV.Length() {
|
if i >= inc.PV.Length() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
vi := inc.V.Index(i)
|
vi := inc.V.Last(i)
|
||||||
if vi == 0 {
|
if vi == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.PV.Index(i) / vi
|
return inc.PV.Last(i) / vi
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VWEMA) Length() int {
|
func (inc *VWEMA) Length() int {
|
||||||
|
@ -262,11 +262,11 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
|
||||||
if s.heikinAshi.Close.Length() == 0 {
|
if s.heikinAshi.Close.Length() == 0 {
|
||||||
for _, kline := range window {
|
for _, kline := range window {
|
||||||
s.heikinAshi.Update(kline)
|
s.heikinAshi.Update(kline)
|
||||||
s.ccis.Update(getSource(window).Last())
|
s.ccis.Update(getSource(window).Last(0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s.heikinAshi.Update(window[len(window)-1])
|
s.heikinAshi.Update(window[len(window)-1])
|
||||||
s.ccis.Update(getSource(window).Last())
|
s.ccis.Update(getSource(window).Last(0))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if s.UseEma {
|
if s.UseEma {
|
||||||
|
@ -283,7 +283,7 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
|
||||||
ema34.Update(cloze)
|
ema34.Update(cloze)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cloze := getSource(window).Last()
|
cloze := getSource(window).Last(0)
|
||||||
ema5.Update(cloze)
|
ema5.Update(cloze)
|
||||||
ema34.Update(cloze)
|
ema34.Update(cloze)
|
||||||
}
|
}
|
||||||
|
@ -306,7 +306,7 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
|
||||||
sma34.Update(cloze)
|
sma34.Update(cloze)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cloze := getSource(window).Last()
|
cloze := getSource(window).Last(0)
|
||||||
sma5.Update(cloze)
|
sma5.Update(cloze)
|
||||||
sma34.Update(cloze)
|
sma34.Update(cloze)
|
||||||
}
|
}
|
||||||
|
@ -330,14 +330,14 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
|
||||||
vols := getVol(window)
|
vols := getVol(window)
|
||||||
if evwma5.PV.Length() == 0 {
|
if evwma5.PV.Length() == 0 {
|
||||||
for i := clozes.Length() - 1; i >= 0; i-- {
|
for i := clozes.Length() - 1; i >= 0; i-- {
|
||||||
price := clozes.Index(i)
|
price := clozes.Last(i)
|
||||||
vol := vols.Index(i)
|
vol := vols.Last(i)
|
||||||
evwma5.UpdateVal(price, vol)
|
evwma5.UpdateVal(price, vol)
|
||||||
evwma34.UpdateVal(price, vol)
|
evwma34.UpdateVal(price, vol)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
price := clozes.Last()
|
price := clozes.Last(0)
|
||||||
vol := vols.Last()
|
vol := vols.Last(0)
|
||||||
evwma5.UpdateVal(price, vol)
|
evwma5.UpdateVal(price, vol)
|
||||||
evwma34.UpdateVal(price, vol)
|
evwma34.UpdateVal(price, vol)
|
||||||
}
|
}
|
||||||
|
@ -363,7 +363,7 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
|
||||||
sig.Update(ewoValue)
|
sig.Update(ewoValue)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sig.Update(s.ewo.Last())
|
sig.Update(s.ewo.Last(0))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
s.ewoSignal = sig
|
s.ewoSignal = sig
|
||||||
|
@ -381,7 +381,7 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
|
||||||
sig.Update(ewoValue)
|
sig.Update(ewoValue)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sig.Update(s.ewo.Last())
|
sig.Update(s.ewo.Last(0))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
s.ewoSignal = sig
|
s.ewoSignal = sig
|
||||||
|
@ -398,13 +398,13 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
|
||||||
// lazy init
|
// lazy init
|
||||||
ewoVals := s.ewo.Reverse()
|
ewoVals := s.ewo.Reverse()
|
||||||
for i, ewoValue := range ewoVals {
|
for i, ewoValue := range ewoVals {
|
||||||
vol := window.Volume().Index(i)
|
vol := window.Volume().Last(i)
|
||||||
sig.PV.Update(ewoValue * vol)
|
sig.PV.Update(ewoValue * vol)
|
||||||
sig.V.Update(vol)
|
sig.V.Update(vol)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vol := window.Volume().Last()
|
vol := window.Volume().Last(0)
|
||||||
sig.PV.Update(s.ewo.Last() * vol)
|
sig.PV.Update(s.ewo.Last(0) * vol)
|
||||||
sig.V.Update(vol)
|
sig.V.Update(vol)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -663,11 +663,13 @@ func (s *Strategy) GetLastPrice() fixedpoint.Value {
|
||||||
// - TP by (lastprice < peak price - atr) || (lastprice > bottom price + atr)
|
// - TP by (lastprice < peak price - atr) || (lastprice > bottom price + atr)
|
||||||
// - SL by s.StopLoss (Abs(price_diff / price) > s.StopLoss)
|
// - SL by s.StopLoss (Abs(price_diff / price) > s.StopLoss)
|
||||||
// - entry condition on ewo(Elliott wave oscillator) Crosses ewoSignal(ma on ewo, signalWindow)
|
// - entry condition on ewo(Elliott wave oscillator) Crosses ewoSignal(ma on ewo, signalWindow)
|
||||||
// * buy signal on (crossover on previous K bar and no crossunder on latest K bar)
|
// - buy signal on (crossover on previous K bar and no crossunder on latest K bar)
|
||||||
// * sell signal on (crossunder on previous K bar and no crossunder on latest K bar)
|
// - sell signal on (crossunder on previous K bar and no crossunder on latest K bar)
|
||||||
|
//
|
||||||
// - and filtered by the following rules:
|
// - and filtered by the following rules:
|
||||||
// * buy: buy signal ON, kline Close > Open, Close > ma5, Close > ma34, CCI Stochastic Buy signal
|
// - buy: buy signal ON, kline Close > Open, Close > ma5, Close > ma34, CCI Stochastic Buy signal
|
||||||
// * sell: sell signal ON, kline Close < Open, Close < ma5, Close < ma34, CCI Stochastic Sell signal
|
// - sell: sell signal ON, kline Close < Open, Close < ma5, Close < ma34, CCI Stochastic Sell signal
|
||||||
|
//
|
||||||
// - or entry when ma34 +- atr * 3 gets touched
|
// - or entry when ma34 +- atr * 3 gets touched
|
||||||
// - entry price: latestPrice +- atr / 2 (short,long), close at market price
|
// - entry price: latestPrice +- atr / 2 (short,long), close at market price
|
||||||
// Cancel non-fully filled orders on new signal (either in same direction or not)
|
// Cancel non-fully filled orders on new signal (either in same direction or not)
|
||||||
|
@ -784,8 +786,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
s.SetupIndicators(store)
|
s.SetupIndicators(store)
|
||||||
|
|
||||||
// local peak of ewo
|
// local peak of ewo
|
||||||
shortSig := s.ewo.Last() < s.ewo.Index(1) && s.ewo.Index(1) > s.ewo.Index(2)
|
shortSig := s.ewo.Last(0) < s.ewo.Last(1) && s.ewo.Last(1) > s.ewo.Last(2)
|
||||||
longSig := s.ewo.Last() > s.ewo.Index(1) && s.ewo.Index(1) < s.ewo.Index(2)
|
longSig := s.ewo.Last(0) > s.ewo.Last(1) && s.ewo.Last(1) < s.ewo.Last(2)
|
||||||
|
|
||||||
sellOrderTPSL := func(price fixedpoint.Value) {
|
sellOrderTPSL := func(price fixedpoint.Value) {
|
||||||
lastPrice := s.GetLastPrice()
|
lastPrice := s.GetLastPrice()
|
||||||
|
@ -798,8 +800,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
balances := session.GetAccount().Balances()
|
balances := session.GetAccount().Balances()
|
||||||
quoteBalance := balances[s.Market.QuoteCurrency].Available
|
quoteBalance := balances[s.Market.QuoteCurrency].Available
|
||||||
atr := fixedpoint.NewFromFloat(s.atr.Last())
|
atr := fixedpoint.NewFromFloat(s.atr.Last(0))
|
||||||
atrx2 := fixedpoint.NewFromFloat(s.atr.Last() * 2)
|
atrx2 := fixedpoint.NewFromFloat(s.atr.Last(0) * 2)
|
||||||
buyall := false
|
buyall := false
|
||||||
if s.bottomPrice.IsZero() || s.bottomPrice.Compare(price) > 0 {
|
if s.bottomPrice.IsZero() || s.bottomPrice.Compare(price) > 0 {
|
||||||
s.bottomPrice = price
|
s.bottomPrice = price
|
||||||
|
@ -809,7 +811,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
spBack := s.sellPrice
|
spBack := s.sellPrice
|
||||||
reason := -1
|
reason := -1
|
||||||
if quoteBalance.Div(lastPrice).Compare(s.Market.MinQuantity) >= 0 && quoteBalance.Compare(s.Market.MinNotional) >= 0 {
|
if quoteBalance.Div(lastPrice).Compare(s.Market.MinQuantity) >= 0 && quoteBalance.Compare(s.Market.MinNotional) >= 0 {
|
||||||
base := fixedpoint.NewFromFloat(s.ma34.Last())
|
base := fixedpoint.NewFromFloat(s.ma34.Last(0))
|
||||||
// TP
|
// TP
|
||||||
if lastPrice.Compare(s.sellPrice) < 0 && (longSig ||
|
if lastPrice.Compare(s.sellPrice) < 0 && (longSig ||
|
||||||
(!atrx2.IsZero() && base.Sub(atrx2).Compare(lastPrice) >= 0)) {
|
(!atrx2.IsZero() && base.Sub(atrx2).Compare(lastPrice) >= 0)) {
|
||||||
|
@ -903,8 +905,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
}
|
}
|
||||||
balances := session.GetAccount().Balances()
|
balances := session.GetAccount().Balances()
|
||||||
baseBalance := balances[s.Market.BaseCurrency].Available
|
baseBalance := balances[s.Market.BaseCurrency].Available
|
||||||
atr := fixedpoint.NewFromFloat(s.atr.Last())
|
atr := fixedpoint.NewFromFloat(s.atr.Last(0))
|
||||||
atrx2 := fixedpoint.NewFromFloat(s.atr.Last() * 2)
|
atrx2 := fixedpoint.NewFromFloat(s.atr.Last(0) * 2)
|
||||||
sellall := false
|
sellall := false
|
||||||
if s.peakPrice.IsZero() || s.peakPrice.Compare(price) < 0 {
|
if s.peakPrice.IsZero() || s.peakPrice.Compare(price) < 0 {
|
||||||
s.peakPrice = price
|
s.peakPrice = price
|
||||||
|
@ -915,7 +917,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
reason := -1
|
reason := -1
|
||||||
if baseBalance.Compare(s.Market.MinQuantity) >= 0 && baseBalance.Mul(lastPrice).Compare(s.Market.MinNotional) >= 0 {
|
if baseBalance.Compare(s.Market.MinQuantity) >= 0 && baseBalance.Mul(lastPrice).Compare(s.Market.MinNotional) >= 0 {
|
||||||
// TP
|
// TP
|
||||||
base := fixedpoint.NewFromFloat(s.ma34.Last())
|
base := fixedpoint.NewFromFloat(s.ma34.Last(0))
|
||||||
if lastPrice.Compare(s.buyPrice) > 0 && (shortSig ||
|
if lastPrice.Compare(s.buyPrice) > 0 && (shortSig ||
|
||||||
(!atrx2.IsZero() && base.Add(atrx2).Compare(lastPrice) <= 0)) {
|
(!atrx2.IsZero() && base.Add(atrx2).Compare(lastPrice) <= 0)) {
|
||||||
sellall = true
|
sellall = true
|
||||||
|
@ -1089,10 +1091,10 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
balances := session.GetAccount().Balances()
|
balances := session.GetAccount().Balances()
|
||||||
baseBalance := balances[s.Market.BaseCurrency].Total()
|
baseBalance := balances[s.Market.BaseCurrency].Total()
|
||||||
quoteBalance := balances[s.Market.QuoteCurrency].Total()
|
quoteBalance := balances[s.Market.QuoteCurrency].Total()
|
||||||
atr := fixedpoint.NewFromFloat(s.atr.Last())
|
atr := fixedpoint.NewFromFloat(s.atr.Last(0))
|
||||||
if !s.Environment.IsBackTesting() {
|
if !s.Environment.IsBackTesting() {
|
||||||
log.Infof("Get last price: %v, ewo %f, ewoSig %f, ccis: %f, atr %v, kline: %v, balance[base]: %v balance[quote]: %v",
|
log.Infof("Get last price: %v, ewo %f, ewoSig %f, ccis: %f, atr %v, kline: %v, balance[base]: %v balance[quote]: %v",
|
||||||
lastPrice, s.ewo.Last(), s.ewoSignal.Last(), s.ccis.ma.Last(), atr, kline, baseBalance, quoteBalance)
|
lastPrice, s.ewo.Last(0), s.ewoSignal.Last(0), s.ccis.ma.Last(0), atr, kline, baseBalance, quoteBalance)
|
||||||
}
|
}
|
||||||
|
|
||||||
if kline.Interval != s.Interval {
|
if kline.Interval != s.Interval {
|
||||||
|
@ -1104,21 +1106,21 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
priceChangeRate := (priceHighest - priceLowest) / priceHighest / 14
|
priceChangeRate := (priceHighest - priceLowest) / priceHighest / 14
|
||||||
ewoHighest := types.Highest(s.ewoHistogram, 233)
|
ewoHighest := types.Highest(s.ewoHistogram, 233)
|
||||||
|
|
||||||
s.ewoChangeRate = math.Abs(s.ewoHistogram.Last() / ewoHighest * priceChangeRate)
|
s.ewoChangeRate = math.Abs(s.ewoHistogram.Last(0) / ewoHighest * priceChangeRate)
|
||||||
|
|
||||||
longSignal := types.CrossOver(s.ewo, s.ewoSignal)
|
longSignal := types.CrossOver(s.ewo, s.ewoSignal)
|
||||||
shortSignal := types.CrossUnder(s.ewo, s.ewoSignal)
|
shortSignal := types.CrossUnder(s.ewo, s.ewoSignal)
|
||||||
|
|
||||||
base := s.ma34.Last()
|
base := s.ma34.Last(0)
|
||||||
sellLine := base + s.atr.Last()*3
|
sellLine := base + s.atr.Last(0)*3
|
||||||
buyLine := base - s.atr.Last()*3
|
buyLine := base - s.atr.Last(0)*3
|
||||||
clozes := getClose(window)
|
clozes := getClose(window)
|
||||||
opens := getOpen(window)
|
opens := getOpen(window)
|
||||||
|
|
||||||
// get trend flags
|
// get trend flags
|
||||||
bull := clozes.Last() > opens.Last()
|
bull := clozes.Last(0) > opens.Last(0)
|
||||||
breakThrough := clozes.Last() > s.ma5.Last() && clozes.Last() > s.ma34.Last()
|
breakThrough := clozes.Last(0) > s.ma5.Last(0) && clozes.Last(0) > s.ma34.Last(0)
|
||||||
breakDown := clozes.Last() < s.ma5.Last() && clozes.Last() < s.ma34.Last()
|
breakDown := clozes.Last(0) < s.ma5.Last(0) && clozes.Last(0) < s.ma34.Last(0)
|
||||||
|
|
||||||
// kline breakthrough ma5, ma34 trend up, and cci Stochastic bull
|
// kline breakthrough ma5, ma34 trend up, and cci Stochastic bull
|
||||||
IsBull := bull && breakThrough && s.ccis.BuySignal() && s.ewoChangeRate < s.EwoChangeFilterHigh && s.ewoChangeRate > s.EwoChangeFilterLow
|
IsBull := bull && breakThrough && s.ccis.BuySignal() && s.ewoChangeRate < s.EwoChangeFilterHigh && s.ewoChangeRate > s.EwoChangeFilterLow
|
||||||
|
@ -1141,7 +1143,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
|
|
||||||
// backup, since the s.sellPrice will be cleared when doing ClosePosition
|
// backup, since the s.sellPrice will be cleared when doing ClosePosition
|
||||||
sellPrice := s.sellPrice
|
sellPrice := s.sellPrice
|
||||||
log.Errorf("ewoChangeRate %v, emv %v", s.ewoChangeRate, s.emv.Last())
|
log.Errorf("ewoChangeRate %v, emv %v", s.ewoChangeRate, s.emv.Last(0))
|
||||||
|
|
||||||
// calculate report
|
// calculate report
|
||||||
if closeOrder, _ := s.PlaceBuyOrder(ctx, price); closeOrder != nil {
|
if closeOrder, _ := s.PlaceBuyOrder(ctx, price); closeOrder != nil {
|
||||||
|
@ -1175,7 +1177,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
|
||||||
|
|
||||||
// backup, since the s.buyPrice will be cleared when doing ClosePosition
|
// backup, since the s.buyPrice will be cleared when doing ClosePosition
|
||||||
buyPrice := s.buyPrice
|
buyPrice := s.buyPrice
|
||||||
log.Errorf("ewoChangeRate: %v, emv %v", s.ewoChangeRate, s.emv.Last())
|
log.Errorf("ewoChangeRate: %v, emv %v", s.ewoChangeRate, s.emv.Last(0))
|
||||||
|
|
||||||
// calculate report
|
// calculate report
|
||||||
if closeOrder, _ := s.PlaceSellOrder(ctx, price); closeOrder != nil {
|
if closeOrder, _ := s.PlaceSellOrder(ctx, price); closeOrder != nil {
|
||||||
|
|
|
@ -34,14 +34,14 @@ func (inc *MOM) Index(i int) float64 {
|
||||||
if inc.Values == nil {
|
if inc.Values == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Values.Index(i)
|
return inc.Values.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *MOM) Last() float64 {
|
func (inc *MOM) Last(int) float64 {
|
||||||
if inc.Values.Length() == 0 {
|
if inc.Values.Length() == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *MOM) Length() int {
|
func (inc *MOM) Length() int {
|
||||||
|
@ -51,7 +51,7 @@ func (inc *MOM) Length() int {
|
||||||
return inc.Values.Length()
|
return inc.Values.Length()
|
||||||
}
|
}
|
||||||
|
|
||||||
//var _ types.SeriesExtend = &MOM{}
|
// var _ types.SeriesExtend = &MOM{}
|
||||||
|
|
||||||
func (inc *MOM) Update(open, close float64) {
|
func (inc *MOM) Update(open, close float64) {
|
||||||
if inc.SeriesBase.Series == nil {
|
if inc.SeriesBase.Series == nil {
|
||||||
|
@ -62,7 +62,7 @@ func (inc *MOM) Update(open, close float64) {
|
||||||
inc.opens.Update(open)
|
inc.opens.Update(open)
|
||||||
inc.closes.Update(close)
|
inc.closes.Update(close)
|
||||||
if inc.opens.Length() >= inc.Window && inc.closes.Length() >= inc.Window {
|
if inc.opens.Length() >= inc.Window && inc.closes.Length() >= inc.Window {
|
||||||
gap := inc.opens.Last()/inc.closes.Index(1) - 1
|
gap := inc.opens.Last(0)/inc.closes.Index(1) - 1
|
||||||
inc.Values.Push(gap)
|
inc.Values.Push(gap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,11 +72,11 @@ func (inc *MOM) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ func (inc *MOM) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(k.Open.Float64(), k.Close.Float64())
|
inc.Update(k.Open.Float64(), k.Close.Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateMomentum(klines []types.KLine, window int, valA KLineValueMapper, valB KLineValueMapper) (float64, error) {
|
func calculateMomentum(klines []types.KLine, window int, valA KLineValueMapper, valB KLineValueMapper) (float64, error) {
|
||||||
|
|
|
@ -35,12 +35,12 @@ func (inc *PMR) Update(price float64) {
|
||||||
}
|
}
|
||||||
inc.SMA.Update(price)
|
inc.SMA.Update(price)
|
||||||
if inc.SMA.Length() >= inc.Window {
|
if inc.SMA.Length() >= inc.Window {
|
||||||
reversion := inc.SMA.Last() / price
|
reversion := inc.SMA.Last(0) / price
|
||||||
inc.Values.Push(reversion)
|
inc.Values.Push(reversion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PMR) Last() float64 {
|
func (inc *PMR) Last(int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -65,11 +65,11 @@ func (inc *PMR) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ func (inc *PMR) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(indicator.KLineClosePriceMapper(k))
|
inc.Update(indicator.KLineClosePriceMapper(k))
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateKLinesPMR(allKLines []types.KLine, window int) float64 {
|
func CalculateKLinesPMR(allKLines []types.KLine, window int) float64 {
|
||||||
|
|
|
@ -47,7 +47,7 @@ func (inc *PVD) Update(price float64, volume float64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *PVD) Last() float64 {
|
func (inc *PVD) Last(int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -72,11 +72,11 @@ func (inc *PVD) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ func (inc *PVD) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(indicator.KLineClosePriceMapper(k), indicator.KLineVolumeMapper(k))
|
inc.Update(indicator.KLineClosePriceMapper(k), indicator.KLineVolumeMapper(k))
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateKLinesPVD(allKLines []types.KLine, window int) float64 {
|
func CalculateKLinesPVD(allKLines []types.KLine, window int) float64 {
|
||||||
|
|
|
@ -30,12 +30,12 @@ func (inc *RR) Update(price float64) {
|
||||||
inc.prices = types.NewQueue(inc.Window)
|
inc.prices = types.NewQueue(inc.Window)
|
||||||
}
|
}
|
||||||
inc.prices.Update(price)
|
inc.prices.Update(price)
|
||||||
irr := inc.prices.Last()/inc.prices.Index(1) - 1
|
irr := inc.prices.Last(0)/inc.prices.Index(1) - 1
|
||||||
inc.Values.Push(irr)
|
inc.Values.Push(irr)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *RR) Last() float64 {
|
func (inc *RR) Last(int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -60,11 +60,11 @@ func (inc *RR) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,14 +91,14 @@ func (inc *RR) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(indicator.KLineClosePriceMapper(k))
|
inc.Update(indicator.KLineClosePriceMapper(k))
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *RR) LoadK(allKLines []types.KLine) {
|
func (inc *RR) LoadK(allKLines []types.KLine) {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
//func calculateReturn(klines []types.KLine, window int, val KLineValueMapper) (float64, error) {
|
//func calculateReturn(klines []types.KLine, window int, val KLineValueMapper) (float64, error) {
|
||||||
|
|
|
@ -33,14 +33,14 @@ func (inc *VMOM) Index(i int) float64 {
|
||||||
if inc.Values == nil {
|
if inc.Values == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Values.Index(i)
|
return inc.Values.Last(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VMOM) Last() float64 {
|
func (inc *VMOM) Last(int) float64 {
|
||||||
if inc.Values.Length() == 0 {
|
if inc.Values.Length() == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return inc.Values.Last()
|
return inc.Values.Last(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *VMOM) Length() int {
|
func (inc *VMOM) Length() int {
|
||||||
|
@ -59,7 +59,7 @@ func (inc *VMOM) Update(volume float64) {
|
||||||
}
|
}
|
||||||
inc.volumes.Update(volume)
|
inc.volumes.Update(volume)
|
||||||
if inc.volumes.Length() >= inc.Window {
|
if inc.volumes.Length() >= inc.Window {
|
||||||
v := inc.volumes.Last() / inc.volumes.Mean()
|
v := inc.volumes.Last(0) / inc.volumes.Mean()
|
||||||
inc.Values.Push(v)
|
inc.Values.Push(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,11 +69,11 @@ func (inc *VMOM) CalculateAndUpdate(allKLines []types.KLine) {
|
||||||
for _, k := range allKLines {
|
for _, k := range allKLines {
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
}
|
}
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
} else {
|
} else {
|
||||||
k := allKLines[len(allKLines)-1]
|
k := allKLines[len(allKLines)-1]
|
||||||
inc.PushK(k)
|
inc.PushK(k)
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ func (inc *VMOM) PushK(k types.KLine) {
|
||||||
|
|
||||||
inc.Update(k.Volume.Float64())
|
inc.Update(k.Volume.Float64())
|
||||||
inc.EndTime = k.EndTime.Time()
|
inc.EndTime = k.EndTime.Time()
|
||||||
inc.EmitUpdate(inc.Last())
|
inc.EmitUpdate(inc.Last(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateVolumeMomentum(klines []types.KLine, window int, valV KLineValueMapper, valP KLineValueMapper) (float64, error) {
|
func calculateVolumeMomentum(klines []types.KLine, window int, valV KLineValueMapper, valP KLineValueMapper) (float64, error) {
|
||||||
|
@ -110,7 +110,7 @@ func calculateVolumeMomentum(klines []types.KLine, window int, valV KLineValueMa
|
||||||
vma += valV(p)
|
vma += valV(p)
|
||||||
}
|
}
|
||||||
vma /= float64(window)
|
vma /= float64(window)
|
||||||
momentum := valV(klines[length-1]) / vma //* (valP(klines[length-1-2]) / valP(klines[length-1]))
|
momentum := valV(klines[length-1]) / vma // * (valP(klines[length-1-2]) / valP(klines[length-1]))
|
||||||
|
|
||||||
return momentum, nil
|
return momentum, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,11 +104,11 @@ func (s *Linear) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.General
|
||||||
|
|
||||||
// use the last value from indicators, or the SeriesExtends' predict function. (e.g., look back: 5)
|
// use the last value from indicators, or the SeriesExtends' predict function. (e.g., look back: 5)
|
||||||
input := []float64{
|
input := []float64{
|
||||||
s.divergence.Last(),
|
s.divergence.Last(0),
|
||||||
s.reversion.Last(),
|
s.reversion.Last(0),
|
||||||
s.drift.Last(),
|
s.drift.Last(0),
|
||||||
s.momentum.Last(),
|
s.momentum.Last(0),
|
||||||
s.volume.Last(),
|
s.volume.Last(0),
|
||||||
}
|
}
|
||||||
pred := model.Predict(input)
|
pred := model.Predict(input)
|
||||||
predLst.Update(pred)
|
predLst.Update(pred)
|
||||||
|
|
|
@ -213,7 +213,7 @@ func (s *Strategy) generateSubmitOrders(ctx context.Context) ([]types.SubmitOrde
|
||||||
log.Infof("mid price: %+v", midPrice)
|
log.Infof("mid price: %+v", midPrice)
|
||||||
|
|
||||||
if s.ATRMultiplier.Float64() > 0 {
|
if s.ATRMultiplier.Float64() > 0 {
|
||||||
atr := fixedpoint.NewFromFloat(s.atr.Last())
|
atr := fixedpoint.NewFromFloat(s.atr.Last(0))
|
||||||
log.Infof("atr: %s", atr.String())
|
log.Infof("atr: %s", atr.String())
|
||||||
s.HalfSpreadRatio = s.ATRMultiplier.Mul(atr).Div(midPrice)
|
s.HalfSpreadRatio = s.ATRMultiplier.Mul(atr).Div(midPrice)
|
||||||
log.Infof("half spread ratio: %s", s.HalfSpreadRatio.String())
|
log.Infof("half spread ratio: %s", s.HalfSpreadRatio.String())
|
||||||
|
|
|
@ -75,7 +75,7 @@ func (s *Strategy) updateBidOrders(orderExecutor bbgo.OrderExecutor, session *bb
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var startPrice = fixedpoint.NewFromFloat(s.ewma.Last()).Mul(s.Percentage)
|
var startPrice = fixedpoint.NewFromFloat(s.ewma.Last(0)).Mul(s.Percentage)
|
||||||
|
|
||||||
var submitOrders []types.SubmitOrder
|
var submitOrders []types.SubmitOrder
|
||||||
for i := 0; i < s.GridNum; i++ {
|
for i := 0; i < s.GridNum; i++ {
|
||||||
|
|
|
@ -21,7 +21,7 @@ type A18 struct {
|
||||||
UpdateCallbacks []func(val float64)
|
UpdateCallbacks []func(val float64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (inc *A18) Last() float64 {
|
func (inc *A18) Last(int) float64 {
|
||||||
if len(inc.Values) == 0 {
|
if len(inc.Values) == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
}
|
}
|
||||||
|
@ -84,8 +84,8 @@ func calculateA18(klines []types.KLine, valClose KLineValueMapper) (float64, err
|
||||||
closes.Push(valClose(k))
|
closes.Push(valClose(k))
|
||||||
}
|
}
|
||||||
|
|
||||||
delay5 := closes.Index(4)
|
delay5 := closes.Last(4)
|
||||||
curr := closes.Index(0)
|
curr := closes.Last(0)
|
||||||
alpha := curr / delay5
|
alpha := curr / delay5
|
||||||
|
|
||||||
return alpha, nil
|
return alpha, nil
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user