all: add parameter index to the Last method

This commit is contained in:
c9s 2023-05-31 19:35:44 +08:00
parent 2a074ba11b
commit 5515f588e3
No known key found for this signature in database
GPG Key ID: 7385E7E464CB0A54
142 changed files with 845 additions and 998 deletions

View File

@ -48,7 +48,7 @@ func (s *LowerShadowTakeProfit) Bind(session *ExchangeSession, orderExecutor *Ge
}
// skip close price higher than the ewma
if closePrice.Float64() > ewma.Last() {
if closePrice.Float64() > ewma.Last(0) {
return
}

View File

@ -21,7 +21,7 @@ func (s *StopEMA) Bind(session *ExchangeSession, orderExecutor *GeneralOrderExec
}
func (s *StopEMA) Allowed(closePrice fixedpoint.Value) bool {
ema := fixedpoint.NewFromFloat(s.stopEWMA.Last())
ema := fixedpoint.NewFromFloat(s.stopEWMA.Last(0))
if ema.IsZero() {
logrus.Infof("stopEMA protection: value is zero, skip")
return false

View File

@ -29,12 +29,12 @@ func (s *TrendEMA) Bind(session *ExchangeSession, orderExecutor *GeneralOrderExe
}
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) {
s.last = s.current
s.current = s.ewma.Last()
s.current = s.ewma.Last(0)
}))
}

View File

@ -183,13 +183,13 @@ func (s Slice) Addr() *Slice {
}
// Last, Index, Length implements the types.Series interface
func (s Slice) Last() float64 {
func (s Slice) Last(i int) float64 {
length := len(s)
if length > 0 {
return s[length-1]
}
if i < 0 || length-1-i < 0 {
return 0.0
}
return s[length-1-i]
}
// Index fetches the element from the end of the slice
// WARNING: it does not start from 0!!!

View File

@ -35,15 +35,16 @@ func (inc *AD) Update(high, low, cloze, volume float64) {
moneyFlowVolume = ((2*cloze - high - low) / (high - low)) * volume
}
ad := inc.Last() + moneyFlowVolume
ad := inc.Last(0) + moneyFlowVolume
inc.Values.Push(ad)
}
func (inc *AD) Last() float64 {
if len(inc.Values) == 0 {
return 0.0
func (inc *AD) Last(i int) float64 {
length := len(inc.Values)
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 {
@ -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.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
}

View File

@ -20,6 +20,7 @@ import (
//
// @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
//
//go:generate callbackgen -type ALMA
type ALMA struct {
types.SeriesBase
@ -64,11 +65,11 @@ func (inc *ALMA) Update(value float64) {
}
}
func (inc *ALMA) Last() float64 {
if len(inc.Values) == 0 {
func (inc *ALMA) Last(i int) float64 {
if i >= len(inc.Values) {
return 0
}
return inc.Values[len(inc.Values)-1]
return inc.Values[len(inc.Values)-i-1]
}
func (inc *ALMA) Index(i int) float64 {
@ -88,12 +89,12 @@ func (inc *ALMA) CalculateAndUpdate(allKLines []types.KLine) {
if inc.input == nil {
for _, k := range allKLines {
inc.Update(k.Close.Float64())
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
return
}
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) {

View File

@ -53,7 +53,7 @@ func Test_ALMA(t *testing.T) {
Sigma: 6,
}
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.Equal(t, tt.all, alma.Length())
})

View File

@ -74,18 +74,18 @@ func (inc *ATR) Update(high, low, cloze float64) {
// apply rolling moving average
inc.RMA.Update(trueRange)
atr := inc.RMA.Last()
atr := inc.RMA.Last(0)
inc.PercentageVolatility.Push(atr / cloze)
if len(inc.PercentageVolatility) > MaxNumOfATR {
inc.PercentageVolatility = inc.PercentageVolatility[MaxNumOfATRTruncateSize-1:]
}
}
func (inc *ATR) Last() float64 {
func (inc *ATR) Last(i int) float64 {
if inc.RMA == nil {
return 0
}
return inc.RMA.Last()
return inc.RMA.Last(i)
}
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.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}

View File

@ -65,7 +65,7 @@ func Test_calculateATR(t *testing.T) {
atr.PushK(k)
}
got := atr.Last()
got := atr.Last(0)
diff := math.Trunc((got-tt.want)*100) / 100
if diff != 0 {
t.Errorf("calculateATR() = %v, want %v", got, tt.want)

View File

@ -69,15 +69,15 @@ func (inc *ATRP) Update(high, low, cloze float64) {
// apply rolling moving average
inc.RMA.Update(trueRange)
atr := inc.RMA.Last()
atr := inc.RMA.Last(0)
inc.PercentageVolatility.Push(atr / cloze)
}
func (inc *ATRP) Last() float64 {
func (inc *ATRP) Last(i int) float64 {
if inc.RMA == nil {
return 0
}
return inc.RMA.Last()
return inc.RMA.Last(i)
}
func (inc *ATRP) Index(i int) float64 {
@ -109,7 +109,7 @@ func (inc *ATRP) CalculateAndUpdate(kLines []types.KLine) {
inc.PushK(k)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
}

View File

@ -84,8 +84,8 @@ func (inc *BOLL) Update(value float64) {
inc.SMA.Update(value)
inc.StdDev.Update(value)
var sma = inc.SMA.Last()
var stdDev = inc.StdDev.Last()
var sma = inc.SMA.Last(0)
var stdDev = inc.StdDev.Last(0)
var band = inc.K * stdDev
var upBand = sma + band
@ -105,7 +105,7 @@ func (inc *BOLL) PushK(k types.KLine) {
}
inc.Update(k.Close.Float64())
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) {
@ -113,7 +113,7 @@ func (inc *BOLL) LoadK(allKLines []types.KLine) {
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) {

View File

@ -61,8 +61,8 @@ func TestBOLL(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
boll := BOLL{IntervalWindow: types.IntervalWindow{Window: tt.window}, K: tt.k}
boll.CalculateAndUpdate(tt.kLines)
assert.InDelta(t, tt.up, boll.UpBand.Last(), Delta)
assert.InDelta(t, tt.down, boll.DownBand.Last(), Delta)
assert.InDelta(t, tt.up, boll.UpBand.Last(0), Delta)
assert.InDelta(t, tt.down, boll.DownBand.Last(0), Delta)
})
}

View File

@ -43,7 +43,7 @@ func (inc *CCI) Update(value float64) {
}
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)
if len(inc.Input) < inc.Window {
return
@ -55,7 +55,7 @@ func (inc *CCI) Update(value float64) {
}
md := 0.
for i := 0; i < inc.Window; i++ {
diff := inc.Input.Index(i) - ma
diff := inc.Input.Last(i) - ma
md += diff * diff
}
md = math.Sqrt(md / float64(inc.Window))
@ -68,18 +68,12 @@ func (inc *CCI) Update(value float64) {
}
}
func (inc *CCI) Last() float64 {
if len(inc.Values) == 0 {
return 0
}
return inc.Values[len(inc.Values)-1]
func (inc *CCI) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *CCI) Index(i int) float64 {
if i >= len(inc.Values) {
return 0
}
return inc.Values[len(inc.Values)-1-i]
return inc.Last(i)
}
func (inc *CCI) Length() int {
@ -96,12 +90,12 @@ func (inc *CCI) CalculateAndUpdate(allKLines []types.KLine) {
if inc.TypicalPrice.Length() == 0 {
for _, k := range allKLines {
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
} else {
k := allKLines[len(allKLines)-1]
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}

View File

@ -29,7 +29,7 @@ func Test_CCI(t *testing.T) {
cci.Update(value)
}
last := cci.Last()
last := cci.Last(0)
assert.InDelta(t, 93.250481, last, Delta)
assert.InDelta(t, 81.813449, cci.Index(1), Delta)
assert.Equal(t, 50-16+1, cci.Length())

View File

@ -7,6 +7,7 @@ import (
// Refer: Cumulative Moving Average, Cumulative Average
// Refer: https://en.wikipedia.org/wiki/Moving_average
//
//go:generate callbackgen -type CA
type CA struct {
types.SeriesBase
@ -20,7 +21,7 @@ func (inc *CA) Update(x float64) {
if len(inc.Values) == 0 {
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.Values.Push(newVal)
if len(inc.Values) > MaxNumOfEWMA {
@ -29,18 +30,12 @@ func (inc *CA) Update(x float64) {
}
}
func (inc *CA) Last() float64 {
if len(inc.Values) == 0 {
return 0
}
return inc.Values[len(inc.Values)-1]
func (inc *CA) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *CA) Index(i int) float64 {
if i >= len(inc.Values) {
return 0
}
return inc.Values[len(inc.Values)-1-i]
return inc.Last(i)
}
func (inc *CA) Length() int {
@ -56,7 +51,7 @@ func (inc *CA) PushK(k types.KLine) {
func (inc *CA) CalculateAndUpdate(allKLines []types.KLine) {
for _, k := range allKLines {
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}

View File

@ -51,22 +51,19 @@ func (inc *DEMA) Update(value float64) {
}
inc.a1.Update(value)
inc.a2.Update(inc.a1.Last())
inc.Values.Push(2*inc.a1.Last() - inc.a2.Last())
inc.a2.Update(inc.a1.Last(0))
inc.Values.Push(2*inc.a1.Last(0) - inc.a2.Last(0))
if len(inc.Values) > MaxNumOfEWMA {
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
}
}
func (inc *DEMA) Last() float64 {
return inc.Values.Last()
func (inc *DEMA) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *DEMA) Index(i int) float64 {
if len(inc.Values)-i-1 >= 0 {
return inc.Values[len(inc.Values)-1-i]
}
return 0
return inc.Last(i)
}
func (inc *DEMA) Length() int {
@ -83,13 +80,13 @@ func (inc *DEMA) CalculateAndUpdate(allKLines []types.KLine) {
if inc.a1 == nil {
for _, k := range allKLines {
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
} else {
// last k
k := allKLines[len(allKLines)-1]
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}

View File

@ -46,7 +46,7 @@ func Test_DEMA(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
dema := DEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
dema.CalculateAndUpdate(tt.kLines)
last := dema.Last()
last := dema.Last(0)
assert.InDelta(t, tt.want, last, Delta)
assert.InDelta(t, tt.next, dema.Index(1), Delta)
assert.Equal(t, tt.all, dema.Length())

View File

@ -72,9 +72,9 @@ func (inc *DMI) Update(high, low, cloze float64) {
if inc.atr.Length() < inc.Window {
return
}
k := 100. / inc.atr.Last()
dmp := inc.DMP.Last()
dmn := inc.DMN.Last()
k := 100. / inc.atr.Last(0)
dmp := inc.DMP.Last(0)
dmn := inc.DMN.Last(0)
inc.DIPlus.Update(k * dmp)
inc.DIMinus.Update(k * dmn)
dx := 100. * math.Abs(dmp-dmn) / (dmp + dmn)
@ -108,11 +108,11 @@ func (inc *DMI) CalculateAndUpdate(allKLines []types.KLine) {
if inc.ADX == nil {
for _, k := range allKLines {
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 {
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))
}
}

View File

@ -78,9 +78,9 @@ func Test_DMI(t *testing.T) {
ADXSmoothing: 14,
}
dmi.CalculateAndUpdate(tt.klines)
assert.InDelta(t, dmi.GetDIPlus().Last(), tt.want.dip, Delta)
assert.InDelta(t, dmi.GetDIMinus().Last(), tt.want.dim, Delta)
assert.InDelta(t, dmi.GetADX().Last(), tt.want.adx, Delta)
assert.InDelta(t, dmi.GetDIPlus().Last(0), tt.want.dip, Delta)
assert.InDelta(t, dmi.GetDIMinus().Last(0), tt.want.dim, Delta)
assert.InDelta(t, dmi.GetADX().Last(0), tt.want.adx, Delta)
})
}

View File

@ -51,7 +51,7 @@ func (inc *Drift) Update(value float64) {
inc.chng.Update(chng)
if inc.chng.Length() >= 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)
}
}
@ -74,7 +74,7 @@ func (inc *Drift) ZeroPoint() float64 {
} else {
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) {
@ -96,17 +96,11 @@ func (inc *Drift) TestUpdate(value float64) *Drift {
}
func (inc *Drift) Index(i int) float64 {
if inc.Values == nil {
return 0
}
return inc.Values.Index(i)
return inc.Last(i)
}
func (inc *Drift) Last() float64 {
if inc.Values.Length() == 0 {
return 0
}
return inc.Values.Last()
func (inc *Drift) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *Drift) Length() int {
@ -126,12 +120,12 @@ func (inc *Drift) CalculateAndUpdate(allKLines []types.KLine) {
if inc.chng == nil {
for _, k := range allKLines {
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
} else {
k := allKLines[len(allKLines)-1]
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}

View File

@ -55,11 +55,12 @@ func (inc *EMV) Index(i int) float64 {
return inc.Values.Index(i)
}
func (inc *EMV) Last() float64 {
func (inc *EMV) Last(i int) float64 {
if inc.Values == nil {
return 0
}
return inc.Values.Last()
return inc.Values.Last(i)
}
func (inc *EMV) Length() int {

View File

@ -16,7 +16,7 @@ func Test_EMV(t *testing.T) {
}
emv.Update(63.74, 62.63, 32178836)
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.31, 62.62, 42476356)
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(64.69, 63.65, 40672116)
emv.Update(64.26, 63.68, 35627200)
assert.InDelta(t, -0.03, emv.Last(), Delta)
assert.InDelta(t, -0.03, emv.Last(0), Delta)
}

View File

@ -50,24 +50,20 @@ func (inc *EWMA) Update(value float64) {
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)
}
func (inc *EWMA) Last() float64 {
func (inc *EWMA) Last(i int) float64 {
if len(inc.Values) == 0 {
return 0
}
return inc.Values[len(inc.Values)-1]
return inc.Values.Last(i)
}
func (inc *EWMA) Index(i int) float64 {
if i >= len(inc.Values) {
return 0
}
return inc.Values[len(inc.Values)-1-i]
return inc.Last(i)
}
func (inc *EWMA) Length() int {
@ -81,7 +77,7 @@ func (inc *EWMA) PushK(k types.KLine) {
inc.Update(k.Close.Float64())
inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
func CalculateKLinesEMA(allKLines []types.KLine, priceF KLineValueMapper, window int) float64 {

View File

@ -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,
// 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.
//
//go:generate callbackgen -type FisherTransform
type FisherTransform struct {
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 {
return 0.0
}
return inc.Values.Last()
return inc.Values.Last(i)
}
func (inc *FisherTransform) Index(i int) float64 {
if inc.Values == nil {
return 0.0
}
return inc.Values.Index(i)
return inc.Last(i)
}
func (inc *FisherTransform) Length() int {

View File

@ -23,16 +23,12 @@ func NewFloat64Series(v ...float64) Float64Series {
return s
}
func (f *Float64Series) Last() float64 {
return f.slice.Last()
func (f *Float64Series) Last(i int) float64 {
return f.slice.Last(i)
}
func (f *Float64Series) Index(i int) float64 {
length := len(f.slice)
if length == 0 || length-i-1 < 0 {
return 0
}
return f.slice[length-i-1]
return f.slice.Last(i)
}
func (f *Float64Series) Length() int {

View File

@ -1,9 +1,10 @@
package indicator
import (
"math"
"github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
"math"
)
// 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
lambda2 := lambda * lambda
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.lastMeasurement = value
}
func (inc *GHFilter) Index(i int) float64 {
if inc.Values == nil {
return 0.0
}
return inc.Values.Index(i)
return inc.Last(i)
}
func (inc *GHFilter) Length() int {
@ -58,11 +56,11 @@ func (inc *GHFilter) Length() int {
return inc.Values.Length()
}
func (inc *GHFilter) Last() float64 {
func (inc *GHFilter) Last(i int) float64 {
if inc.Values == nil {
return 0.0
}
return inc.Values.Last()
return inc.Values.Last(i)
}
// interfaces implementation check

View File

@ -6058,7 +6058,7 @@ func Test_GHFilter(t *testing.T) {
for _, k := range klines {
filter.PushK(k)
}
got := filter.Last()
got := filter.Last(0)
got = math.Trunc(got*100.0) / 100.0
if 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 {
// square error between last estimated state and current actual state
if i > 0 {
filterDiff2Sum += klineSquareError(filter.Last(), k)
ewmaDiff2Sum += klineSquareError(ewma.Last(), k)
filterCloseDiff2Sum += closeSquareError(filter.Last(), k)
ewmaCloseDiff2Sum += closeSquareError(ewma.Last(), k)
filterDiff2Sum += klineSquareError(filter.Last(0), k)
ewmaDiff2Sum += klineSquareError(ewma.Last(0), k)
filterCloseDiff2Sum += closeSquareError(filter.Last(0), k)
ewmaCloseDiff2Sum += closeSquareError(ewma.Last(0), k)
}
// update estimations

View File

@ -22,18 +22,15 @@ type GMA struct {
UpdateCallbacks []func(value float64)
}
func (inc *GMA) Last() float64 {
func (inc *GMA) Last(i int) float64 {
if inc.SMA == nil {
return 0.0
}
return math.Exp(inc.SMA.Last())
return math.Exp(inc.SMA.Last(i))
}
func (inc *GMA) Index(i int) float64 {
if inc.SMA == nil {
return 0.0
}
return math.Exp(inc.SMA.Index(i))
return inc.Last(i)
}
func (inc *GMA) Length() int {

View File

@ -51,10 +51,10 @@ func Test_GMA(t *testing.T) {
for _, k := range tt.kLines {
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)
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())
})
}

View File

@ -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 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.
//
//go:generate callbackgen -type HULL
type HULL struct {
types.SeriesBase
@ -36,14 +37,14 @@ func (inc *HULL) Update(value float64) {
}
inc.ma1.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 {
return 0
}
return inc.result.Last()
return inc.result.Index(i)
}
func (inc *HULL) Index(i int) float64 {
@ -66,5 +67,5 @@ func (inc *HULL) PushK(k types.KLine) {
}
inc.Update(k.Close.Float64())
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}

View File

@ -51,7 +51,7 @@ func Test_HULL(t *testing.T) {
hull.PushK(k)
}
last := hull.Last()
last := hull.Last(0)
assert.InDelta(t, tt.want, last, Delta)
assert.InDelta(t, tt.next, hull.Index(1), Delta)
assert.Equal(t, tt.all, hull.Length())

View File

@ -25,7 +25,7 @@ type KLinePusher interface {
// Simple is the simple indicator that only returns one float64 value
type Simple interface {
KLinePusher
Last() float64
Last(int) float64
OnUpdate(f func(value float64))
}

View File

@ -1,9 +1,10 @@
package indicator
import (
"math"
"github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
"math"
)
// Refer: https://www.kalmanfilter.net/kalman1d.html
@ -25,7 +26,7 @@ type KalmanFilter struct {
func (inc *KalmanFilter) Update(value float64) {
var measureMove = value
if inc.measurements != nil {
measureMove = value - inc.measurements.Last()
measureMove = value - inc.measurements.Last(0)
}
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)
// update
lastPredict := inc.Values.Last()
lastPredict := inc.Values.Last(0)
curState := value + (value - 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 {
if inc.Values == nil {
return 0.0
}
return inc.Values.Index(i)
return inc.Last(i)
}
func (inc *KalmanFilter) Length() int {
if inc.Values == nil {
return 0
}
return inc.Values.Length()
}
func (inc *KalmanFilter) Last() float64 {
if inc.Values == nil {
return 0.0
}
return inc.Values.Last()
func (inc *KalmanFilter) Last(i int) float64 {
return inc.Values.Last(i)
}
// interfaces implementation check

View File

@ -6065,7 +6065,7 @@ func Test_KalmanFilter(t *testing.T) {
for _, k := range klines {
filter.PushK(k)
}
got := filter.Last()
got := filter.Last(0)
got = math.Trunc(got*100.0) / 100.0
if 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 {
// square error between last estimated state and current actual state
if ewma.Length() > 0 {
filterDiff2Sum += klineSquareError(filter.Last(), k)
ewmaDiff2Sum += klineSquareError(ewma.Last(), k)
filterCloseDiff2Sum += closeSquareError(filter.Last(), k)
ewmaCloseDiff2Sum += closeSquareError(ewma.Last(), k)
filterDiff2Sum += klineSquareError(filter.Last(0), k)
ewmaDiff2Sum += klineSquareError(ewma.Last(0), k)
filterCloseDiff2Sum += closeSquareError(filter.Last(0), k)
ewmaCloseDiff2Sum += closeSquareError(ewma.Last(0), k)
numEstimations++
}

View File

@ -12,6 +12,7 @@ import (
// The Klinger Oscillator is calculated by taking the difference between a 34-period and 55-period moving average.
// Usually the indicator is using together with a 9-period or 13-period of moving average as the signal line.
// This indicator is often used to identify potential turning points in the market, as well as to confirm the strength of a trend.
//
//go:generate callbackgen -type KlingerOscillator
type KlingerOscillator struct {
types.SeriesBase
@ -30,17 +31,11 @@ func (inc *KlingerOscillator) Length() int {
return inc.Fast.Length()
}
func (inc *KlingerOscillator) Last() float64 {
func (inc *KlingerOscillator) Last(i int) float64 {
if inc.Fast == nil || inc.Slow == nil {
return 0
}
return inc.Fast.Last() - inc.Slow.Last()
}
func (inc *KlingerOscillator) Index(i int) float64 {
if inc.Fast == nil || inc.Slow == nil {
return 0
}
return inc.Fast.Index(i) - inc.Slow.Index(i)
return inc.Fast.Last(i) - inc.Slow.Last(i)
}
func (inc *KlingerOscillator) Update(high, low, cloze, volume float64) {

View File

@ -38,12 +38,12 @@ func (l *Line) Bind(updater KLineWindowUpdater) {
updater.OnKLineWindowUpdate(l.handleKLineWindowUpdate)
}
func (l *Line) Last() float64 {
return (l.end-l.start)/float64(l.startIndex-l.endIndex)*float64(l.endIndex) + l.end
func (l *Line) Last(i int) float64 {
return (l.end-l.start)/float64(l.startIndex-l.endIndex)*float64(l.endIndex-i) + l.end
}
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 {

View File

@ -1,9 +1,10 @@
package indicator
import (
"github.com/sirupsen/logrus"
"time"
"github.com/sirupsen/logrus"
"github.com/c9s/bbgo/pkg/datatype/floats"
"github.com/c9s/bbgo/pkg/types"
)
@ -11,6 +12,7 @@ import (
var logLinReg = logrus.WithField("indicator", "LinReg")
// LinReg is Linear Regression baseline
//
//go:generate callbackgen -type LinReg
type LinReg struct {
types.SeriesBase
@ -28,11 +30,8 @@ type LinReg struct {
}
// Last slope of linear regression baseline
func (lr *LinReg) Last() float64 {
if lr.Values.Length() == 0 {
return 0.0
}
return lr.Values.Last()
func (lr *LinReg) Last(i int) float64 {
return lr.Values.Last(i)
}
// LastRatio of slope to price
@ -40,16 +39,12 @@ func (lr *LinReg) LastRatio() float64 {
if lr.ValueRatios.Length() == 0 {
return 0.0
}
return lr.ValueRatios.Last()
return lr.ValueRatios.Last(0)
}
// Index returns the slope of specified index
func (lr *LinReg) Index(i int) float64 {
if i >= lr.Values.Length() {
return 0.0
}
return lr.Values.Index(i)
return lr.Values.Last(i)
}
// IndexRatio returns the slope ratio
@ -58,7 +53,7 @@ func (lr *LinReg) IndexRatio(i int) float64 {
return 0.0
}
return lr.ValueRatios.Index(i)
return lr.ValueRatios.Last(i)
}
// Length of the slope values
@ -99,9 +94,9 @@ func (lr *LinReg) Update(kline types.KLine) {
endPrice := average - slope*sumX/length + slope
startPrice := endPrice + slope*(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) {

View File

@ -33,5 +33,5 @@ func (inc *Low) PushK(k types.KLine) {
inc.Update(k.Low.Float64())
inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}

View File

@ -59,14 +59,14 @@ func (inc *MACDLegacy) Update(x float64) {
inc.slowEWMA.Update(x)
// update MACD value, it's also the signal line
fast := inc.fastEWMA.Last()
slow := inc.slowEWMA.Last()
fast := inc.fastEWMA.Last(0)
slow := inc.slowEWMA.Last(0)
macd := fast - slow
inc.Values.Push(macd)
// update signal line
inc.signalLine.Update(macd)
signal := inc.signalLine.Last()
signal := inc.signalLine.Last(0)
// update histogram
histogram := macd - signal
@ -75,7 +75,7 @@ func (inc *MACDLegacy) Update(x float64) {
inc.EmitUpdate(macd, signal, histogram)
}
func (inc *MACDLegacy) Last() float64 {
func (inc *MACDLegacy) Last(int) float64 {
if len(inc.Values) == 0 {
return 0.0
}
@ -106,21 +106,12 @@ type MACDValues struct {
*MACDLegacy
}
func (inc *MACDValues) Last() float64 {
if len(inc.Values) == 0 {
return 0.0
}
return inc.Values[len(inc.Values)-1]
func (inc *MACDValues) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *MACDValues) Index(i int) float64 {
length := len(inc.Values)
if length == 0 || length-1-i < 0 {
return 0.0
}
return inc.Values[length-1+i]
return inc.Values.Last(i)
}
func (inc *MACDValues) Length() int {

View File

@ -45,7 +45,7 @@ func Test_calculateMACD(t *testing.T) {
macd.PushK(k)
}
got := macd.Last()
got := macd.Last(0)
diff := math.Trunc((got-tt.want)*100) / 100
if diff != 0 {
t.Errorf("calculateMACD() = %v, want %v", got, tt.want)

View File

@ -40,24 +40,18 @@ func (inc *OBV) Update(price, volume float64) {
}
if volume < inc.PrePrice {
inc.Values.Push(inc.Last() - volume)
inc.Values.Push(inc.Last(0) - volume)
} else {
inc.Values.Push(inc.Last() + volume)
inc.Values.Push(inc.Last(0) + volume)
}
}
func (inc *OBV) Last() float64 {
if len(inc.Values) == 0 {
return 0.0
}
return inc.Values[len(inc.Values)-1]
func (inc *OBV) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *OBV) Index(i int) float64 {
if len(inc.Values)-i <= 0 {
return 0.0
}
return inc.Values[len(inc.Values)-i-1]
return inc.Last(i)
}
var _ types.SeriesExtend = &OBV{}
@ -75,7 +69,7 @@ func (inc *OBV) CalculateAndUpdate(kLines []types.KLine) {
inc.PushK(k)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
}

View File

@ -10,7 +10,6 @@ import (
"github.com/c9s/bbgo/pkg/types"
)
//go:generate callbackgen -type Pivot
type Pivot struct {
types.IntervalWindow
@ -105,12 +104,12 @@ func calculatePivot(klines []types.KLine, window int, valLow KLineValueMapper, v
}
pl := 0.
if lows.Min() == lows.Index(int(window/2.)-1) {
if lows.Min() == lows.Last(int(window/2.)-1) {
pl = lows.Min()
}
ph := 0.
if highs.Max() == highs.Index(int(window/2.)-1) {
if highs.Max() == highs.Last(int(window/2.)-1) {
ph = highs.Max()
}

View File

@ -24,12 +24,12 @@ func (inc *PivotHigh) Length() int {
return inc.Values.Length()
}
func (inc *PivotHigh) Last() float64 {
func (inc *PivotHigh) Last(int) float64 {
if len(inc.Values) == 0 {
return 0.0
}
return inc.Values.Last()
return inc.Values.Last(0)
}
func (inc *PivotHigh) Update(value float64) {
@ -60,5 +60,5 @@ func (inc *PivotHigh) PushK(k types.KLine) {
inc.Update(k.High.Float64())
inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}

View File

@ -24,12 +24,12 @@ func (inc *PivotLow) Length() int {
return inc.Values.Length()
}
func (inc *PivotLow) Last() float64 {
func (inc *PivotLow) Last(int) float64 {
if len(inc.Values) == 0 {
return 0.0
}
return inc.Values.Last()
return inc.Values.Last(0)
}
func (inc *PivotLow) Update(value float64) {
@ -60,7 +60,7 @@ func (inc *PivotLow) PushK(k types.KLine) {
inc.Update(k.Low.Float64())
inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
func calculatePivotHigh(highs floats.Slice, left, right int) (float64, bool) {

View File

@ -34,11 +34,11 @@ type PSAR struct {
UpdateCallbacks []func(value float64)
}
func (inc *PSAR) Last() float64 {
func (inc *PSAR) Last(int) float64 {
if len(inc.Values) == 0 {
return 0
}
return inc.Values.Last()
return inc.Values.Last(0)
}
func (inc *PSAR) Length() int {
@ -46,8 +46,8 @@ func (inc *PSAR) Length() int {
}
func (inc *PSAR) falling() bool {
up := inc.High.Last() - inc.High.Index(1)
dn := inc.Low.Index(1) - inc.Low.Last()
up := inc.High.Last(0) - inc.High.Index(1)
dn := inc.Low.Index(1) - inc.Low.Last(0)
return (dn > up) && (dn > 0)
}
@ -66,7 +66,7 @@ func (inc *PSAR) Update(high, low float64) {
inc.High.Update(high)
inc.Low.Update(low)
if !isFirst {
ppsar := inc.Values.Last()
ppsar := inc.Values.Last(0)
if inc.Falling { // falling formula
psar := ppsar - inc.AF*(ppsar-inc.EP)
h := inc.High.Shift(1).Highest(2)

View File

@ -36,5 +36,5 @@ func Test_PSAR(t *testing.T) {
}
assert.Equal(t, psar.Length(), 29)
assert.Equal(t, psar.AF, 0.04)
assert.Equal(t, psar.Last(), 0.16)
assert.Equal(t, psar.Last(0), 0.16)
}

View File

@ -78,16 +78,12 @@ func (inc *RMA) Update(x float64) {
}
}
func (inc *RMA) Last() float64 {
return inc.Values.Last()
func (inc *RMA) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *RMA) Index(i int) float64 {
length := len(inc.Values)
if length == 0 || length-i-1 < 0 {
return 0
}
return inc.Values[length-i-1]
return inc.Last(i)
}
func (inc *RMA) Length() int {
@ -116,7 +112,7 @@ func (inc *RMA) CalculateAndUpdate(kLines []types.KLine) {
inc.PushK(last)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
func (inc *RMA) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {

View File

@ -65,19 +65,12 @@ func (inc *RSI) Update(price float64) {
inc.PreviousAvgLoss = avgLoss
}
func (inc *RSI) Last() float64 {
if len(inc.Values) == 0 {
return 0.0
}
return inc.Values[len(inc.Values)-1]
func (inc *RSI) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *RSI) Index(i int) float64 {
length := len(inc.Values)
if length <= 0 || length-i-1 < 0 {
return 0.0
}
return inc.Values[length-i-1]
return inc.Last(i)
}
func (inc *RSI) Length() int {
@ -99,7 +92,7 @@ func (inc *RSI) CalculateAndUpdate(kLines []types.KLine) {
inc.PushK(k)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
}

View File

@ -22,19 +22,12 @@ type SMA struct {
UpdateCallbacks []func(value float64)
}
func (inc *SMA) Last() float64 {
if inc.Values.Length() == 0 {
return 0.0
}
return inc.Values.Last()
func (inc *SMA) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *SMA) Index(i int) float64 {
if i >= inc.Values.Length() {
return 0.0
}
return inc.Values.Index(i)
return inc.Last(i)
}
func (inc *SMA) Length() int {
@ -81,7 +74,7 @@ func (inc *SMA) PushK(k types.KLine) {
inc.Update(k.Close.Float64())
inc.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Values.Last())
inc.EmitUpdate(inc.Values.Last(0))
}
func (inc *SMA) LoadK(allKLines []types.KLine) {

View File

@ -58,10 +58,10 @@ func Test_SMA(t *testing.T) {
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)
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())
})
}

View File

@ -50,9 +50,9 @@ func (inc *SSF) Update(value float64) {
}
result := inc.c1*value +
inc.c2*inc.Values.Index(0) +
inc.c3*inc.Values.Index(1) +
inc.c4*inc.Values.Index(2)
inc.c2*inc.Values.Last(0) +
inc.c3*inc.Values.Last(1) +
inc.c4*inc.Values.Last(2)
inc.Values.Push(result)
} else { // poles == 2
if inc.Values == nil {
@ -65,17 +65,18 @@ func (inc *SSF) Update(value float64) {
inc.Values = floats.Slice{}
}
result := inc.c1*value +
inc.c2*inc.Values.Index(0) +
inc.c3*inc.Values.Index(1)
inc.c2*inc.Values.Last(0) +
inc.c3*inc.Values.Last(1)
inc.Values.Push(result)
}
}
func (inc *SSF) Index(i int) float64 {
if inc.Values == nil {
return 0.0
func (inc *SSF) Last(i int) float64 {
return inc.Values.Last(i)
}
return inc.Values.Index(i)
func (inc *SSF) Index(i int) float64 {
return inc.Last(i)
}
func (inc *SSF) Length() int {
@ -85,13 +86,6 @@ func (inc *SSF) Length() int {
return inc.Values.Length()
}
func (inc *SSF) Last() float64 {
if inc.Values == nil {
return 0.0
}
return inc.Values.Last()
}
var _ types.SeriesExtend = &SSF{}
func (inc *SSF) PushK(k types.KLine) {
@ -102,12 +96,12 @@ func (inc *SSF) CalculateAndUpdate(allKLines []types.KLine) {
if inc.Values != nil {
k := allKLines[len(allKLines)-1]
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
return
}
for _, k := range allKLines {
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}

View File

@ -63,7 +63,7 @@ func Test_SSF(t *testing.T) {
Poles: tt.poles,
}
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.Equal(t, tt.all, ssf.Length())
})

View File

@ -21,19 +21,12 @@ type StdDev struct {
updateCallbacks []func(value float64)
}
func (inc *StdDev) Last() float64 {
if inc.Values.Length() == 0 {
return 0.0
}
return inc.Values.Last()
func (inc *StdDev) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *StdDev) Index(i int) float64 {
if i >= inc.Values.Length() {
return 0.0
}
return inc.Values.Index(i)
return inc.Last(i)
}
func (inc *StdDev) Length() int {
@ -76,7 +69,7 @@ func (inc *StdDev) CalculateAndUpdate(allKLines []types.KLine) {
inc.PushK(last)
}
inc.EmitUpdate(inc.Values.Last())
inc.EmitUpdate(inc.Values.Last(0))
}
func (inc *StdDev) handleKLineWindowUpdate(interval types.Interval, window types.KLineWindow) {

View File

@ -50,16 +50,12 @@ type Supertrend struct {
UpdateCallbacks []func(value float64)
}
func (inc *Supertrend) Last() float64 {
return inc.trendPrices.Last()
func (inc *Supertrend) Last(i int) float64 {
return inc.trendPrices.Last(i)
}
func (inc *Supertrend) Index(i int) float64 {
length := inc.Length()
if length == 0 || length-i-1 < 0 {
return 0
}
return inc.trendPrices[length-i-1]
return inc.Last(i)
}
func (inc *Supertrend) Length() int {
@ -94,13 +90,13 @@ func (inc *Supertrend) Update(highPrice, lowPrice, closePrice float64) {
src := (highPrice + lowPrice) / 2
// Update uptrend
inc.uptrendPrice = src - inc.AverageTrueRange.Last()*inc.ATRMultiplier
inc.uptrendPrice = src - inc.AverageTrueRange.Last(0)*inc.ATRMultiplier
if inc.previousClosePrice > inc.previousUptrendPrice {
inc.uptrendPrice = math.Max(inc.uptrendPrice, inc.previousUptrendPrice)
}
// Update downtrend
inc.downtrendPrice = src + inc.AverageTrueRange.Last()*inc.ATRMultiplier
inc.downtrendPrice = src + inc.AverageTrueRange.Last(0)*inc.ATRMultiplier
if inc.previousClosePrice < inc.previousDowntrendPrice {
inc.downtrendPrice = math.Min(inc.downtrendPrice, inc.previousDowntrendPrice)
}
@ -115,7 +111,7 @@ func (inc *Supertrend) Update(highPrice, lowPrice, closePrice float64) {
}
// Update signal
if inc.AverageTrueRange.Last() <= 0 {
if inc.AverageTrueRange.Last(0) <= 0 {
inc.tradeSignal = types.DirectionNone
} else if inc.trend == types.DirectionUp && inc.previousTrend == types.DirectionDown {
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,"+
" 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 {
@ -152,12 +148,12 @@ func (inc *Supertrend) Direction() types.Direction {
// LastSupertrendSupport return the current supertrend support
func (inc *Supertrend) LastSupertrendSupport() float64 {
return inc.supportLine.Last()
return inc.supportLine.Last(0)
}
// LastSupertrendResistance return the current supertrend resistance
func (inc *Supertrend) LastSupertrendResistance() float64 {
return inc.resistanceLine.Last()
return inc.resistanceLine.Last(0)
}
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.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.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
inc.EndTime = kLines[len(kLines)-1].EndTime.Time()
}

View File

@ -50,16 +50,12 @@ type PivotSupertrend struct {
UpdateCallbacks []func(value float64)
}
func (inc *PivotSupertrend) Last() float64 {
return inc.trendPrices.Last()
func (inc *PivotSupertrend) Last(i int) float64 {
return inc.trendPrices.Last(i)
}
func (inc *PivotSupertrend) Index(i int) float64 {
length := inc.Length()
if length == 0 || length-i-1 < 0 {
return 0
}
return inc.trendPrices[length-i-1]
return inc.Last(i)
}
func (inc *PivotSupertrend) Length() int {
@ -80,8 +76,8 @@ func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
inc.trend = types.DirectionUp
}
inc.previousPivotLow = inc.PivotLow.Last()
inc.previousPivotHigh = inc.PivotHigh.Last()
inc.previousPivotLow = inc.PivotLow.Last(0)
inc.previousPivotHigh = inc.PivotHigh.Last(0)
// Update High / Low pivots
inc.PivotLow.Update(lowPrice)
@ -101,9 +97,9 @@ func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
// Initialize lastPp as soon as pivots are made
if inc.lastPp == 0 || math.IsNaN(inc.lastPp) {
if inc.PivotHigh.Length() > 0 {
inc.lastPp = inc.PivotHigh.Last()
inc.lastPp = inc.PivotHigh.Last(0)
} else if inc.PivotLow.Length() > 0 {
inc.lastPp = inc.PivotLow.Last()
inc.lastPp = inc.PivotLow.Last(0)
} else {
inc.lastPp = math.NaN()
return
@ -111,10 +107,10 @@ func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
}
// Set lastPp to the latest pivotPoint (only changed when new pivot is found)
if inc.PivotHigh.Last() != inc.previousPivotHigh {
inc.lastPp = inc.PivotHigh.Last()
} else if inc.PivotLow.Last() != inc.previousPivotLow {
inc.lastPp = inc.PivotLow.Last()
if inc.PivotHigh.Last(0) != inc.previousPivotHigh {
inc.lastPp = inc.PivotHigh.Last(0)
} else if inc.PivotLow.Last(0) != inc.previousPivotLow {
inc.lastPp = inc.PivotLow.Last(0)
}
// calculate the Center line using pivot points
@ -126,13 +122,13 @@ func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
}
// 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 {
inc.uptrendPrice = math.Max(inc.uptrendPrice, inc.previousUptrendPrice)
}
// 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 {
inc.downtrendPrice = math.Min(inc.downtrendPrice, inc.previousDowntrendPrice)
}
@ -147,7 +143,7 @@ func (inc *PivotSupertrend) Update(highPrice, lowPrice, closePrice float64) {
}
// Update signal
if inc.AverageTrueRange.Last() <= 0 {
if inc.AverageTrueRange.Last(0) <= 0 {
inc.tradeSignal = types.DirectionNone
} else if inc.trend == types.DirectionUp && inc.previousTrend == types.DirectionDown {
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,"+
" 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)
@ -185,12 +181,12 @@ func (inc *PivotSupertrend) Direction() types.Direction {
// LastSupertrendSupport return the current supertrend support value
func (inc *PivotSupertrend) LastSupertrendSupport() float64 {
return inc.supportLine.Last()
return inc.supportLine.Last(0)
}
// LastSupertrendResistance return the current supertrend resistance value
func (inc *PivotSupertrend) LastSupertrendResistance() float64 {
return inc.resistanceLine.Last()
return inc.resistanceLine.Last(0)
}
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.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
func (inc *PivotSupertrend) BindK(target KLineClosedEmitter, symbol string, interval types.Interval) {

View File

@ -36,26 +36,20 @@ func (inc *TEMA) Update(value float64) {
inc.A3 = &EWMA{IntervalWindow: inc.IntervalWindow}
}
inc.A1.Update(value)
a1 := inc.A1.Last()
a1 := inc.A1.Last(0)
inc.A2.Update(a1)
a2 := inc.A2.Last()
a2 := inc.A2.Last(0)
inc.A3.Update(a2)
a3 := inc.A3.Last()
a3 := inc.A3.Last(0)
inc.Values.Push(3*a1 - 3*a2 + a3)
}
func (inc *TEMA) Last() float64 {
if len(inc.Values) > 0 {
return inc.Values[len(inc.Values)-1]
}
return 0.0
func (inc *TEMA) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *TEMA) Index(i int) float64 {
if i >= len(inc.Values) {
return 0
}
return inc.Values[len(inc.Values)-i-1]
return inc.Last(i)
}
func (inc *TEMA) Length() int {
@ -72,12 +66,12 @@ func (inc *TEMA) CalculateAndUpdate(allKLines []types.KLine) {
if inc.A1 == nil {
for _, k := range allKLines {
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
} else {
k := allKLines[len(allKLines)-1]
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}

View File

@ -47,7 +47,7 @@ func Test_TEMA(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
tema := TEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
tema.CalculateAndUpdate(tt.kLines)
last := tema.Last()
last := tema.Last(0)
assert.InDelta(t, tt.want, last, Delta)
assert.InDelta(t, tt.next, tema.Index(1), Delta)
assert.Equal(t, tt.all, tema.Length())

View File

@ -57,28 +57,18 @@ func (inc *TILL) Update(value float64) {
}
inc.e1.Update(value)
inc.e2.Update(inc.e1.Last())
inc.e3.Update(inc.e2.Last())
inc.e4.Update(inc.e3.Last())
inc.e5.Update(inc.e4.Last())
inc.e6.Update(inc.e5.Last())
inc.e2.Update(inc.e1.Last(0))
inc.e3.Update(inc.e2.Last(0))
inc.e4.Update(inc.e3.Last(0))
inc.e5.Update(inc.e4.Last(0))
inc.e6.Update(inc.e5.Last(0))
}
func (inc *TILL) Last() 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 {
func (inc *TILL) Last(i int) float64 {
if inc.e1 == nil || inc.e1.Length() <= i {
return 0
}
e3 := inc.e3.Index(i)
e4 := inc.e4.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
}
func (inc *TILL) Index(i int) float64 {
return inc.Last(i)
}
func (inc *TILL) Length() int {
if inc.e1 == nil {
return 0
@ -101,7 +95,7 @@ func (inc *TILL) PushK(k types.KLine) {
}
inc.Update(k.Close.Float64())
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
func (inc *TILL) LoadK(allKLines []types.KLine) {

View File

@ -57,7 +57,7 @@ func Test_TILL(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
till := TILL{IntervalWindow: types.IntervalWindow{Window: 16}}
till.CalculateAndUpdate(tt.kLines)
last := till.Last()
last := till.Last(0)
assert.InDelta(t, tt.want, last, Delta)
assert.InDelta(t, tt.next, till.Index(1), Delta)
assert.Equal(t, tt.all, till.Length())

View File

@ -6,6 +6,7 @@ import (
// Refer: Triangular Moving Average
// Refer URL: https://ja.wikipedia.org/wiki/移動平均
//
//go:generate callbackgen -type TMA
type TMA struct {
types.SeriesBase
@ -24,21 +25,15 @@ func (inc *TMA) Update(value float64) {
}
inc.s1.Update(value)
inc.s2.Update(inc.s1.Last())
inc.s2.Update(inc.s1.Last(0))
}
func (inc *TMA) Last() float64 {
if inc.s2 == nil {
return 0
}
return inc.s2.Last()
func (inc *TMA) Last(i int) float64 {
return inc.s2.Last(i)
}
func (inc *TMA) Index(i int) float64 {
if inc.s2 == nil {
return 0
}
return inc.s2.Index(i)
return inc.Last(i)
}
func (inc *TMA) Length() int {
@ -58,12 +53,12 @@ func (inc *TMA) CalculateAndUpdate(allKLines []types.KLine) {
if inc.s1 == nil {
for _, k := range allKLines {
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
} else {
k := allKLines[len(allKLines)-1]
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}

View File

@ -9,6 +9,7 @@ import (
// Refer: True Strength Index
// Refer URL: https://www.investopedia.com/terms/t/tsi.asp
//
//go:generate callbackgen -type TSI
type TSI struct {
types.SeriesBase
@ -66,10 +67,10 @@ func (inc *TSI) Update(value float64) {
apc := math.Abs(pc)
inc.Apcs.Update(apc)
inc.Pcds.Update(inc.Pcs.Last())
inc.Apcds.Update(inc.Apcs.Last())
inc.Pcds.Update(inc.Pcs.Last(0))
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)
if inc.Values.Length() > MaxNumOfEWMA {
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
@ -80,12 +81,12 @@ func (inc *TSI) Length() int {
return inc.Values.Length()
}
func (inc *TSI) Last() float64 {
return inc.Values.Last()
func (inc *TSI) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *TSI) Index(i int) float64 {
return inc.Values.Index(i)
return inc.Last(i)
}
func (inc *TSI) PushK(k types.KLine) {

View File

@ -32,5 +32,5 @@ func Test_TSI(t *testing.T) {
}
assert.Equal(t, tsi.Length(), 29)
Delta := 1.5e-2
assert.InDelta(t, tsi.Last(), 22.89, Delta)
assert.InDelta(t, tsi.Last(0), 22.89, Delta)
}

View File

@ -70,20 +70,20 @@ func (inc *UtBotAlert) Update(highPrice, lowPrice, closePrice float64) {
// Update ATR
inc.AverageTrueRange.Update(highPrice, lowPrice, closePrice)
nLoss := inc.AverageTrueRange.Last() * inc.KeyValue
nLoss := inc.AverageTrueRange.Last(0) * inc.KeyValue
// xATRTrailingStop
if inc.xATRTrailingStop.Length() == 0 {
// For first run
inc.xATRTrailingStop.Update(0)
} else if closePrice > inc.xATRTrailingStop.Index(1) && inc.previousClosePrice > inc.xATRTrailingStop.Index(1) {
inc.xATRTrailingStop.Update(math.Max(inc.xATRTrailingStop.Index(1), closePrice-nLoss))
} else if closePrice > inc.xATRTrailingStop.Last(1) && inc.previousClosePrice > inc.xATRTrailingStop.Last(1) {
inc.xATRTrailingStop.Update(math.Max(inc.xATRTrailingStop.Last(1), closePrice-nLoss))
} else if closePrice < inc.xATRTrailingStop.Index(1) && inc.previousClosePrice < inc.xATRTrailingStop.Index(1) {
inc.xATRTrailingStop.Update(math.Min(inc.xATRTrailingStop.Index(1), closePrice+nLoss))
} else if closePrice < inc.xATRTrailingStop.Last(1) && inc.previousClosePrice < inc.xATRTrailingStop.Last(1) {
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)
} else {
@ -91,19 +91,19 @@ func (inc *UtBotAlert) Update(highPrice, lowPrice, closePrice float64) {
}
// 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
} 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
} else {
inc.pos = inc.previousPos
}
above := closePrice > inc.xATRTrailingStop.Last() && inc.previousClosePrice < inc.xATRTrailingStop.Index(1)
below := 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(0) && inc.previousClosePrice > inc.xATRTrailingStop.Last(1)
buy := closePrice > inc.xATRTrailingStop.Last() && above // buy
sell := closePrice < inc.xATRTrailingStop.Last() && below // sell
buy := closePrice > inc.xATRTrailingStop.Last(0) && above // buy
sell := closePrice < inc.xATRTrailingStop.Last(0) && below // sell
inc.buyValue.Push(buy)
inc.sellValue.Push(sell)

View File

@ -71,7 +71,7 @@ func Test_ATR2(t *testing.T) {
stream.EmitKLineClosed(k)
}
got := atr.Last()
got := atr.Last(0)
diff := math.Trunc((got-tt.want)*100) / 100
if diff != 0 {
t.Errorf("ATR2() = %v, want %v", got, tt.want)

View File

@ -30,7 +30,7 @@ func (s *EWMAStream) calculateAndPush(v float64) {
}
func (s *EWMAStream) calculate(v float64) float64 {
last := s.slice.Last()
last := s.slice.Last(0)
m := s.multiplier
return (1.0-m)*last + m*v
}

View File

@ -32,8 +32,8 @@ func (s *RSIStream) calculate(_ float64) float64 {
var sourceLen = s.source.Length()
var limit = min(s.window, sourceLen)
for i := 0; i < limit; i++ {
value := s.source.Index(i)
prev := s.source.Index(i + 1)
value := s.source.Last(i)
prev := s.source.Last(i + 1)
change := value - prev
if change >= 0 {
gainSum += change

View File

@ -72,7 +72,7 @@ func Test_TR_and_RMA(t *testing.T) {
stream.EmitKLineClosed(k)
}
got := rma.Last()
got := rma.Last(0)
diff := math.Trunc((got-tt.want)*100) / 100
if diff != 0 {
t.Errorf("RMA(TR()) = %v, want %v", got, tt.want)

View File

@ -58,18 +58,18 @@ func (inc *VIDYA) Update(value float64) {
change := types.Change(&inc.input)
CMO := math.Abs(types.Sum(change, inc.Window) / types.Sum(types.Abs(change), inc.Window))
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 {
inc.Values = inc.Values[MaxNumOfEWMATruncateSize-1:]
}
}
func (inc *VIDYA) Last() float64 {
return inc.Values.Last()
func (inc *VIDYA) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *VIDYA) Index(i int) float64 {
return inc.Values.Index(i)
return inc.Last(i)
}
func (inc *VIDYA) Length() int {
@ -86,12 +86,12 @@ func (inc *VIDYA) CalculateAndUpdate(allKLines []types.KLine) {
if inc.input.Length() == 0 {
for _, k := range allKLines {
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
} else {
k := allKLines[len(allKLines)-1]
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}

View File

@ -10,10 +10,10 @@ import (
func Test_VIDYA(t *testing.T) {
vidya := &VIDYA{IntervalWindow: types.IntervalWindow{Window: 16}}
vidya.Update(1)
assert.Equal(t, vidya.Last(), 1.)
assert.Equal(t, vidya.Last(0), 1.)
vidya.Update(2)
newV := 2./17.*2. + 1.*(1.-2./17.)
assert.Equal(t, vidya.Last(), newV)
assert.Equal(t, vidya.Last(0), newV)
vidya.Update(1)
assert.Equal(t, vidya.Last(), vidya.Index(1))
assert.Equal(t, vidya.Last(0), vidya.Index(1))
}

View File

@ -26,18 +26,12 @@ type Volatility struct {
UpdateCallbacks []func(value float64)
}
func (inc *Volatility) Last() float64 {
if len(inc.Values) == 0 {
return 0.0
}
return inc.Values[len(inc.Values)-1]
func (inc *Volatility) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *Volatility) Index(i int) float64 {
if len(inc.Values)-i <= 0 {
return 0.0
}
return inc.Values[len(inc.Values)-i-1]
return inc.Last(i)
}
func (inc *Volatility) Length() int {

View File

@ -56,20 +56,12 @@ func (inc *VWAP) Update(price, volume float64) {
inc.Values.Push(vwap)
}
func (inc *VWAP) Last() float64 {
if len(inc.Values) == 0 {
return 0.0
}
return inc.Values[len(inc.Values)-1]
func (inc *VWAP) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *VWAP) Index(i int) float64 {
length := len(inc.Values)
if length == 0 || length-i-1 < 0 {
return 0
}
return inc.Values[length-i-1]
return inc.Last(i)
}
func (inc *VWAP) Length() int {
@ -91,7 +83,7 @@ func (inc *VWAP) CalculateAndUpdate(allKLines []types.KLine) {
inc.PushK(k)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
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 {
vwap.Update(priceF(k), k.Volume.Float64())
}
return vwap.Last()
return vwap.Last(0)
}

View File

@ -36,19 +36,12 @@ type VWMA struct {
updateCallbacks []func(value float64)
}
func (inc *VWMA) Last() float64 {
if len(inc.Values) == 0 {
return 0.0
}
return inc.Values[len(inc.Values)-1]
func (inc *VWMA) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *VWMA) Index(i int) float64 {
length := len(inc.Values)
if length == 0 || length-i-1 < 0 {
return 0
}
return inc.Values[length-i-1]
return inc.Last(i)
}
func (inc *VWMA) Length() int {
@ -70,8 +63,8 @@ func (inc *VWMA) Update(price, volume float64) {
inc.PriceVolumeSMA.Update(price * volume)
inc.VolumeSMA.Update(volume)
pv := inc.PriceVolumeSMA.Last()
v := inc.VolumeSMA.Last()
pv := inc.PriceVolumeSMA.Last(0)
v := inc.VolumeSMA.Last(0)
vwma := pv / v
inc.Values.Push(vwma)
}
@ -104,7 +97,7 @@ func (inc *VWMA) CalculateAndUpdate(allKLines []types.KLine) {
}
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) {

View File

@ -10,6 +10,7 @@ import (
// Refer: https://tradingview.com/script/aDymGrFx-Drift-Study-Inspired-by-Monte-Carlo-Simulations-with-BM-KL/
// Brownian Motion's drift factor
// could be used in Monte Carlo Simulations
//
//go:generate callbackgen -type WeightedDrift
type WeightedDrift struct {
types.SeriesBase
@ -54,7 +55,7 @@ func (inc *WeightedDrift) Update(value float64, weight float64) {
}
if inc.chng.Length() >= 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)
}
}
@ -77,7 +78,7 @@ func (inc *WeightedDrift) ZeroPoint() float64 {
} else {
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) {
@ -100,17 +101,11 @@ func (inc *WeightedDrift) TestUpdate(value float64, weight float64) *WeightedDri
}
func (inc *WeightedDrift) Index(i int) float64 {
if inc.Values == nil {
return 0
}
return inc.Values.Index(i)
return inc.Last(i)
}
func (inc *WeightedDrift) Last() float64 {
if inc.Values.Length() == 0 {
return 0
}
return inc.Values.Last()
func (inc *WeightedDrift) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *WeightedDrift) Length() int {
@ -130,12 +125,12 @@ func (inc *WeightedDrift) CalculateAndUpdate(allKLines []types.KLine) {
if inc.chng == nil {
for _, k := range allKLines {
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
} else {
k := allKLines[len(allKLines)-1]
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}

View File

@ -33,25 +33,17 @@ func (inc *WWMA) Update(value float64) {
inc.Values = inc.Values[MaxNumOfWWMATruncateSize-1:]
}
last := inc.Last()
last := inc.Last(0)
wma := last + (value-last)/float64(inc.Window)
inc.Values.Push(wma)
}
func (inc *WWMA) Last() float64 {
if len(inc.Values) == 0 {
return 0
}
return inc.Values[len(inc.Values)-1]
func (inc *WWMA) Last(i int) float64 {
return inc.Values.Last(i)
}
func (inc *WWMA) Index(i int) float64 {
if i >= len(inc.Values) {
return 0
}
return inc.Values[len(inc.Values)-1-i]
return inc.Last(i)
}
func (inc *WWMA) Length() int {
@ -76,7 +68,7 @@ func (inc *WWMA) CalculateAndUpdate(allKLines []types.KLine) {
if doable {
inc.PushK(k)
inc.LastOpenTime = k.StartTime.Time()
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}
}

View File

@ -27,17 +27,14 @@ type ZLEMA struct {
}
func (inc *ZLEMA) Index(i int) float64 {
if inc.zlema == nil {
return 0
}
return inc.zlema.Index(i)
return inc.Last(i)
}
func (inc *ZLEMA) Last() float64 {
func (inc *ZLEMA) Last(i int) float64 {
if inc.zlema == nil {
return 0
}
return inc.zlema.Last()
return inc.zlema.Last(i)
}
func (inc *ZLEMA) Length() int {
@ -74,12 +71,12 @@ func (inc *ZLEMA) CalculateAndUpdate(allKLines []types.KLine) {
if inc.zlema == nil {
for _, k := range allKLines {
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
} else {
k := allKLines[len(allKLines)-1]
inc.PushK(k)
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
}

View File

@ -46,7 +46,7 @@ func Test_ZLEMA(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
zlema := ZLEMA{IntervalWindow: types.IntervalWindow{Window: 16}}
zlema.CalculateAndUpdate(tt.kLines)
last := zlema.Last()
last := zlema.Last(0)
assert.InDelta(t, tt.want, last, Delta)
assert.InDelta(t, tt.next, zlema.Index(1), Delta)
assert.Equal(t, tt.all, zlema.Length())

View File

@ -59,9 +59,9 @@ func (d *DynamicExposureBollBand) initialize(symbol string, session *bbgo.Exchan
// getMaxExposure returns the max exposure
func (d *DynamicExposureBollBand) getMaxExposure(price float64, trend types.Direction) (fixedpoint.Value, error) {
downBand := d.dynamicExposureBollBand.DownBand.Last()
upBand := d.dynamicExposureBollBand.UpBand.Last()
sma := d.dynamicExposureBollBand.SMA.Last()
downBand := d.dynamicExposureBollBand.DownBand.Last(0)
upBand := d.dynamicExposureBollBand.UpBand.Last(0)
sma := d.dynamicExposureBollBand.SMA.Last(0)
log.Infof("dynamicExposureBollBand bollinger band: up %f sma %f down %f", upBand, sma, downBand)
bandPercentage := 0.0

View File

@ -114,7 +114,7 @@ func (ds *DynamicAmpSpread) update(kline types.KLine) {
func (ds *DynamicAmpSpread) getAskSpread() (askSpread float64, err error) {
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 {
log.WithError(err).Errorf("can not calculate dynamicAskSpread")
return 0, err
@ -128,7 +128,7 @@ func (ds *DynamicAmpSpread) getAskSpread() (askSpread float64, err error) {
func (ds *DynamicAmpSpread) getBidSpread() (bidSpread float64, err error) {
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 {
log.WithError(err).Errorf("can not calculate dynamicBidSpread")
return 0, err
@ -224,12 +224,12 @@ func (ds *DynamicSpreadBollWidthRatio) getWeightedBBWidthRatio(positiveSigmoid b
// - To ask spread, the higher neutral band get greater ratio
// - To bid spread, the lower neutral band get greater ratio
defaultMid := ds.defaultBoll.SMA.Last()
defaultUpper := ds.defaultBoll.UpBand.Last()
defaultLower := ds.defaultBoll.DownBand.Last()
defaultMid := ds.defaultBoll.SMA.Last(0)
defaultUpper := ds.defaultBoll.UpBand.Last(0)
defaultLower := ds.defaultBoll.DownBand.Last(0)
defaultWidth := defaultUpper - defaultLower
neutralUpper := ds.neutralBoll.UpBand.Last()
neutralLower := ds.neutralBoll.DownBand.Last()
neutralUpper := ds.neutralBoll.UpBand.Last(0)
neutralLower := ds.neutralBoll.DownBand.Last(0)
factor := defaultWidth / ds.Sensitivity
var weightedUpper, weightedLower, weightedDivUpper, weightedDivLower float64
if positiveSigmoid {

View File

@ -92,7 +92,7 @@ func (s *PerTrade) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
// min-max scaling
ofsMax := orderFlowSize.Tail(100).Max()
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
orderFlowSizeMinMax.Push(ofsMinMax)
}
@ -102,7 +102,7 @@ func (s *PerTrade) Bind(session *bbgo.ExchangeSession, orderExecutor *bbgo.Gener
// min-max scaling
ofnMax := orderFlowNumber.Tail(100).Max()
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
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 {
stddev := stat.StdDev(fs, nil)
if fs.Last() > fs.Mean()+multiplier*stddev {
if fs.Last(0) > fs.Mean()+multiplier*stddev {
return 1
} else if fs.Last() < fs.Mean()-multiplier*stddev {
} else if fs.Last(0) < fs.Mean()-multiplier*stddev {
return -1
}
return 0

View File

@ -135,7 +135,7 @@ func (s *Strategy) generateGridBuyOrders(session *bbgo.ExchangeSession) ([]types
ema99 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 99})
ema25 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 25})
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")
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})
ema25 := s.StandardIndicatorSet.EWMA(types.IntervalWindow{Interval: s.Interval, Window: 25})
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")
return nil, nil
}

View File

@ -126,7 +126,7 @@ func (ds *DynamicSpreadAmpSettings) update(kline types.KLine) {
func (ds *DynamicSpreadAmpSettings) getAskSpread() (askSpread float64, err error) {
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 {
log.WithError(err).Errorf("can not calculate dynamicAskSpread")
return 0, err
@ -140,7 +140,7 @@ func (ds *DynamicSpreadAmpSettings) getAskSpread() (askSpread float64, err error
func (ds *DynamicSpreadAmpSettings) getBidSpread() (bidSpread float64, err error) {
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 {
log.WithError(err).Errorf("can not calculate dynamicBidSpread")
return 0, err
@ -224,12 +224,12 @@ func (ds *DynamicSpreadBollWidthRatioSettings) getWeightedBBWidthRatio(positiveS
// - To ask spread, the higher neutral band get greater ratio
// - To bid spread, the lower neutral band get greater ratio
defaultMid := ds.defaultBoll.SMA.Last()
defaultUpper := ds.defaultBoll.UpBand.Last()
defaultLower := ds.defaultBoll.DownBand.Last()
defaultMid := ds.defaultBoll.SMA.Last(0)
defaultUpper := ds.defaultBoll.UpBand.Last(0)
defaultLower := ds.defaultBoll.DownBand.Last(0)
defaultWidth := defaultUpper - defaultLower
neutralUpper := ds.neutralBoll.UpBand.Last()
neutralLower := ds.neutralBoll.DownBand.Last()
neutralUpper := ds.neutralBoll.UpBand.Last(0)
neutralLower := ds.neutralBoll.DownBand.Last(0)
factor := defaultWidth / ds.Sensitivity
var weightedUpper, weightedLower, weightedDivUpper, weightedDivLower float64
if positiveSigmoid {

View File

@ -278,9 +278,9 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k
baseBalance, hasBaseBalance := balances[s.Market.BaseCurrency]
quoteBalance, hasQuoteBalance := balances[s.Market.QuoteCurrency]
downBand := s.defaultBoll.DownBand.Last()
upBand := s.defaultBoll.UpBand.Last()
sma := s.defaultBoll.SMA.Last()
downBand := s.defaultBoll.DownBand.Last(0)
upBand := s.defaultBoll.UpBand.Last(0)
sma := s.defaultBoll.SMA.Last(0)
log.Infof("%s bollinger band: up %f sma %f down %f", s.Symbol, upBand, sma, downBand)
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
// THEN: we apply strongUpTrend skew
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")
return
}
@ -410,7 +410,7 @@ func (s *Strategy) placeOrders(ctx context.Context, midPrice fixedpoint.Value, k
canSell = false
}
if s.BuyBelowNeutralSMA && midPrice.Float64() > s.neutralBoll.SMA.Last() {
if s.BuyBelowNeutralSMA && midPrice.Float64() > s.neutralBoll.SMA.Last(0) {
canBuy = false
}

View File

@ -12,7 +12,7 @@ const (
)
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
}

View File

@ -17,19 +17,19 @@ func (s *DriftMA) Update(value, weight float64) {
if s.ma1.Length() == 0 {
return
}
s.drift.Update(s.ma1.Last(), weight)
s.drift.Update(s.ma1.Last(0), weight)
if s.drift.Length() == 0 {
return
}
s.ma2.Update(s.drift.Last())
s.ma2.Update(s.drift.Last(0))
}
func (s *DriftMA) Last() float64 {
return s.ma2.Last()
func (s *DriftMA) Last(int) float64 {
return s.ma2.Last(0)
}
func (s *DriftMA) Index(i int) float64 {
return s.ma2.Index(i)
return s.ma2.Last(i)
}
func (s *DriftMA) Length() int {

View File

@ -9,7 +9,7 @@ func (s *Strategy) CheckStopLoss() bool {
}
}
if s.UseAtr {
atr := s.atr.Last()
atr := s.atr.Last(0)
if s.sellPrice > 0 && s.sellPrice+atr <= s.highestPrice ||
s.buyPrice > 0 && s.buyPrice-atr >= s.lowestPrice {
return true

View File

@ -261,8 +261,8 @@ func (s *Strategy) initIndicators(store *bbgo.SerialMarketDataStore) error {
high := kline.High.Float64()
low := kline.Low.Float64()
s.ma.Update(source)
s.stdevHigh.Update(high - s.ma.Last())
s.stdevLow.Update(s.ma.Last() - low)
s.stdevHigh.Update(high - s.ma.Last(0))
s.stdevLow.Update(s.ma.Last(0) - low)
s.drift.Update(source, kline.Volume.Abs().Float64())
s.trendLine.Update(source)
s.atr.PushK(kline)
@ -485,7 +485,7 @@ func (s *Strategy) klineHandlerMin(ctx context.Context, kline types.KLine, count
return
}
// for doing the trailing stoploss during backtesting
atr := s.atr.Last()
atr := s.atr.Last(0)
price := s.getLastPrice()
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.atr.PushK(kline)
atr := s.atr.Last()
atr := s.atr.Last(0)
price := kline.Close // s.getLastPrice()
pricef := price.Float64()
@ -563,7 +563,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine, counter
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()
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)
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 s.priceLines.Index(1) > s.priceLines.Last() {
if s.priceLines.Index(1) > s.priceLines.Last(0) {
longCondition = false
} else {
shortCondition = false
@ -625,7 +625,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine, counter
}
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 {
source = price
}
@ -654,7 +654,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine, counter
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)
if err != nil {
@ -675,12 +675,12 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine, counter
return
}
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 {
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.Short = true

View File

@ -11,8 +11,8 @@ func (s *ElliottWave) Index(i int) float64 {
return s.maQuick.Index(i)/s.maSlow.Index(i) - 1.0
}
func (s *ElliottWave) Last() float64 {
return s.maQuick.Last()/s.maSlow.Last() - 1.0
func (s *ElliottWave) Last(int) float64 {
return s.maQuick.Last(0)/s.maSlow.Last(0) - 1.0
}
func (s *ElliottWave) Length() int {

View File

@ -199,12 +199,12 @@ func (s *Strategy) smartCancel(ctx context.Context, pricef float64) int {
if s.counter-s.orderPendingCounter[order.OrderID] >= s.PendingMinInterval {
toCancel = true
} 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
}
} else if order.Side == types.SideTypeSell {
// 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
}
} else {
@ -425,7 +425,7 @@ func (s *Strategy) klineHandlerMin(ctx context.Context, kline types.KLine) {
stoploss := s.Stoploss.Float64()
price := s.getLastPrice()
pricef := price.Float64()
atr := s.atr.Last()
atr := s.atr.Last(0)
numPending := s.smartCancel(ctx, pricef)
if numPending > 0 {
@ -476,7 +476,7 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
s.smartCancel(ctx, pricef)
atr := s.atr.Last()
atr := s.atr.Last(0)
ewo := types.Array(s.ewo, 4)
if len(ewo) < 4 {
return

View File

@ -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) {
closePriceF := closePrice.Float64()
movingAveragePriceF := indicator.Last()
movingAveragePriceF := indicator.Last(0)
// skip it if it's near zero because it's not loaded yet
if movingAveragePriceF < 0.0001 {

View File

@ -27,11 +27,11 @@ func NewHeikinAshi(size int) *HeikinAshi {
func (s *HeikinAshi) Print() string {
return fmt.Sprintf("Heikin c: %.3f, o: %.3f, h: %.3f, l: %.3f, v: %.3f",
s.Close.Last(),
s.Open.Last(),
s.High.Last(),
s.Low.Last(),
s.Volume.Last())
s.Close.Last(0),
s.Open.Last(0),
s.High.Last(0),
s.Low.Last(0),
s.Volume.Last(0))
}
func (inc *HeikinAshi) Update(kline types.KLine) {
@ -40,7 +40,7 @@ func (inc *HeikinAshi) Update(kline types.KLine) {
high := kline.High.Float64()
low := kline.Low.Float64()
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.Open.Update(newOpen)
inc.High.Update(math.Max(math.Max(high, newOpen), newClose))

View File

@ -139,7 +139,7 @@ func NewCCISTOCH(i types.Interval, filterHigh, filterLow float64) *CCISTOCH {
func (inc *CCISTOCH) Update(cloze float64) {
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())
}
@ -180,19 +180,19 @@ type VWEMA struct {
V types.UpdatableSeries
}
func (inc *VWEMA) Last() float64 {
return inc.PV.Last() / inc.V.Last()
func (inc *VWEMA) Last(int) float64 {
return inc.PV.Last(0) / inc.V.Last(0)
}
func (inc *VWEMA) Index(i int) float64 {
if i >= inc.PV.Length() {
return 0
}
vi := inc.V.Index(i)
vi := inc.V.Last(i)
if vi == 0 {
return 0
}
return inc.PV.Index(i) / vi
return inc.PV.Last(i) / vi
}
func (inc *VWEMA) Length() int {
@ -262,11 +262,11 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
if s.heikinAshi.Close.Length() == 0 {
for _, kline := range window {
s.heikinAshi.Update(kline)
s.ccis.Update(getSource(window).Last())
s.ccis.Update(getSource(window).Last(0))
}
} else {
s.heikinAshi.Update(window[len(window)-1])
s.ccis.Update(getSource(window).Last())
s.ccis.Update(getSource(window).Last(0))
}
})
if s.UseEma {
@ -283,7 +283,7 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
ema34.Update(cloze)
}
} else {
cloze := getSource(window).Last()
cloze := getSource(window).Last(0)
ema5.Update(cloze)
ema34.Update(cloze)
}
@ -306,7 +306,7 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
sma34.Update(cloze)
}
} else {
cloze := getSource(window).Last()
cloze := getSource(window).Last(0)
sma5.Update(cloze)
sma34.Update(cloze)
}
@ -330,14 +330,14 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
vols := getVol(window)
if evwma5.PV.Length() == 0 {
for i := clozes.Length() - 1; i >= 0; i-- {
price := clozes.Index(i)
vol := vols.Index(i)
price := clozes.Last(i)
vol := vols.Last(i)
evwma5.UpdateVal(price, vol)
evwma34.UpdateVal(price, vol)
}
} else {
price := clozes.Last()
vol := vols.Last()
price := clozes.Last(0)
vol := vols.Last(0)
evwma5.UpdateVal(price, vol)
evwma34.UpdateVal(price, vol)
}
@ -363,7 +363,7 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
sig.Update(ewoValue)
}
} else {
sig.Update(s.ewo.Last())
sig.Update(s.ewo.Last(0))
}
})
s.ewoSignal = sig
@ -381,7 +381,7 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
sig.Update(ewoValue)
}
} else {
sig.Update(s.ewo.Last())
sig.Update(s.ewo.Last(0))
}
})
s.ewoSignal = sig
@ -398,13 +398,13 @@ func (s *Strategy) SetupIndicators(store *bbgo.MarketDataStore) {
// lazy init
ewoVals := s.ewo.Reverse()
for i, ewoValue := range ewoVals {
vol := window.Volume().Index(i)
vol := window.Volume().Last(i)
sig.PV.Update(ewoValue * vol)
sig.V.Update(vol)
}
} else {
vol := window.Volume().Last()
sig.PV.Update(s.ewo.Last() * vol)
vol := window.Volume().Last(0)
sig.PV.Update(s.ewo.Last(0) * 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)
// - SL by s.StopLoss (Abs(price_diff / price) > s.StopLoss)
// - 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)
// * sell signal on (crossunder 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)
//
// - and filtered by the following rules:
// * 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
// - 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
//
// - or entry when ma34 +- atr * 3 gets touched
// - 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)
@ -784,8 +786,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
s.SetupIndicators(store)
// local peak of ewo
shortSig := s.ewo.Last() < s.ewo.Index(1) && s.ewo.Index(1) > s.ewo.Index(2)
longSig := 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(0) > s.ewo.Last(1) && s.ewo.Last(1) < s.ewo.Last(2)
sellOrderTPSL := func(price fixedpoint.Value) {
lastPrice := s.GetLastPrice()
@ -798,8 +800,8 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
}
balances := session.GetAccount().Balances()
quoteBalance := balances[s.Market.QuoteCurrency].Available
atr := fixedpoint.NewFromFloat(s.atr.Last())
atrx2 := fixedpoint.NewFromFloat(s.atr.Last() * 2)
atr := fixedpoint.NewFromFloat(s.atr.Last(0))
atrx2 := fixedpoint.NewFromFloat(s.atr.Last(0) * 2)
buyall := false
if s.bottomPrice.IsZero() || s.bottomPrice.Compare(price) > 0 {
s.bottomPrice = price
@ -809,7 +811,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
spBack := s.sellPrice
reason := -1
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
if lastPrice.Compare(s.sellPrice) < 0 && (longSig ||
(!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()
baseBalance := balances[s.Market.BaseCurrency].Available
atr := fixedpoint.NewFromFloat(s.atr.Last())
atrx2 := fixedpoint.NewFromFloat(s.atr.Last() * 2)
atr := fixedpoint.NewFromFloat(s.atr.Last(0))
atrx2 := fixedpoint.NewFromFloat(s.atr.Last(0) * 2)
sellall := false
if s.peakPrice.IsZero() || s.peakPrice.Compare(price) < 0 {
s.peakPrice = price
@ -915,7 +917,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
reason := -1
if baseBalance.Compare(s.Market.MinQuantity) >= 0 && baseBalance.Mul(lastPrice).Compare(s.Market.MinNotional) >= 0 {
// TP
base := fixedpoint.NewFromFloat(s.ma34.Last())
base := fixedpoint.NewFromFloat(s.ma34.Last(0))
if lastPrice.Compare(s.buyPrice) > 0 && (shortSig ||
(!atrx2.IsZero() && base.Add(atrx2).Compare(lastPrice) <= 0)) {
sellall = true
@ -1089,10 +1091,10 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
balances := session.GetAccount().Balances()
baseBalance := balances[s.Market.BaseCurrency].Total()
quoteBalance := balances[s.Market.QuoteCurrency].Total()
atr := fixedpoint.NewFromFloat(s.atr.Last())
atr := fixedpoint.NewFromFloat(s.atr.Last(0))
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",
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 {
@ -1104,21 +1106,21 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se
priceChangeRate := (priceHighest - priceLowest) / priceHighest / 14
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)
shortSignal := types.CrossUnder(s.ewo, s.ewoSignal)
base := s.ma34.Last()
sellLine := base + s.atr.Last()*3
buyLine := base - s.atr.Last()*3
base := s.ma34.Last(0)
sellLine := base + s.atr.Last(0)*3
buyLine := base - s.atr.Last(0)*3
clozes := getClose(window)
opens := getOpen(window)
// get trend flags
bull := clozes.Last() > opens.Last()
breakThrough := clozes.Last() > s.ma5.Last() && clozes.Last() > s.ma34.Last()
breakDown := clozes.Last() < s.ma5.Last() && clozes.Last() < s.ma34.Last()
bull := clozes.Last(0) > opens.Last(0)
breakThrough := clozes.Last(0) > s.ma5.Last(0) && clozes.Last(0) > s.ma34.Last(0)
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
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
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
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
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
if closeOrder, _ := s.PlaceSellOrder(ctx, price); closeOrder != nil {

View File

@ -34,14 +34,14 @@ func (inc *MOM) Index(i int) float64 {
if inc.Values == nil {
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 {
return 0
}
return inc.Values.Last()
return inc.Values.Last(0)
}
func (inc *MOM) Length() int {
@ -62,7 +62,7 @@ func (inc *MOM) Update(open, close float64) {
inc.opens.Update(open)
inc.closes.Update(close)
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)
}
}
@ -72,11 +72,11 @@ func (inc *MOM) CalculateAndUpdate(allKLines []types.KLine) {
for _, k := range allKLines {
inc.PushK(k)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
} else {
k := allKLines[len(allKLines)-1]
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.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) {

View File

@ -35,12 +35,12 @@ func (inc *PMR) Update(price float64) {
}
inc.SMA.Update(price)
if inc.SMA.Length() >= inc.Window {
reversion := inc.SMA.Last() / price
reversion := inc.SMA.Last(0) / price
inc.Values.Push(reversion)
}
}
func (inc *PMR) Last() float64 {
func (inc *PMR) Last(int) float64 {
if len(inc.Values) == 0 {
return 0
}
@ -65,11 +65,11 @@ func (inc *PMR) CalculateAndUpdate(allKLines []types.KLine) {
for _, k := range allKLines {
inc.PushK(k)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
} else {
k := allKLines[len(allKLines)-1]
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.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
func CalculateKLinesPMR(allKLines []types.KLine, window int) float64 {

View File

@ -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 {
return 0
}
@ -72,11 +72,11 @@ func (inc *PVD) CalculateAndUpdate(allKLines []types.KLine) {
for _, k := range allKLines {
inc.PushK(k)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
} else {
k := allKLines[len(allKLines)-1]
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.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
func CalculateKLinesPVD(allKLines []types.KLine, window int) float64 {

View File

@ -30,12 +30,12 @@ func (inc *RR) Update(price float64) {
inc.prices = types.NewQueue(inc.Window)
}
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)
}
func (inc *RR) Last() float64 {
func (inc *RR) Last(int) float64 {
if len(inc.Values) == 0 {
return 0
}
@ -60,11 +60,11 @@ func (inc *RR) CalculateAndUpdate(allKLines []types.KLine) {
for _, k := range allKLines {
inc.PushK(k)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
} else {
k := allKLines[len(allKLines)-1]
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.EndTime = k.EndTime.Time()
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
func (inc *RR) LoadK(allKLines []types.KLine) {
for _, k := range allKLines {
inc.PushK(k)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
}
//func calculateReturn(klines []types.KLine, window int, val KLineValueMapper) (float64, error) {

View File

@ -33,14 +33,14 @@ func (inc *VMOM) Index(i int) float64 {
if inc.Values == nil {
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 {
return 0
}
return inc.Values.Last()
return inc.Values.Last(0)
}
func (inc *VMOM) Length() int {
@ -59,7 +59,7 @@ func (inc *VMOM) Update(volume float64) {
}
inc.volumes.Update(volume)
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)
}
}
@ -69,11 +69,11 @@ func (inc *VMOM) CalculateAndUpdate(allKLines []types.KLine) {
for _, k := range allKLines {
inc.PushK(k)
}
inc.EmitUpdate(inc.Last())
inc.EmitUpdate(inc.Last(0))
} else {
k := allKLines[len(allKLines)-1]
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.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) {

View File

@ -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)
input := []float64{
s.divergence.Last(),
s.reversion.Last(),
s.drift.Last(),
s.momentum.Last(),
s.volume.Last(),
s.divergence.Last(0),
s.reversion.Last(0),
s.drift.Last(0),
s.momentum.Last(0),
s.volume.Last(0),
}
pred := model.Predict(input)
predLst.Update(pred)

View File

@ -213,7 +213,7 @@ func (s *Strategy) generateSubmitOrders(ctx context.Context) ([]types.SubmitOrde
log.Infof("mid price: %+v", midPrice)
if s.ATRMultiplier.Float64() > 0 {
atr := fixedpoint.NewFromFloat(s.atr.Last())
atr := fixedpoint.NewFromFloat(s.atr.Last(0))
log.Infof("atr: %s", atr.String())
s.HalfSpreadRatio = s.ATRMultiplier.Mul(atr).Div(midPrice)
log.Infof("half spread ratio: %s", s.HalfSpreadRatio.String())

View File

@ -75,7 +75,7 @@ func (s *Strategy) updateBidOrders(orderExecutor bbgo.OrderExecutor, session *bb
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
for i := 0; i < s.GridNum; i++ {

View File

@ -21,7 +21,7 @@ type A18 struct {
UpdateCallbacks []func(val float64)
}
func (inc *A18) Last() float64 {
func (inc *A18) Last(int) float64 {
if len(inc.Values) == 0 {
return 0.0
}
@ -84,8 +84,8 @@ func calculateA18(klines []types.KLine, valClose KLineValueMapper) (float64, err
closes.Push(valClose(k))
}
delay5 := closes.Index(4)
curr := closes.Index(0)
delay5 := closes.Last(4)
curr := closes.Last(0)
alpha := curr / delay5
return alpha, nil

Some files were not shown because too many files have changed in this diff Show More