diff --git a/.gitignore b/.gitignore index f41e4ff8a7..5b42b45fd8 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ *.out .idea +.vscode # Dependency directories (remove the comment below to include it) # vendor/ diff --git a/go.mod b/go.mod index 91eca5f8be..a5de5e7e4d 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/c9s/bbgo -go 1.18 +go 1.20 require ( github.com/DATA-DOG/go-sqlmock v1.5.0 diff --git a/pkg/bbgo/indicator_set.go b/pkg/bbgo/indicator_set.go index 0d5dbd3636..eb15722128 100644 --- a/pkg/bbgo/indicator_set.go +++ b/pkg/bbgo/indicator_set.go @@ -3,7 +3,7 @@ package bbgo import ( "github.com/sirupsen/logrus" - "github.com/c9s/bbgo/pkg/indicator/v2" + indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/types" ) diff --git a/pkg/datatype/floats/slice.go b/pkg/datatype/floats/slice.go index 1d610a4f53..bace6adbf0 100644 --- a/pkg/datatype/floats/slice.go +++ b/pkg/datatype/floats/slice.go @@ -36,6 +36,18 @@ func (s Slice) Max() float64 { return floats.Max(s) } +func (s Slice) IndexOfMaxValue() int { + maxIdx := 0 + maxVal := s[0] + for i, val := range s { + if val > maxVal { + maxVal = val + maxIdx = i + } + } + return maxIdx +} + func (s Slice) Min() float64 { return floats.Min(s) } @@ -87,6 +99,17 @@ func (s Slice) Mean() (mean float64) { return s.Sum() / float64(length) } +/* Calculates the variance across the dataset of float64s */ +func (s Slice) Variance() float64 { + var variance = .0 + + for _, diff := range s { + variance += math.Pow(diff-s.Mean(), 2) + } + + return variance / float64(len(s)) +} + func (s Slice) Tail(size int) Slice { length := len(s) if length <= size { diff --git a/pkg/exchange/bitget/bitgetapi/types.go b/pkg/exchange/bitget/bitgetapi/types.go index 1aa6280f77..30442e13b0 100644 --- a/pkg/exchange/bitget/bitgetapi/types.go +++ b/pkg/exchange/bitget/bitgetapi/types.go @@ -14,6 +14,7 @@ const ( OrderTypeMarket OrderType = "market" ) +// OrderSide represents the side of an order: Buy (long) or Sell (short). type OrderSide string const ( diff --git a/pkg/fixedpoint/helpers.go b/pkg/fixedpoint/helpers.go index cb585e5c0f..5e4e87ae49 100644 --- a/pkg/fixedpoint/helpers.go +++ b/pkg/fixedpoint/helpers.go @@ -13,3 +13,16 @@ func Avg(values []Value) (avg Value) { avg = s.Div(NewFromInt(int64(len(values)))) return avg } + +// maxDiff is the maximum deviation between a and b to consider them approximately equal +func ApproxEqual(a, b Value, maxDiff float64) bool { + // Calculate the absolute difference + diff := Abs(a.Sub(b)) + + // Define the small multiple + smallMultiple := a.Mul(NewFromFloat(maxDiff)) + + // Compare the absolute difference to the small multiple + cmp := diff.Compare(smallMultiple) + return cmp == -1 || cmp == 0 +} diff --git a/pkg/indicator/util.go b/pkg/indicator/util.go index ce5c209b2d..27314df772 100644 --- a/pkg/indicator/util.go +++ b/pkg/indicator/util.go @@ -1,6 +1,6 @@ package indicator -func max(x, y int) int { +func Max(x, y int) int { if x > y { return x } diff --git a/pkg/indicator/v2/abondoned_baby.go b/pkg/indicator/v2/abondoned_baby.go new file mode 100644 index 0000000000..75751b1ebf --- /dev/null +++ b/pkg/indicator/v2/abondoned_baby.go @@ -0,0 +1,68 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type AbondonedBabyStream struct { + *types.Float64Series + + window int +} + +func AbondonedBaby(source KLineSubscription) *AbondonedBabyStream { + s := &AbondonedBabyStream{ + Float64Series: types.NewFloat64Series(), + window: 3, + } + + source.AddSubscriber(func(kLine types.KLine) { + + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + one = source.Last(2) + two = source.Last(1) + three = source.Last(0) + abs = fixedpoint.Abs((two.Close.Sub(two.Open).Div(two.Open))).Float64() + ) + + if one.Open.Float64() < one.Close.Float64() { + if one.High.Float64() < two.Low.Float64() { + if abs < threshold { + if three.Open.Float64() < two.Low.Float64() && + three.Close.Float64() < three.Open.Float64() { + output = -1.0 + } + } + } + } + + if one.Open.Float64() > one.Close.Float64() { + if one.Low.Float64() > two.High.Float64() { + if abs <= threshold { + if three.Open.Float64() > two.High.Float64() && + three.Close.Float64() > three.Open.Float64() { + output = 1.0 + } + } + } + } + + s.Float64Series.PushAndEmit(output) + + }) + + return s +} + +func (s *AbondonedBabyStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/abondoned_baby_test.go b/pkg/indicator/v2/abondoned_baby_test.go new file mode 100644 index 0000000000..d7d2f6701f --- /dev/null +++ b/pkg/indicator/v2/abondoned_baby_test.go @@ -0,0 +1,28 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestAbandonedBaby(t *testing.T) { + ts := []types.KLine{ + {Open: n(90), Low: n(85), High: n(105), Close: n(100)}, + {Open: n(125), Low: n(120), High: n(135), Close: n(130)}, + {Open: n(110), Low: n(92), High: n(115), Close: n(95)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := AbondonedBaby(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestAbandonedBaby Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } +} diff --git a/pkg/indicator/v2/accumulation_distiribution.go b/pkg/indicator/v2/accumulation_distiribution.go new file mode 100644 index 0000000000..c635158c21 --- /dev/null +++ b/pkg/indicator/v2/accumulation_distiribution.go @@ -0,0 +1,47 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +// Accumulation/Distribution Indicator (A/D). Cumulative indicator +// that uses volume and price to assess whether a stock is +// being accumulated or distributed. +// +// MFM = ((Closing - Low) - (High - Closing)) / (High - Low) +// MFV = MFM * Period Volume +// AD = Previous AD + CMFV +type AccumulationDistributionStream struct { + *types.Float64Series +} + +func AccumulationDistribution(source KLineSubscription) *AccumulationDistributionStream { + s := &AccumulationDistributionStream{ + Float64Series: types.NewFloat64Series(), + } + + source.AddSubscriber(func(v types.KLine) { + var ( + i = s.Slice.Length() + output = fixedpoint.NewFromInt(0) + cl = v.Close.Sub(v.Low) + hc = v.High.Sub(v.Close) + hl = v.High.Sub(v.Low) + ) + + if i > 0 { + output = fixedpoint.NewFromFloat(s.Slice.Last(0)) + } + + output = output.Add(v.Volume.Mul(cl.Sub(hc).Div(hl))) + + s.PushAndEmit(output.Float64()) + }) + + return s +} + +func (s *AccumulationDistributionStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/accumulation_distiribution_test.go b/pkg/indicator/v2/accumulation_distiribution_test.go new file mode 100644 index 0000000000..0547da6329 --- /dev/null +++ b/pkg/indicator/v2/accumulation_distiribution_test.go @@ -0,0 +1,41 @@ +package indicatorv2 + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +func TestAccumulationDistribution(t *testing.T) { + high := []byte(`[62.3400,62.0500,62.2700,60.7900,59.9300,61.7500,60.0000,59.0000,59.0700,59.2200,58.7500,58.6500,58.4700,58.2500,58.3500,59.8600,59.5299,62.1000,62.1600,62.6700,62.3800,63.7300,63.8500,66.1500,65.3400,66.4800,65.2300,63.4000,63.1800,62.7000]`) + low := []byte(`[61.3700,60.6900,60.1000,58.6100,58.7120,59.8600,57.9700,58.0200,57.4800,58.3000,57.8276,57.8600,57.9100,57.8333,57.5300,58.5800,58.3000,58.5300,59.8000,60.9300,60.1500,62.2618,63.0000,63.5800,64.0700,65.2000,63.2100,61.8800,61.1100,61.2500]`) + close := []byte(`[62.1500,60.8100,60.4500,59.1800,59.2400,60.2000,58.4800,58.2400,58.6900,58.6500,58.4700,58.0200,58.1700,58.0700,58.1300,58.9400,59.1000,61.9200,61.3700,61.6800,62.0900,62.8900,63.5300,64.0100,64.7700,65.2200,63.2800,62.4000,61.5500,62.6900]`) + volume := []byte(`[7849.025,11692.075,10575.307,13059.128,20733.508,29630.096,17705.294,7259.203,10474.629,5203.714,3422.865,3962.15,4095.905,3766.006,4239.335,8039.979,6956.717,18171.552,22225.894,14613.509,12319.763,15007.69,8879.667,22693.812,10191.814,10074.152,9411.62,10391.69,8926.512,7459.575]`) + buildKLines := func(high, low, close, volume []fixedpoint.Value) (kLines []types.KLine) { + for i := range high { + kLines = append(kLines, types.KLine{High: high[i], Low: low[i], Close: close[i], Volume: volume[i]}) + } + return kLines + } + var h, l, c, v []fixedpoint.Value + _ = json.Unmarshal(high, &h) + _ = json.Unmarshal(low, &l) + _ = json.Unmarshal(close, &c) + _ = json.Unmarshal(volume, &v) + + expected := []float64{4774, -4855, -12019, -18249, -21006, -39976, -48785, -52785, -47317, -48561, -47216, -49574, -49866, -49354, -47389, -50907, -48813, -32474, -25128, -27144, -18028, -20193, -18000, -33099, -32056, -41816, -50575, -53856, -58988, -51631} + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := AccumulationDistribution(kLines) + k := buildKLines(h, l, c, v) + for _, candle := range k { + stream.EmitKLineClosed(candle) + } + for i, v := range expected { + assert.InDelta(t, v, ind.Slice[i], 0.5, "Expected AccumulationDistribution.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } +} diff --git a/pkg/indicator/v2/alma.go b/pkg/indicator/v2/alma.go new file mode 100644 index 0000000000..37624d6e9b --- /dev/null +++ b/pkg/indicator/v2/alma.go @@ -0,0 +1,76 @@ +// Copyright 2022 The Coln Group Ltd +// SPDX-License-Identifier: MIT + +package indicatorv2 + +import ( + "math" + + "github.com/c9s/bbgo/pkg/types" +) + +const ( + // DefaultALMAOffset is the default offset for the ALMA indicator. + DefaultALMAOffset = 0.85 + + // DefaultALMASigma is the default sigma for the ALMA indicator. + DefaultALMASigma = 6 +) + +type ALMAStream struct { + // embedded struct + *types.Float64Series + + sample []float64 + offset float64 + sigma float64 + window int +} + +// ALMA is a modern low lag moving average. +// Ported from https://www.tradingview.com/pine-script-reference/#fun_alma +// NewALMA creates a new ALMA indicator with default parameters. +func ALMA(source types.Float64Source, window int) *ALMAStream { + return ALMAWithSigma(source, window, DefaultALMAOffset, DefaultALMASigma) +} + +// NewALMAWithSigma creates a new ALMA indicator with the given offset and sigma. +func ALMAWithSigma(source types.Float64Source, window int, offset, sigma float64) *ALMAStream { + s := &ALMAStream{ + Float64Series: types.NewFloat64Series(), + window: window, + offset: offset, + sigma: sigma, + } + s.Bind(source, s) + return s +} + +func (s *ALMAStream) Calculate(v float64) float64 { + + s.sample = WindowAppend(s.sample, s.window-1, v) + + if s.window < 1 { + return v + } + var ( + length = float64(s.window) + norm, sum float64 + offset = s.offset * (length - 1) + m = math.Floor(offset) + sig = length / s.sigma + ) + for i := 0; i < len(s.sample); i++ { + pow := math.Pow(float64(i)-m, 2) / (math.Pow(sig, 2) * 2) + weight := math.Exp(-1 * pow) + norm += weight + sum += s.sample[i] * weight + } + ma := sum / norm + + return ma +} + +func (s *ALMAStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/alma_test.go b/pkg/indicator/v2/alma_test.go new file mode 100644 index 0000000000..963c35e1f2 --- /dev/null +++ b/pkg/indicator/v2/alma_test.go @@ -0,0 +1,44 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestALMA(t *testing.T) { + + tests := []struct { + name string + giveV []float64 + giveLength int + want float64 + }{ + { + name: "Valid sample", + giveV: []float64{10, 89, 20, 43, 44, 33, 19}, + giveLength: 3, + want: 32.68047906324239, + }, + { + name: "0 length window", + giveV: []float64{10, 89, 20, 43, 44, 33, 19}, + giveLength: 0, + want: 19, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + source := types.NewFloat64Series() + alma := ALMA(source, tt.giveLength) + + for _, d := range tt.giveV { + source.PushAndEmit(d) + } + assert.Equal(t, tt.want, alma.Last(0)) + }) + } + +} diff --git a/pkg/indicator/v2/atrp.go b/pkg/indicator/v2/atrp.go index e22810ff78..431dec8740 100644 --- a/pkg/indicator/v2/atrp.go +++ b/pkg/indicator/v2/atrp.go @@ -1,6 +1,8 @@ package indicatorv2 -import "github.com/c9s/bbgo/pkg/types" +import ( + "github.com/c9s/bbgo/pkg/types" +) type ATRPStream struct { *types.Float64Series diff --git a/pkg/indicator/v2/awesome_osc_test.go b/pkg/indicator/v2/awesome_osc_test.go new file mode 100644 index 0000000000..3629d12b4f --- /dev/null +++ b/pkg/indicator/v2/awesome_osc_test.go @@ -0,0 +1,62 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/datatype/floats" + "github.com/c9s/bbgo/pkg/types" +) + +func Test_AwesomeOscillator(t *testing.T) { + tests := []struct { + name string + high []float64 + low []float64 + want floats.Slice + }{ + { + name: "AwesomeOscillator", + high: []float64{24.63, 24.69, 24.99, 25.36, 25.19, 25.17, 25.01, 24.96, 25.08, 25.25, 25.21, 25.37, 25.61, 25.58, 25.46, 25.33, 25.09, 25.03, 24.91, 24.89, 25.13, 24.63, 24.69, 24.99, 25.36, 25.19, 25.17, 25.01, 24.96, 25.08, 25.25, 25.21, 25.37, 25.61, 25.58, 25.46, 25.33, 25.09, 25.03, 24.91, 24.89, 25.13}, + low: []float64{24.63, 24.69, 24.99, 25.36, 25.19, 25.17, 25.01, 24.96, 25.08, 25.25, 25.21, 25.37, 25.61, 25.58, 25.46, 25.33, 25.09, 25.03, 24.91, 24.89, 25.13, 24.63, 24.69, 24.99, 25.36, 25.19, 25.17, 25.01, 24.96, 25.08, 25.25, 25.21, 25.37, 25.61, 25.58, 25.46, 25.33, 25.09, 25.03, 24.91, 24.89, 25.13}, + want: floats.Slice{0.0, 0.17, 0.24, 0.26, 0.28, 0.23, 0.12, -0.01, -0.12, -0.16}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stream := &types.StandardStream{} + prices := HL2(KLines(stream, "", "")) + + ind := AwesomeOscillator(prices) + + for _, k := range buildKLinesFromHL(tt.high, tt.low) { + stream.EmitKLineClosed(k) + + } + + if assert.Equal(t, len(tt.want)+32, len(ind.Slice)) { + for i, v := range tt.want { + assert.InDelta(t, v, ind.Slice[i+32], 0.005, "Expected awesome_osc.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } + } + }) + } +} + +func buildKLinesFromHL(high, low []float64) (klines []types.KLine) { + for i := range high { + klines = append(klines, types.KLine{High: n(high[i]), Low: n(low[i])}) + } + + return klines +} + +func buildKLinesFromC(closing []float64) (klines []types.KLine) { + for i := range closing { + klines = append(klines, types.KLine{Close: n(closing[i])}) + } + + return klines +} diff --git a/pkg/indicator/v2/awseome_osc.go b/pkg/indicator/v2/awseome_osc.go new file mode 100644 index 0000000000..e48b4e61cb --- /dev/null +++ b/pkg/indicator/v2/awseome_osc.go @@ -0,0 +1,78 @@ +package indicatorv2 + +import ( + "fmt" + + "github.com/c9s/bbgo/pkg/types" +) + +// https://github.com/Nikhil-Adithyan/Algorithmic-Trading-with-Awesome-Oscillator-in-Python/blob/master/Strategy_code.py +type AwesomeOscillatorStream struct { + // embedded structs + *types.Float64Series + sma5 *SMAStream + sma34 *SMAStream +} + +// Awesome Oscillator. +// +// Median Price = ((Low + High) / 2) >> need HL2Source +// AO = 5-Period SMA - 34-Period SMA. +// +// Returns ao. +func AwesomeOscillator(source types.Float64Source) *AwesomeOscillatorStream { + s := &AwesomeOscillatorStream{ + Float64Series: types.NewFloat64Series(), + sma5: SMA(source, 5), + sma34: SMA(source, 34), + } + s.Bind(source, s) + return s +} + +func (s *AwesomeOscillatorStream) Calculate(v float64) float64 { + if s.Length() < 33 { + return 0 + } + ao := 0.0 + if s.Slice.Length() > 0 { + ao = s.sma5.Last(0) - s.sma34.Last(0) + + var ( + prevao = s.sma5.Last(0) - s.sma34.Last(0) + currDiff = ao - v + prevDiff = prevao - s.Slice.Last(0) + crossOver = crossOver(currDiff, prevDiff, 0) + crossUnder = crossUnder(currDiff, prevDiff, 0) + ) + if crossOver { + fmt.Println("awesome oscillator changed to green: ", ao) + } + if crossUnder { + fmt.Println("awesome oscillator changed to red: ", ao) + } + } + + return ao +} + +func (s *AwesomeOscillatorStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} + +// todo move this +// CrossOver returns true if the latest series values cross above x +func crossOver(curr, prev, x float64) bool { + if prev < x && curr > x { + return true + } + return false +} + +// CrossDown returns true if the latest series values cross below x +func crossUnder(curr, prev, x float64) bool { + if prev > x && curr < x { + return true + } + return false +} diff --git a/pkg/indicator/v2/belthold.go b/pkg/indicator/v2/belthold.go new file mode 100644 index 0000000000..f00a5f4289 --- /dev/null +++ b/pkg/indicator/v2/belthold.go @@ -0,0 +1,62 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type BeltholdStream struct { + *types.Float64Series + + window int +} + +func Belthold(source KLineSubscription) *BeltholdStream { + s := &BeltholdStream{ + Float64Series: types.NewFloat64Series(), + window: 2, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + two = source.Last(1) + one = source.Last(0) + ) + + if two.Close.Float64() > two.Open.Float64() { + if two.High.Float64() < one.Open.Float64() { + if one.Open.Float64() == one.High.Float64() { + if one.Close.Float64() < one.Open.Float64() { + output = Bear + } + } + } + } + + if two.Close.Float64() < two.Open.Float64() { + if two.Low.Float64() > one.Open.Float64() { + if one.Open.Float64() == one.Low.Float64() { + if one.Close.Float64() > one.Open.Float64() { + output = Bull + } + } + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *BeltholdStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/belthold_test.go b/pkg/indicator/v2/belthold_test.go new file mode 100644 index 0000000000..fef871e156 --- /dev/null +++ b/pkg/indicator/v2/belthold_test.go @@ -0,0 +1,44 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestBelthold(t *testing.T) { + + ts := []types.KLine{ + {Open: n(60), Low: n(55), High: n(75), Close: n(70)}, + {Open: n(100), Low: n(75), High: n(100), Close: n(80)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := Belthold(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestBelthold Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(120), Low: n(100), High: n(125), Close: n(105)}, + {Open: n(70), Low: n(70), High: n(95), Close: n(90)}, + } + + ind = Belthold(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestBelthold Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/boll.go b/pkg/indicator/v2/bollinger_band.go similarity index 85% rename from pkg/indicator/v2/boll.go rename to pkg/indicator/v2/bollinger_band.go index 611f0ea346..53337aa831 100644 --- a/pkg/indicator/v2/boll.go +++ b/pkg/indicator/v2/bollinger_band.go @@ -10,14 +10,13 @@ type BOLLStream struct { UpBand, DownBand *types.Float64Series - window int - k float64 + k float64 SMA *SMAStream StdDev *StdDevStream } -// BOOL2 is bollinger indicator +// BOLL2 is bollinger indicator // the data flow: // // priceSource -> @@ -33,7 +32,6 @@ func BOLL(source types.Float64Source, window int, k float64) *BOLLStream { Float64Series: types.NewFloat64Series(), UpBand: types.NewFloat64Series(), DownBand: types.NewFloat64Series(), - window: window, k: k, SMA: sma, StdDev: stdDev, @@ -54,3 +52,8 @@ func (s *BOLLStream) Calculate(v float64) float64 { band := stdDev * s.k return band } + +func (s *BOLLStream) Truncate() { + s.UpBand.Slice = s.UpBand.Slice.Truncate(5000) + s.DownBand.Slice = s.DownBand.Slice.Truncate(5000) +} diff --git a/pkg/indicator/v2/bollinger_band_test.go b/pkg/indicator/v2/bollinger_band_test.go new file mode 100644 index 0000000000..b17ed9d527 --- /dev/null +++ b/pkg/indicator/v2/bollinger_band_test.go @@ -0,0 +1,113 @@ +package indicatorv2 + +import ( + "strconv" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +// number data from https://school.stockcharts.com/doku.php?id=technical_indicators:bollinger_bands +func TestBollingerBand(t *testing.T) { + ts := []float64{ + 86.16, + 89.09, + 88.78, + 90.32, + 89.07, + 91.15, + 89.44, + 89.18, + 86.93, + 87.68, + 86.96, + 89.43, + 89.32, + 88.72, + 87.45, + 87.26, + 89.50, + 87.90, + 89.13, + 90.70, + 92.90, + 92.98, + 91.80, + 92.66, + 92.68, + 92.30, + 92.77, + 92.54, + 92.95, + 93.20, + 91.07, + 89.83, + 89.74, + 90.40, + 90.74, + 88.02, + 88.09, + 88.84, + 90.78, + 90.54, + 91.39, + 90.65, + } + + expectedLines := strings.Split(` +88.71 1.29 91.29 86.12 5.17 +89.05 1.45 91.95 86.14 5.81 +89.24 1.69 92.61 85.87 6.75 +89.39 1.77 92.93 85.85 7.09 +89.51 1.90 93.31 85.70 7.61 +89.69 2.02 93.73 85.65 8.08 +89.75 2.08 93.90 85.59 8.31 +89.91 2.18 94.27 85.56 8.71 +90.08 2.24 94.57 85.60 8.97 +90.38 2.20 94.79 85.98 8.81 +90.66 2.19 95.04 86.27 8.77 +90.86 2.02 94.91 86.82 8.09 +90.88 2.01 94.90 86.87 8.04 +90.91 2.00 94.90 86.91 7.98 +90.99 1.94 94.86 87.12 7.74 +91.15 1.76 94.67 87.63 7.04 +91.19 1.68 94.56 87.83 6.73 +91.12 1.78 94.68 87.56 7.12 +91.17 1.70 94.58 87.76 6.82 +91.25 1.64 94.53 87.97 6.57 +91.24 1.65 94.53 87.95 6.58 +91.17 1.60 94.37 87.96 6.41 +91.05 1.55 94.15 87.95 6.20`, "\n") + + // SMAs := []float64{} + // STDEVs := []float64{} + BBUPs := []float64{} + BBLOs := []float64{} + // BBWs := []float64{} + for _, line := range expectedLines { + tokens := strings.Split(line, "\t") + if len(tokens) == 5 { + f3, _ := strconv.ParseFloat(tokens[2], 64) + BBUPs = append(BBUPs, f3) + f4, _ := strconv.ParseFloat(tokens[3], 64) + BBLOs = append(BBLOs, f4) + } + } + window := 20 + sigma := 2.0 + + source := types.NewFloat64Series() + ind := BOLL(source, window, sigma) + + for _, d := range ts { + source.PushAndEmit(d) + } + for i := window - 1; i < len(ts); i++ { + j := i - (window - 1) + assert.InEpsilon(t, ind.UpBand.Slice[i], BBUPs[j], 0.01) + assert.InEpsilon(t, ind.DownBand.Slice[i], BBLOs[j], 0.01) + } +} diff --git a/pkg/indicator/v2/breakaway.go b/pkg/indicator/v2/breakaway.go new file mode 100644 index 0000000000..8086efe269 --- /dev/null +++ b/pkg/indicator/v2/breakaway.go @@ -0,0 +1,71 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type BreakAwayStream struct { + *types.Float64Series + + window int +} + +func BreakAway(source KLineSubscription) *BreakAwayStream { + s := &BreakAwayStream{ + Float64Series: types.NewFloat64Series(), + window: 5, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + five = source.Last(4) + four = source.Last(3) + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + ) + + if five.Open.Float64() < five.Close.Float64() { + if four.Open.Float64() < four.Close.Float64() && + five.Close.Float64() < four.Open.Float64() { + if four.Close.Float64() < three.Close.Float64() && + three.Close.Float64() < two.Close.Float64() { + if one.Open.Float64() > one.Close.Float64() && + one.Close.Float64() > five.Close.Float64() { + output = Bear + } + } + } + } + + if five.Open.Float64() > five.Close.Float64() { + if four.Open.Float64() > four.Close.Float64() && + five.Close.Float64() > four.Open.Float64() { + if four.Close.Float64() > three.Close.Float64() && + three.Close.Float64() > two.Close.Float64() { + if one.Open.Float64() < one.Close.Float64() && + one.Close.Float64() < five.Close.Float64() { + output = Bull + } + } + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *BreakAwayStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/breakaway_test.go b/pkg/indicator/v2/breakaway_test.go new file mode 100644 index 0000000000..a267495a88 --- /dev/null +++ b/pkg/indicator/v2/breakaway_test.go @@ -0,0 +1,48 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestBreakAway(t *testing.T) { + ts := []types.KLine{ + {Open: n(70), Low: n(60), High: n(85), Close: n(80)}, + {Open: n(115), Low: n(110), High: n(125), Close: n(120)}, + {Open: n(120), Low: n(115), High: n(130), Close: n(125)}, + {Open: n(125), Low: n(120), High: n(135), Close: n(130)}, + {Open: n(125), Low: n(95), High: n(130), Close: n(100)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := BreakAway(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestBreakAway Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(130), Low: n(115), High: n(135), Close: n(120)}, + {Open: n(100), Low: n(85), High: n(105), Close: n(90)}, + {Open: n(95), Low: n(80), High: n(100), Close: n(85)}, + {Open: n(90), Low: n(75), High: n(95), Close: n(80)}, + {Open: n(85), Low: n(80), High: n(115), Close: n(110)}, + } + ind = BreakAway(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestBreakAway Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/chaikin_money_flow.go b/pkg/indicator/v2/chaikin_money_flow.go new file mode 100644 index 0000000000..a028bcaa42 --- /dev/null +++ b/pkg/indicator/v2/chaikin_money_flow.go @@ -0,0 +1,57 @@ +package indicatorv2 + +import ( + "gonum.org/v1/gonum/floats" + + bbfloat "github.com/c9s/bbgo/pkg/datatype/floats" + "github.com/c9s/bbgo/pkg/types" +) + +type ChaikinMoneyFlowStream struct { + *types.Float64Series + moneyFlowVolume bbfloat.Slice + window int +} + +// The Chaikin Money Flow (CMF) measures the amount of money flow volume +// over a given period. +// +// Money Flow Multiplier = ((Closing - Low) - (High - Closing)) / (High - Low) +// Money Flow Volume = Money Flow Multiplier * Volume +// Chaikin Money Flow = Sum(20, Money Flow Volume) / Sum(20, Volume) +func ChaikinMoneyFlow(source KLineSubscription, window int) *ChaikinMoneyFlowStream { + var ( + closing = ClosePrices(source) + low = LowPrices(source) + high = HighPrices(source) + volume = Volumes(source) + cl = Subtract(closing, low) + hc = Subtract(high, closing) + flow = Subtract(cl, hc) + hl = Subtract(high, low) + s = &ChaikinMoneyFlowStream{ + Float64Series: types.NewFloat64Series(), + window: window, + } + ) + source.AddSubscriber(func(v types.KLine) { + var moneyFlowMultiplier = flow.Last(0) / hl.Last(0) + s.moneyFlowVolume.Push(moneyFlowMultiplier * volume.Last(0)) + var ( + sumFlowVol = floats.Sum(s.moneyFlowVolume.Tail(s.window)) + sumVol = floats.Sum(volume.Slice.Tail(s.window)) + cmf = sumFlowVol / sumVol + ) + + s.PushAndEmit(cmf) + }) + return s +} + +func (s *ChaikinMoneyFlowStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} + +func ChaikinMoneyFlowDefault(source KLineSubscription) *ChaikinMoneyFlowStream { + return ChaikinMoneyFlow(source, 20) +} diff --git a/pkg/indicator/v2/chaikin_money_flow_test.go b/pkg/indicator/v2/chaikin_money_flow_test.go new file mode 100644 index 0000000000..348726cf3b --- /dev/null +++ b/pkg/indicator/v2/chaikin_money_flow_test.go @@ -0,0 +1,32 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestChaikinMoneyFlow(t *testing.T) { + ts := []types.KLine{ + {Volume: n(100), Low: n(6), High: n(10), Close: n(9)}, + {Volume: n(110), Low: n(7), High: n(9), Close: n(11)}, + {Volume: n(80), Low: n(9), High: n(12), Close: n(7)}, + {Volume: n(120), Low: n(12), High: n(14), Close: n(10)}, + {Volume: n(90), Low: n(10), High: n(12), Close: n(8)}, + } + expected := []float64{0.5, 1.81, 0.67, -0.41, -0.87} + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := ChaikinMoneyFlowDefault(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + + for i, v := range expected { + assert.InDelta(t, v, ind.Slice[i], 0.5, "Expected AccumulationDistribution.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } +} diff --git a/pkg/indicator/v2/chaikin_osc.go b/pkg/indicator/v2/chaikin_osc.go new file mode 100644 index 0000000000..c32aa81c18 --- /dev/null +++ b/pkg/indicator/v2/chaikin_osc.go @@ -0,0 +1,36 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +// The DefaultChaikinOscillator function calculates Chaikin +// Oscillator with the most frequently used fast and short +// periods, 3 and 10. +func NewChaikinOscillatorDefault(source KLineSubscription) *ChaikinOscillatorStream { + return ChaikinOscillator(source, 3, 10) +} + +type ChaikinOscillatorStream struct { + // embedded structs + *types.Float64Series +} + +func ChaikinOscillator(source KLineSubscription, slow, fast int) *ChaikinOscillatorStream { + var ( + accDist = AccumulationDistribution(source) + diff = Subtract(EWMA2(accDist, slow), EWMA2(accDist, fast)) + s = &ChaikinOscillatorStream{ + Float64Series: types.NewFloat64Series(), + } + ) + source.AddSubscriber(func(kLine types.KLine) { + s.PushAndEmit(diff.Last(0)) + }) + + return s +} + +func (s *ChaikinOscillatorStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/chaikin_osc_test.go b/pkg/indicator/v2/chaikin_osc_test.go new file mode 100644 index 0000000000..dea8050935 --- /dev/null +++ b/pkg/indicator/v2/chaikin_osc_test.go @@ -0,0 +1,35 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func Test_ChaikinOscillator(t *testing.T) { + ts := []types.KLine{ + {Volume: n(100), Low: n(1), Close: n(5), High: n(10)}, + {Volume: n(200), Low: n(2), Close: n(6), High: n(11)}, + {Volume: n(300), Low: n(3), Close: n(7), High: n(12)}, + {Volume: n(400), Low: n(4), Close: n(8), High: n(13)}, + {Volume: n(500), Low: n(5), Close: n(9), High: n(14)}, + {Volume: n(600), Low: n(6), Close: n(10), High: n(15)}, + {Volume: n(700), Low: n(7), Close: n(11), High: n(16)}, + {Volume: n(800), Low: n(8), Close: n(12), High: n(17)}, + } + + expected := []float64{0, -7.41, -18.52, -31.69, -46.09, -61.27, -76.95, -92.97} + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := ChaikinOscillator(kLines, 2, 5) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + for i, v := range expected { + assert.InDelta(t, v, ind.Slice[i], 0.01, "Expected chaikin_osc.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } +} diff --git a/pkg/indicator/v2/chandelier_exit.go b/pkg/indicator/v2/chandelier_exit.go new file mode 100644 index 0000000000..bac4db2033 --- /dev/null +++ b/pkg/indicator/v2/chandelier_exit.go @@ -0,0 +1,61 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +// OrderSide represents the side of an order: Buy (long) or Sell (short). +type OrderSide int + +const ( + // Buy (long) + Buy OrderSide = iota + 1 + + // Sell (short) + Sell +) + +type ChandelierExitStream struct { + // embedded struct + *types.Float64Series + + atr *ATRStream + min *MinValueStream + max *MaxValueStream +} + +// Chandelier Exit. It sets a trailing stop-loss based on the Average True Value (ATR). +// +// Chandelier Exit Long = 22-Period SMA High - ATR(22) * 3 +// Chandelier Exit Short = 22-Period SMA Low + ATR(22) * 3 +// +// Returns chandelierExitLong, chandelierExitShort +func ChandelierExit(source KLineSubscription, os OrderSide, window int) *ChandelierExitStream { + s := &ChandelierExitStream{ + atr: ATR2(source, window), + min: MinValue(LowPrices(source), window), + max: MaxValue(HighPrices(source), window)} + + source.AddSubscriber(func(v types.KLine) { + high := s.max.Last(0) + low := s.min.Last(0) + atr := s.atr.Last(0) + var chandelierExit float64 + if os == Buy { + chandelierExit = high - atr*3 + } else { + chandelierExit = low + atr*3 + } + s.PushAndEmit(chandelierExit) + }) + + return s +} + +func ChandelierExitDefault(source KLineSubscription, os OrderSide) *ChandelierExitStream { + return ChandelierExit(source, os, 22) +} + +func (s *ChandelierExitStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/dark_cloud.go b/pkg/indicator/v2/dark_cloud.go new file mode 100644 index 0000000000..2c8492b02f --- /dev/null +++ b/pkg/indicator/v2/dark_cloud.go @@ -0,0 +1,57 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type DarkCloudStream struct { + *types.Float64Series + + window int +} + +// Dark Cloud Cover is a candlestick pattern that shows a shift in momentum to the downside +// following a price rise. +// The pattern is composed of a bearish candle that opens above but then closes below the midpoint of +// the prior bullish candle. +// Both candles should be relatively large, showing strong participation by traders and investors. +// When the pattern occurs with small candles it is typically less significant. +func DarkCloud(source KLineSubscription) *DarkCloudStream { + s := &DarkCloudStream{ + Float64Series: types.NewFloat64Series(), + window: 2, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + two = source.Last(1) + one = source.Last(0) + twoMidpoint = (two.Close.Float64() + two.Open.Float64()) / 2 + isFirstBullish = two.Close.Float64() > two.Open.Float64() + isSecondBearish = one.Close.Float64() < one.Open.Float64() + isDarkCloud = one.Open.Float64() > two.High.Float64() && + one.Close.Float64() < twoMidpoint && one.Close.Float64() > two.Open.Float64() + ) + + if isFirstBullish && isSecondBearish && isDarkCloud { + output = Bear + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *DarkCloudStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/dark_cloud_test.go b/pkg/indicator/v2/dark_cloud_test.go new file mode 100644 index 0000000000..b9e06fe502 --- /dev/null +++ b/pkg/indicator/v2/dark_cloud_test.go @@ -0,0 +1,27 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestDarkCloud(t *testing.T) { + ts := []types.KLine{ + {Open: n(30.10), Low: n(28.30), High: n(37.40), Close: n(35.36)}, + {Open: n(39.45), Low: n(31.25), High: n(41.45), Close: n(32.50)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := DarkCloud(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestDarkCloud Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } +} diff --git a/pkg/indicator/v2/dema.go b/pkg/indicator/v2/dema.go new file mode 100644 index 0000000000..2f3e242b80 --- /dev/null +++ b/pkg/indicator/v2/dema.go @@ -0,0 +1,59 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +// Double Exponential Moving Average (DEMA) + +// The [Dema](https://pkg.go.dev/github.com/cinar/indicator#Dema) function calculates +// the Double Exponential Moving Average (DEMA) for a given period. + +// The double exponential moving average (DEMA) is a technical indicator introduced by Patrick Mulloy. +// The purpose is to reduce the amount of noise present in price charts used by technical traders. +// The DEMA uses two exponential moving averages (EMAs) to eliminate lag. +// It helps confirm uptrends when the price is above the average, and helps confirm downtrends +// when the price is below the average. When the price crosses the average that may signal a trend change. + +// ``` +// +// DEMA = (2 * EMA(values)) - EMA(EMA(values)) +// +// ``` + +type DEMAStream struct { + // embedded struct + *types.Float64Series + + ema1 *EWMAStream + ema2 *EWMAStream +} + +func DEMA(source types.Float64Source, window int) *DEMAStream { + var ( + ema1 = EWMA2(source, window) + ema2 = EWMA2(ema1, window) + ) + + s := &DEMAStream{ + Float64Series: types.NewFloat64Series(), + ema1: ema1, + ema2: ema2, + } + s.Bind(source, s) + return s +} + +func (s *DEMAStream) Calculate(v float64) float64 { + var ( + e1 = s.ema1.Last(0) + e2 = s.ema2.Last(0) + dema = e1*2 - e2 + ) + + return dema +} + +func (s *DEMAStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/doji.go b/pkg/indicator/v2/doji.go new file mode 100644 index 0000000000..03668801ba --- /dev/null +++ b/pkg/indicator/v2/doji.go @@ -0,0 +1,41 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + + "github.com/c9s/bbgo/pkg/types" +) + +type DojiStream struct { + *types.Float64Series +} + +// Is a doji bullish or bearish? +// A doji formation generally can be interpreted as a sign of indecision, meaning neither bulls nor bears +// can successfully take over. Of its variations, the dragonfly doji is seen as a bullish reversal pattern +// that occurs at the bottom of downtrends. The gravestone doji is read as a bearish reversal at the peak of uptrends. +func Doji(source KLineSubscription, maxDiff float64) *DojiStream { + s := &DojiStream{ + Float64Series: types.NewFloat64Series(), + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + output = Neutral + one = source.Last(0) + openEqualClose = fixedpoint.ApproxEqual(one.Open, one.Close, maxDiff) + highEqualsOpen = fixedpoint.ApproxEqual(one.Open, one.High, maxDiff) + lowEqualsClose = fixedpoint.ApproxEqual(one.Close, one.Low, maxDiff) + ) + if openEqualClose && lowEqualsClose && highEqualsOpen { + output = Bull + } + s.PushAndEmit(output) + }) + + return s +} + +func (s *DojiStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/doji_dragon_fly.go b/pkg/indicator/v2/doji_dragon_fly.go new file mode 100644 index 0000000000..9f63e81b35 --- /dev/null +++ b/pkg/indicator/v2/doji_dragon_fly.go @@ -0,0 +1,38 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type DojiDragonFlyStream struct { + *types.Float64Series +} + +func DojiDragonFly(source KLineSubscription, maxDiff float64) *DojiDragonFlyStream { + s := &DojiDragonFlyStream{ + Float64Series: types.NewFloat64Series(), + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + output = Neutral + one = kLine + openEqualClose = fixedpoint.ApproxEqual(one.Open, one.Close, maxDiff) + highEqualsOpen = fixedpoint.ApproxEqual(one.Open, one.High, maxDiff) + lowEqualsClose = fixedpoint.ApproxEqual(one.Close, one.Low, maxDiff) + ) + + if openEqualClose && highEqualsOpen && !lowEqualsClose { + output = Bull + } + s.PushAndEmit(output) + + }) + + return s +} + +func (s *DojiDragonFlyStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/doji_dragon_fly_test.go b/pkg/indicator/v2/doji_dragon_fly_test.go new file mode 100644 index 0000000000..7d22023f6e --- /dev/null +++ b/pkg/indicator/v2/doji_dragon_fly_test.go @@ -0,0 +1,38 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestDojiDragonFly(t *testing.T) { + ts := []types.KLine{ + {Open: n(30.10), Low: n(28.10), High: n(30.13), Close: n(30.09)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := DojiDragonFly(kLines, 0.05) + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + if ind.Last(0) != expectedBull { + t.Errorf("TestDojiDragonFly Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } + ts = []types.KLine{ + {Open: n(30.10), Low: n(30.11), High: n(30.10), Close: n(30.09)}, + } + + ind = DojiDragonFly(kLines, 0.05) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull = 1.0 + + if ind.Last(0) == expectedBull { + t.Errorf("TestDojiDragonFly Not Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/doji_gravestone.go b/pkg/indicator/v2/doji_gravestone.go new file mode 100644 index 0000000000..7c6bf57ba8 --- /dev/null +++ b/pkg/indicator/v2/doji_gravestone.go @@ -0,0 +1,44 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + + "github.com/c9s/bbgo/pkg/types" +) + +type DojiGraveStoneStream struct { + *types.Float64Series +} + +// A gravestone doji candle is a pattern that technical stock traders use as a signal that a stock price +// may soon undergo a bearish reversal. This pattern forms when the open, low, and closing prices of an asset +// are close to each other and have a long upper shadow. The shadow in a candlestick chart is the thin part +// showing the price action for the day as it differs from high to low prices. +func DojiGraveStone(source KLineSubscription, maxDiff float64) *DojiGraveStoneStream { + s := &DojiGraveStoneStream{ + Float64Series: types.NewFloat64Series(), + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + output = Neutral + one = kLine + openEqualClose = fixedpoint.ApproxEqual(one.Open, one.Close, maxDiff) + highEqualsOpen = fixedpoint.ApproxEqual(one.Open, one.High, maxDiff) + lowEqualsClose = fixedpoint.ApproxEqual(one.Close, one.Low, maxDiff) + ) + + if openEqualClose && lowEqualsClose && !highEqualsOpen { + output = Bear + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *DojiGraveStoneStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/doji_gravestone_test.go b/pkg/indicator/v2/doji_gravestone_test.go new file mode 100644 index 0000000000..7ab788d0a8 --- /dev/null +++ b/pkg/indicator/v2/doji_gravestone_test.go @@ -0,0 +1,39 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestDojiGraveStone(t *testing.T) { + hasPattern := []types.KLine{ + {Open: n(30.10), Low: n(30.12), High: n(36.13), Close: n(30.13)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := DojiGraveStone(kLines, 0.05) + + for _, candle := range hasPattern { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestDojiGraveStone Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + noPattern := []types.KLine{ + {Open: n(30.10), Low: n(30.11), High: n(30.10), Close: n(30.09)}, + } + ind = DojiGraveStone(kLines, 0.01) + + for _, candle := range noPattern { + stream.EmitKLineClosed(candle) + } + + if ind.Last(0) == expectedBear { + t.Errorf("TestDojiGraveStone Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } +} diff --git a/pkg/indicator/v2/doji_long_legged.go b/pkg/indicator/v2/doji_long_legged.go new file mode 100644 index 0000000000..902be5ff86 --- /dev/null +++ b/pkg/indicator/v2/doji_long_legged.go @@ -0,0 +1,79 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type DojiLongLeggedStream struct { + *types.Float64Series + + window int +} + +// The long-legged doji is a type of candlestick pattern that signals to traders a point of indecision +// about the future direction of price. This doji has long upper and lower shadows and roughly +// the same opening and closing prices. In addition to signaling indecision, the long-legged doji can also +// indicate the beginning of a consolidation period where price action may soon break out to form a new trend. +// These doji can be a sign that sentiment is changing and that a trend reversal is on the horizon. +func DojiLongLegged(source KLineSubscription) *DojiLongLeggedStream { + s := &DojiLongLeggedStream{ + Float64Series: types.NewFloat64Series(), + window: 4, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + four = source.Last(3) + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + ) + // BEAR + if four.Close.Float64() > four.Open.Float64() { + if three.Close.Float64() > three.Open.Float64() { + if two.Close.Float64() > two.Open.Float64() { + if fixedpoint.Abs(one.Close.Sub(one.Open).Div(one.Open)).Float64() < threshold { + if fixedpoint.Abs(one.High.Sub(one.Open).Div(one.Open)).Float64() > limit { + if fixedpoint.Abs(one.Close.Sub(one.Low).Div(one.Low)).Float64() > limit { + output = Bear + } + } + } + } + } + } + + // BULL + if four.Close.Float64() < four.Open.Float64() { + if three.Close.Float64() < three.Open.Float64() { + if two.Close.Float64() < two.Open.Float64() { + if fixedpoint.Abs(one.Open.Sub(one.Close).Div(one.Close)).Float64() < threshold { + if fixedpoint.Abs(one.Low.Sub(one.Close).Div(one.Close)).Float64() > limit { + if fixedpoint.Abs(one.Open.Sub(one.High).Div(one.High)).Float64() > limit { + output = Bull + } + } + } + } + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *DojiLongLeggedStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/doji_long_legged_test.go b/pkg/indicator/v2/doji_long_legged_test.go new file mode 100644 index 0000000000..db694dcd38 --- /dev/null +++ b/pkg/indicator/v2/doji_long_legged_test.go @@ -0,0 +1,46 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestDojiLongLegged(t *testing.T) { + ts := []types.KLine{ + {Open: n(85), Low: n(80), High: n(95), Close: n(90)}, + {Open: n(95), Low: n(90), High: n(105), Close: n(100)}, + {Open: n(105), Low: n(100), High: n(115), Close: n(110)}, + {Open: n(170), Low: n(120), High: n(210), Close: n(160)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := DojiLongLegged(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestDojiLL Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(90), Low: n(80), High: n(95), Close: n(85)}, + {Open: n(100), Low: n(90), High: n(105), Close: n(95)}, + {Open: n(110), Low: n(100), High: n(115), Close: n(105)}, + {Open: n(160), Low: n(120), High: n(210), Close: n(170)}, + } + ind = DojiLongLegged(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestDojiLL Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/doji_star.go b/pkg/indicator/v2/doji_star.go new file mode 100644 index 0000000000..7baf1dc9cc --- /dev/null +++ b/pkg/indicator/v2/doji_star.go @@ -0,0 +1,77 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type DojiStarStream struct { + *types.Float64Series + + window int +} + +// maxDiff is the maximum deviation between a and b to consider them approximately equal +func DojiStar(source KLineSubscription, direction Direction, maxDiff float64) *DojiStarStream { + s := &DojiStarStream{ + Float64Series: types.NewFloat64Series(), + window: 3, + } + var doji = Doji(source, maxDiff) + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + firstMidpoint = three.Open.Add(three.Close).Div(fixedpoint.Two).Float64() + dojiExists = doji.Last(1) == Bull + ) + if direction == Bullish { + var ( + isFirstBearish = three.Close.Float64() < three.Open.Float64() + isThirdBullish = one.Close.Float64() > one.Open.Float64() + gapExists = two.High.Float64() < three.Low.Float64() && + two.Low.Float64() < three.Low.Float64() && + one.Open.Float64() > two.High.Float64() && + two.Close.Float64() < one.Open.Float64() + doesCloseAboveFirstMidpoint = one.Close.Float64() > firstMidpoint + ) + + if isFirstBearish && dojiExists && isThirdBullish && gapExists && doesCloseAboveFirstMidpoint { + output = Bull + } + } else { + var ( + isFirstBullish = three.Close.Float64() > three.Open.Float64() + isThirdBearish = one.Open.Float64() > one.Close.Float64() + gapExists = two.High.Float64() > three.High.Float64() && + two.Low.Float64() > three.High.Float64() && + one.Open.Float64() < two.Low.Float64() && + two.Close.Float64() > one.Open.Float64() + doesCloseBelowFirstMidpoint = one.Close.Float64() < firstMidpoint + ) + + if isFirstBullish && dojiExists && gapExists && isThirdBearish && doesCloseBelowFirstMidpoint { + output = Bear + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *DojiStarStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/doji_star_test.go b/pkg/indicator/v2/doji_star_test.go new file mode 100644 index 0000000000..df3083dc14 --- /dev/null +++ b/pkg/indicator/v2/doji_star_test.go @@ -0,0 +1,46 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestDojiStar(t *testing.T) { + ts := []types.KLine{ + {Open: n(18.35), Low: n(18.13), High: n(21.60), Close: n(21.30)}, + {Open: n(22.20), Low: n(21.87), High: n(22.40), Close: n(22.22)}, + {Open: n(21.60), Low: n(19.30), High: n(22.05), Close: n(19.45)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := DojiStar(kLines, Bearish, 0.05) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestDojiEveningStar Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(22.20), Low: n(20.65), High: n(22.50), Close: n(20.80)}, + {Open: n(20.30), Low: n(20.10), High: n(20.45), Close: n(20.30)}, + {Open: n(20.70), Low: n(20.40), High: n(21.82), Close: n(21.58)}, + } + stream = &types.StandardStream{} + kLines = KLines(stream, "", "") + ind = DojiStar(kLines, Bullish, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestDojiMorningStar Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/doji_test.go b/pkg/indicator/v2/doji_test.go new file mode 100644 index 0000000000..679699f76f --- /dev/null +++ b/pkg/indicator/v2/doji_test.go @@ -0,0 +1,40 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestDoji(t *testing.T) { + ts := []types.KLine{ + {Open: n(30.10), Low: n(32.10), High: n(30.13), Close: n(28.10)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := Doji(kLines, 0.05) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + hasPattern := 1.0 + + if ind.Last(0) == hasPattern { + t.Errorf("TestDoji Bear unexpected result: got %v want %v", ind.Last(0), hasPattern) + } + + ts = []types.KLine{ + {Open: n(30.10), Low: n(30.11), High: n(30.10), Close: n(30.09)}, + } + ind = Doji(kLines, 0.05) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + hasPattern = 1.0 + + if ind.Last(0) != hasPattern { + t.Errorf("TestDoji Bull unexpected result: got %v want %v", ind.Last(0), hasPattern) + } +} diff --git a/pkg/indicator/v2/donchian_channel.go b/pkg/indicator/v2/donchian_channel.go new file mode 100644 index 0000000000..54eb46b132 --- /dev/null +++ b/pkg/indicator/v2/donchian_channel.go @@ -0,0 +1,48 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type DonchianChannelStream struct { + UpBand, DownBand, MiddleBand *types.Float64Series +} + +// The Donchian Channel (DC) calculates three lines generated by moving average +// calculations that comprise an indicator formed by upper and lower bands +// around a midrange or median band. The upper band marks the highest +// price of an asset while the lower band marks the lowest price of +// an asset, and the area between the upper and lower bands +// represents the Donchian Channel. +// +// Upper Channel = Mmax(period, closings) +// Lower Channel = Mmin(period, closings) +// Middle Channel = (Upper Channel + Lower Channel) / 2 +func DonchianChannel(source KLineSubscription, window int) *DonchianChannelStream { + var ( + closing = ClosePrices(source) + s = &DonchianChannelStream{ + UpBand: types.NewFloat64Series(), + DownBand: types.NewFloat64Series(), + MiddleBand: types.NewFloat64Series(), + } + ) + + source.AddSubscriber(func(v types.KLine) { + var ( + sample = closing.Slice.Tail(window) + upperChannel = sample.Max() + lowerChannel = sample.Min() + ) + s.UpBand.Push(upperChannel) + s.DownBand.Push(lowerChannel) + s.MiddleBand.Push((upperChannel + lowerChannel) / 2) + }) + return s +} + +func (s *DonchianChannelStream) Truncate() { + s.UpBand.Slice = s.UpBand.Slice.Truncate(MaxNumOfMA) + s.DownBand.Slice = s.DownBand.Slice.Truncate(MaxNumOfMA) + s.MiddleBand.Slice = s.MiddleBand.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/donchian_channel_test.go b/pkg/indicator/v2/donchian_channel_test.go new file mode 100644 index 0000000000..f7c905adb1 --- /dev/null +++ b/pkg/indicator/v2/donchian_channel_test.go @@ -0,0 +1,34 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/fixedpoint" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestDonchianChannel(t *testing.T) { + closing := []float64{9, 11, 7, 10, 8} + expectedUpper := []float64{9, 11, 11, 11, 11} + expectedMiddle := []float64{9, 10, 9, 9, 9} + expectedLower := []float64{9, 9, 7, 7, 7} + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := DonchianChannel(kLines, 4) + for i := range closing { + stream.EmitKLineClosed(types.KLine{Close: fixedpoint.NewFromFloat(closing[i])}) + } + for i, v := range expectedLower { + assert.InDelta(t, v, ind.DownBand.Slice[i], 0.01, "Expected DonchianDownBand.slice[%d] to be %v, but got %v", i, v, ind.DownBand.Slice[i]) + } + for i, v := range expectedMiddle { + assert.InDelta(t, v, ind.MiddleBand.Slice[i], 0.01, "Expected DonchianUpBand.slice[%d] to be %v, but got %v", i, v, ind.UpBand.Slice[i]) + } + for i, v := range expectedUpper { + assert.InDelta(t, v, ind.UpBand.Slice[i], 0.01, "Expected DonchianUpBand.slice[%d] to be %v, but got %v", i, v, ind.UpBand.Slice[i]) + } +} diff --git a/pkg/indicator/v2/downside_tazuki_gap.go b/pkg/indicator/v2/downside_tazuki_gap.go new file mode 100644 index 0000000000..39d22598ca --- /dev/null +++ b/pkg/indicator/v2/downside_tazuki_gap.go @@ -0,0 +1,55 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type TazukiGapStream struct { + *types.Float64Series + + window int +} + +func TazukiGap(source KLineSubscription) *TazukiGapStream { + s := &TazukiGapStream{ + Float64Series: types.NewFloat64Series(), + window: 3, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + isFirstBearish = three.Close.Float64() < three.Open.Float64() + isSecondBearish = two.Close.Float64() < two.Open.Float64() + isThirdBullish = one.Close.Float64() > one.Open.Float64() + isFirstGapExists = two.High.Float64() < three.Low.Float64() + isTazukiGap = two.Open.Float64() > one.Open.Float64() && + two.Close.Float64() < one.Open.Float64() && + one.Close.Float64() > two.Open.Float64() && + one.Close.Float64() < three.Close.Float64() + ) + + if isFirstBearish && isSecondBearish && isThirdBullish && isFirstGapExists && isTazukiGap { + output = Bear + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *TazukiGapStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/downside_tazuki_gap_test.go b/pkg/indicator/v2/downside_tazuki_gap_test.go new file mode 100644 index 0000000000..47d2b4e497 --- /dev/null +++ b/pkg/indicator/v2/downside_tazuki_gap_test.go @@ -0,0 +1,27 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestTazukiGap(t *testing.T) { + ts := []types.KLine{ + {Open: n(45.00), Low: n(38.56), High: n(46.20), Close: n(41.20)}, + {Open: n(33.45), Low: n(28), High: n(34.70), Close: n(29.31)}, + {Open: n(30.20), Low: n(29.80), High: n(36.63), Close: n(36.28)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := TazukiGap(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestTazukiGap Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } +} diff --git a/pkg/indicator/v2/ewma.go b/pkg/indicator/v2/ema.go similarity index 75% rename from pkg/indicator/v2/ewma.go rename to pkg/indicator/v2/ema.go index fdd9745f0d..fd62ca2ca1 100644 --- a/pkg/indicator/v2/ewma.go +++ b/pkg/indicator/v2/ema.go @@ -9,12 +9,15 @@ type EWMAStream struct { multiplier float64 } -func EWMA2(source types.Float64Source, window int) *EWMAStream { +func EWMA2(source types.Float64Source, window int, multiplier ...float64) *EWMAStream { s := &EWMAStream{ Float64Series: types.NewFloat64Series(), window: window, multiplier: 2.0 / float64(1+window), } + if len(multiplier) == 1 { + s.multiplier = multiplier[0] + } s.Bind(source, s) return s } diff --git a/pkg/indicator/v2/ema_test.go b/pkg/indicator/v2/ema_test.go new file mode 100644 index 0000000000..10e45c61f2 --- /dev/null +++ b/pkg/indicator/v2/ema_test.go @@ -0,0 +1,31 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestExponentialMovingAverage(t *testing.T) { + closing := []float64{ + 64.75, 63.79, 63.73, + 63.73, 63.55, 63.19, + 63.91, 63.85, 62.95, + 63.37, 61.33, 61.51} + expected := []float64{ + 64.75, 64.37, 64.11, + 63.96, 63.8, 63.55, + 63.7, 63.76, 63.43, + 63.41, 62.58, 62.15, + } + prices := ClosePrices(nil) + ind := EWMA2(prices, 4) + + for _, price := range closing { + prices.PushAndEmit(price) + } + for i, v := range expected { + assert.InDelta(t, v, ind.Slice[i], 0.01, "Expected EWMA.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } + +} diff --git a/pkg/indicator/v2/engulfing.go b/pkg/indicator/v2/engulfing.go new file mode 100644 index 0000000000..b0e81af28b --- /dev/null +++ b/pkg/indicator/v2/engulfing.go @@ -0,0 +1,61 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type EngulfingStream struct { + *types.Float64Series + + window int +} + +// Bullish engulfing patterns are more likely to signal reversals when they are preceded by four or more black candlesticks. +func Engulfing(source KLineSubscription) *EngulfingStream { + s := &EngulfingStream{ + Float64Series: types.NewFloat64Series(), + window: 2, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + two = source.Last(1) + one = source.Last(0) + ) + + // BEAR + if two.Open.Float64() < two.Close.Float64() { + if one.Open.Float64() > two.Close.Float64() { + if one.Close.Float64() < two.Open.Float64() { + output = Bear + } + } + } + + // BULL + if two.Open.Float64() > two.Close.Float64() { + if one.Open.Float64() < two.Close.Float64() { + if one.Close.Float64() > two.Open.Float64() { + output = Bull + } + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *EngulfingStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/engulfing_test.go b/pkg/indicator/v2/engulfing_test.go new file mode 100644 index 0000000000..38a67cd3af --- /dev/null +++ b/pkg/indicator/v2/engulfing_test.go @@ -0,0 +1,41 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestEngulfing(t *testing.T) { + ts := []types.KLine{ + {Open: n(80), Low: n(75), High: n(95), Close: n(90)}, + {Open: n(100), Low: n(65), High: n(105), Close: n(70)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := Engulfing(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestEngulfing Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(90), Low: n(75), High: n(95), Close: n(80)}, + {Open: n(70), Low: n(65), High: n(105), Close: n(100)}, + } + ind = Engulfing(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestEngulfing Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/harami.go b/pkg/indicator/v2/harami.go new file mode 100644 index 0000000000..af95fd1b05 --- /dev/null +++ b/pkg/indicator/v2/harami.go @@ -0,0 +1,63 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type HaramiStream struct { + *types.Float64Series + + window int +} + +// The bullish harami indicator is charted as a long candlestick followed by a smaller body, +// referred to as a doji, that is completely contained within the vertical range of the previous body. +// To some, a line drawn around this pattern resembles a pregnant woman. The word harami comes from an +// old Japanese word meaning pregnant. +func Harami(source KLineSubscription) *HaramiStream { + s := &HaramiStream{ + Float64Series: types.NewFloat64Series(), + window: 2, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + two = source.Last(1) + one = source.Last(0) + ) + + // BEAR + if two.Open.Float64() < two.Close.Float64() { + if one.Open.Float64() > one.Close.Float64() { + if one.Open.Float64() < two.Close.Float64() && one.Close.Float64() > two.Open.Float64() { + output = Bear + } + } + } + + // BULL + if two.Open.Float64() > two.Close.Float64() { + if one.Open.Float64() < one.Close.Float64() { + if one.Open.Float64() > two.Close.Float64() && one.Close.Float64() < two.Open.Float64() { + output = Bull + } + } + } + s.PushAndEmit(output) + + }) + + return s +} + +func (s *HaramiStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/harami_cross.go b/pkg/indicator/v2/harami_cross.go new file mode 100644 index 0000000000..ccbf401d73 --- /dev/null +++ b/pkg/indicator/v2/harami_cross.go @@ -0,0 +1,76 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type HaramiCrossStream struct { + *types.Float64Series + + window int +} + +// A bullish harami cross pattern forms after a downtrend. +// The doji shows that some indecision has entered the minds of sellers. +// Typically, traders don't act on the pattern unless the price follows through to the upside within the next +// couple of candles. This is called confirmation. Sometimes the price may pause for a few candles after the doji, +// and then rise or fall. A rise above the open of the first candle helps confirm that the price may be heading higher. +// A bearish harami cross forms after an uptrend. +// The first candlestick is a long up candle (typically colored white or green) which shows buyers are in control. +// This is followed by a doji, which shows indecision on the part of the buyers. Once again, the doji +// must be contained within the real body of the prior candle. +// If the price drops following the pattern, this confirms the pattern. +// If the price continues to rise following the doji, the bearish pattern is invalidated. +func HaramiCross(source KLineSubscription, direction Direction, maxDiff float64) *HaramiCrossStream { + s := &HaramiCrossStream{ + Float64Series: types.NewFloat64Series(), + window: 2, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + two = source.Last(1) + one = source.Last(0) + isLastDoji = fixedpoint.ApproxEqual(one.Open, one.Close, maxDiff) + ) + if direction == Bullish { + var ( + isBullishHaramiCrossPattern = two.Open.Float64() > one.Open.Float64() && + two.Close.Float64() < one.Open.Float64() && + two.Close.Float64() < one.Close.Float64() && + two.Open.Float64() > one.Low.Float64() && + two.High.Float64() > one.High.Float64() + ) + if isBullishHaramiCrossPattern && isLastDoji { + output = Bull + } + } else { + var isBearishHaramiCrossPattern = two.Open.Float64() < one.Open.Float64() && + two.Close.Float64() > one.Open.Float64() && + two.Close.Float64() > one.Close.Float64() && + two.Open.Float64() < one.Low.Float64() && + two.High.Float64() > one.High.Float64() + if isBearishHaramiCrossPattern && isLastDoji { + output = Bear + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *HaramiCrossStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/harami_cross_test.go b/pkg/indicator/v2/harami_cross_test.go new file mode 100644 index 0000000000..ead6827e67 --- /dev/null +++ b/pkg/indicator/v2/harami_cross_test.go @@ -0,0 +1,42 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestHaramiCross(t *testing.T) { + ts := []types.KLine{ + {Open: n(20.12), Low: n(19.88), High: n(23.82), Close: n(23.50)}, + {Open: n(22.13), Low: n(21.31), High: n(22.76), Close: n(22.13)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := HaramiCross(kLines, Bearish, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestHaramiCross Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(25.13), Low: n(21.7), High: n(25.80), Close: n(22.14)}, + {Open: n(23.45), Low: n(23.07), High: n(24.59), Close: n(23.45)}, + } + + ind = HaramiCross(kLines, Bullish, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestHaramiCross Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/harami_test.go b/pkg/indicator/v2/harami_test.go new file mode 100644 index 0000000000..2a79fdc43e --- /dev/null +++ b/pkg/indicator/v2/harami_test.go @@ -0,0 +1,41 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestHarami(t *testing.T) { + ts := []types.KLine{ + {Open: n(100), Low: n(95), High: n(125), Close: n(120)}, + {Open: n(110), Low: n(100), High: n(115), Close: n(105)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := Harami(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestHarami Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(120), Low: n(95), High: n(125), Close: n(100)}, + {Open: n(105), Low: n(100), High: n(115), Close: n(110)}, + } + ind = Harami(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestHarami Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/head_shoulders.go b/pkg/indicator/v2/head_shoulders.go new file mode 100644 index 0000000000..e5b05a1a7b --- /dev/null +++ b/pkg/indicator/v2/head_shoulders.go @@ -0,0 +1,58 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type HeadShoulderStream struct { + *types.Float64Series + + max *MaxValueStream + min *MinValueStream + window int +} + +// Basic Head-Shoulder Detection +func HeadShoulderSimple(source KLineSubscription) *HeadShoulderStream { + var ( + window = 3 + high = HighPrices(source) + low = LowPrices(source) + s = &HeadShoulderStream{ + Float64Series: types.NewFloat64Series(), + max: MaxValue(high, window), + min: MinValue(low, window), + window: window, + } + ) + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + if s.min.Last(1) < low.Last(2) && + s.min.Last(1) < low.Last(0) && + low.Last(1) > low.Last(2) && + low.Last(1) > low.Last(0) { + output = Bull // inverse head/shoulder pattern + } else if s.max.Last(1) > high.Last(2) && + s.max.Last(1) > high.Last(0) && + high.Last(1) < high.Last(2) && + high.Last(1) < high.Last(0) { + output = Bear // head/shoulder pattern + } + + s.PushAndEmit(output) + }) + + return s +} + +func (s *HeadShoulderStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/head_shoulders_test.go b/pkg/indicator/v2/head_shoulders_test.go new file mode 100644 index 0000000000..f8639f6b61 --- /dev/null +++ b/pkg/indicator/v2/head_shoulders_test.go @@ -0,0 +1,55 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestHeadShoulderSimple(t *testing.T) { + ts := []types.KLine{ + {Open: n(90), Low: n(80), High: n(95), Close: n(90)}, + {Open: n(85), Low: n(75), High: n(90), Close: n(85)}, + {Open: n(80), Low: n(70), High: n(85), Close: n(80)}, + {Open: n(90), Low: n(80), High: n(95), Close: n(90)}, + {Open: n(85), Low: n(75), High: n(90), Close: n(85)}, + {Open: n(80), Low: n(70), High: n(85), Close: n(80)}, + {Open: n(75), Low: n(65), High: n(80), Close: n(75)}, + {Open: n(80), Low: n(70), High: n(85), Close: n(80)}, + {Open: n(85), Low: n(75), High: n(90), Close: n(85)}, + {Open: n(90), Low: n(80), High: n(95), Close: n(90)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := HeadShoulderSimple(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(2) != expectedBear { + t.Errorf("TestHeadShoulder unexpected result: got %v want %v", ind.Last(2), expectedBear) + } + inverse := []types.KLine{ + {Open: n(20), Low: n(15), High: n(25), Close: n(20)}, + {Open: n(25), Low: n(20), High: n(30), Close: n(25)}, + {Open: n(30), Low: n(25), High: n(35), Close: n(30)}, + {Open: n(20), Low: n(15), High: n(25), Close: n(20)}, + {Open: n(25), Low: n(20), High: n(30), Close: n(25)}, + {Open: n(30), Low: n(25), High: n(35), Close: n(30)}, + {Open: n(35), Low: n(30), High: n(40), Close: n(35)}, + {Open: n(30), Low: n(25), High: n(35), Close: n(30)}, + {Open: n(25), Low: n(20), High: n(30), Close: n(25)}, + {Open: n(20), Low: n(15), High: n(25), Close: n(20)}, + } + for _, candle := range inverse { + stream.EmitKLineClosed(candle) + } + + expectedBull := 1.0 + + if ind.Last(2) != expectedBull { + t.Errorf("TestInverseHeadShoulder unexpected result: got %v want %v", ind.Last(2), expectedBull) + } +} diff --git a/pkg/indicator/v2/kdj.go b/pkg/indicator/v2/kdj.go new file mode 100644 index 0000000000..ca7d89f1fc --- /dev/null +++ b/pkg/indicator/v2/kdj.go @@ -0,0 +1,66 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type KDJStream struct { + *types.Float64Series + K *SMAStream + D *SMAStream + min *MinValueStream + max *MaxValueStream + window int +} + +// The Kdj function calculates the KDJ indicator, also known as +// the Random Index. KDJ is calculated similar to the Stochastic +// Oscillator with the difference of having the J line. It is +// used to analyze the trend and entry points. +// +// The K and D lines show if the asset is overbought when they +// crosses above 80%, and oversold when they crosses below +// 20%. The J line represents the divergence. +// +// RSV = ((Closing - Min(Low, rPeriod)) +// +// / (Max(High, rPeriod) - Min(Low, rPeriod))) * 100 +// +// K = Sma(RSV, kPeriod) +// D = Sma(K, dPeriod) +// J = (3 * K) - (2 * D) +func KDJ(source KLineSubscription, window, kWindow, dWindow int) *KDJStream { + + s := &KDJStream{ + Float64Series: types.NewFloat64Series(), + min: MinValue(LowPrices(source), window), + max: MaxValue(HighPrices(source), window), + K: SMA(nil, kWindow), + D: SMA(nil, dWindow), + window: window, + } + source.AddSubscriber(func(v types.KLine) { + var ( + closing = v.Close.Float64() + highest = s.max.Last(0) + lowest = s.min.Last(0) + rsv = (closing - lowest) / (highest - lowest) * 100 + k = s.K.Calculate(rsv) + d = s.D.Calculate(k) + j = (3 * k) - (2 * d) + ) + s.PushAndEmit(j) + }) + + return s +} + +// The DefaultKdj function calculates KDJ based on default periods +// consisting of window of 9, kPeriod of 3, and dPeriod of 3. +func KDJDefault(source KLineSubscription) *KDJStream { + return KDJ(source, 9, 3, 3) +} + +func (s *KDJStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/kdj_test.go b/pkg/indicator/v2/kdj_test.go new file mode 100644 index 0000000000..7c806904d3 --- /dev/null +++ b/pkg/indicator/v2/kdj_test.go @@ -0,0 +1,37 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestKdj(t *testing.T) { + ts := []types.KLine{ + {Low: n(1), High: n(10), Close: n(5)}, + {Low: n(2), High: n(20), Close: n(10)}, + {Low: n(3), High: n(30), Close: n(15)}, + {Low: n(4), High: n(40), Close: n(20)}, + {Low: n(5), High: n(50), Close: n(25)}, + {Low: n(6), High: n(60), Close: n(30)}, + {Low: n(7), High: n(70), Close: n(35)}, + {Low: n(8), High: n(80), Close: n(40)}, + {Low: n(9), High: n(90), Close: n(45)}, + {Low: n(10), High: n(100), Close: n(50)}, + } + // expectedK := []float64{44.44, 45.91, 46.70, 48.12, 48.66, 48.95, 49.14, 49.26, 49.36, 49.26} + // expectedD := []float64{44.44, 45.18, 45.68, 46.91, 47.82, 48.58, 48.91, 49.12, 49.25, 49.30} + expectedJ := []float64{44.44, 47.37, 48.72, 50.55, 50.32, 49.70, 49.58, 49.56, 49.57, 49.19} + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := KDJDefault(kLines) + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + for i, v := range expectedJ { + assert.InDelta(t, v, ind.Slice[i], 0.01, "Expected KDJ.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } +} diff --git a/pkg/indicator/v2/kicking.go b/pkg/indicator/v2/kicking.go new file mode 100644 index 0000000000..9113d683e7 --- /dev/null +++ b/pkg/indicator/v2/kicking.go @@ -0,0 +1,74 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + + "github.com/c9s/bbgo/pkg/types" +) + +type KickingStream struct { + *types.Float64Series + + window int +} + +func Kicking(source KLineSubscription, maxDiff float64) *KickingStream { + s := &KickingStream{ + Float64Series: types.NewFloat64Series(), + window: 2, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + two = source.Last(1) + one = source.Last(0) + ) + + // BEAR + if two.Open.Float64() < two.Close.Float64() { + if one.Open.Float64() > one.Close.Float64() { + if two.Open.Float64() > one.Open.Float64() { + if fixedpoint.ApproxEqual(two.Open, two.Low, maxDiff) && + fixedpoint.ApproxEqual(two.Close, two.High, maxDiff) { + if fixedpoint.ApproxEqual(one.Open, one.High, maxDiff) && + fixedpoint.ApproxEqual(one.Close, one.Low, maxDiff) { + output = Bear + } + } + } + } + } + + // BULL + if two.Open.Float64() > two.Close.Float64() { + if one.Open.Float64() < one.Close.Float64() { + if two.Open.Float64() < one.Open.Float64() { + if fixedpoint.ApproxEqual(two.Open, two.High, maxDiff) && + fixedpoint.ApproxEqual(two.Close, two.Low, maxDiff) { + if fixedpoint.ApproxEqual(one.Open, one.Low, maxDiff) && + fixedpoint.ApproxEqual(one.Close, one.High, maxDiff) { + output = Bull + } + } + } + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *KickingStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/kicking_test.go b/pkg/indicator/v2/kicking_test.go new file mode 100644 index 0000000000..53e9a5634b --- /dev/null +++ b/pkg/indicator/v2/kicking_test.go @@ -0,0 +1,41 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestKicking(t *testing.T) { + ts := []types.KLine{ + {Open: n(100), Low: n(100), High: n(120), Close: n(120)}, + {Open: n(90), Low: n(70), High: n(90), Close: n(70)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := Kicking(kLines, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestKicking Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(90), Low: n(70), High: n(90), Close: n(70)}, + {Open: n(100), Low: n(100), High: n(120), Close: n(120)}, + } + ind = Kicking(kLines, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestKicking Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/macd_test.go b/pkg/indicator/v2/macd_test.go index 3b6b72405c..40778ca3c4 100644 --- a/pkg/indicator/v2/macd_test.go +++ b/pkg/indicator/v2/macd_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" ) diff --git a/pkg/indicator/v2/marubozu.go b/pkg/indicator/v2/marubozu.go new file mode 100644 index 0000000000..93ad35da6d --- /dev/null +++ b/pkg/indicator/v2/marubozu.go @@ -0,0 +1,51 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + + "github.com/c9s/bbgo/pkg/types" +) + +type MarubozuStream struct { + *types.Float64Series + + window int +} + +func Marubozu(source KLineSubscription, maxDiff float64) *MarubozuStream { + s := &MarubozuStream{ + Float64Series: types.NewFloat64Series(), + window: 2, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + output = Neutral + one = source.Last(0) + ) + // BEAR + if one.Open.Float64() > one.Close.Float64() { + if fixedpoint.ApproxEqual(one.High, one.Open, maxDiff) && + fixedpoint.ApproxEqual(one.Low, one.Close, maxDiff) { + output = Bear + } + } + + // BULL + if one.Open.Float64() < one.Close.Float64() { + if fixedpoint.ApproxEqual(one.Low, one.Open, maxDiff) && + fixedpoint.ApproxEqual(one.High, one.Close, maxDiff) { + output = Bull + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *MarubozuStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/marubozu_test.go b/pkg/indicator/v2/marubozu_test.go new file mode 100644 index 0000000000..0cb0ada31d --- /dev/null +++ b/pkg/indicator/v2/marubozu_test.go @@ -0,0 +1,39 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestMarubozu(t *testing.T) { + ts := []types.KLine{ + {Open: n(200), Low: n(100), High: n(200), Close: n(100)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := Marubozu(kLines, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestMarubozu Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(100), Low: n(100), High: n(200), Close: n(200)}, + } + ind = Marubozu(kLines, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestMarubozu Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/maxval.go b/pkg/indicator/v2/maxval.go new file mode 100644 index 0000000000..66d3bcb372 --- /dev/null +++ b/pkg/indicator/v2/maxval.go @@ -0,0 +1,41 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" + "github.com/c9s/bbgo/pkg/types/bst" +) + +type MaxValueStream struct { + *types.Float64Series + bst *bst.Tree + buffer []float64 + window int +} + +func MaxValue(source types.Float64Source, window int) *MaxValueStream { + s := &MaxValueStream{ + Float64Series: types.NewFloat64Series(), + bst: bst.New(), + buffer: make([]float64, window), + window: window, + } + + s.Bind(source, s) + + return s +} + +func (s *MaxValueStream) Calculate(v float64) float64 { + s.bst.Insert(v) + var i = s.Slice.Length() + if i > 0 { + s.bst.Remove(s.buffer[i%s.window]) + } + + s.buffer[i%s.window] = v + return s.bst.Max().(float64) +} + +func (s *MaxValueStream) Truncate() { + s.Slice = s.Slice.Truncate(s.window + 100) +} diff --git a/pkg/indicator/v2/maxval_test.go b/pkg/indicator/v2/maxval_test.go new file mode 100644 index 0000000000..a131dae7f5 --- /dev/null +++ b/pkg/indicator/v2/maxval_test.go @@ -0,0 +1,24 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestMaximumValueIndicator(t *testing.T) { + t.Run("rolling max series", func(t *testing.T) { + data := []float64{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} + // expected := []float64{10, 10, 10, 10, 9, 8, 7, 6, 5, 4} + + source := types.NewFloat64Series() + ind := MaxValue(source, 4) + + for _, d := range data { + source.PushAndEmit(d) + } + assert.Equal(t, 4.0, ind.Last(0)) + }) +} diff --git a/pkg/indicator/v2/meeting_lines.go b/pkg/indicator/v2/meeting_lines.go new file mode 100644 index 0000000000..7de531ec53 --- /dev/null +++ b/pkg/indicator/v2/meeting_lines.go @@ -0,0 +1,65 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type MeetingLinesStream struct { + *types.Float64Series + + window int +} + +func MeetingLines(source KLineSubscription) *MeetingLinesStream { + s := &MeetingLinesStream{ + Float64Series: types.NewFloat64Series(), + window: 3, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + ) + + // BEAR + if three.Open.Float64() < three.Close.Float64() { + if two.Open.Float64() < two.Close.Float64() { + if one.Open.Float64() > one.Close.Float64() { + if fixedpoint.Abs(two.Close.Sub(one.Close).Div(one.Close)).Float64() < threshold { + output = Bear + } + } + } + } + + // BULL + if three.Open.Float64() > three.Close.Float64() { + if two.Open.Float64() > two.Close.Float64() { + if one.Open.Float64() < one.Close.Float64() { + if fixedpoint.Abs(two.Close.Sub(one.Close).Div(one.Close)).Float64() < threshold { + output = Bull + } + } + } + } + s.PushAndEmit(output) + + }) + + return s +} + +func (s *MeetingLinesStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/meeting_lines_test.go b/pkg/indicator/v2/meeting_lines_test.go new file mode 100644 index 0000000000..2ae5d4ab2c --- /dev/null +++ b/pkg/indicator/v2/meeting_lines_test.go @@ -0,0 +1,43 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestMeetingLines(t *testing.T) { + ts := []types.KLine{ + {Open: n(85), Low: n(85), High: n(100), Close: n(95)}, + {Open: n(95), Low: n(90), High: n(120), Close: n(115)}, + {Open: n(130), Low: n(105), High: n(140), Close: n(110)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := MeetingLines(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestMeetingLines Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(200), Low: n(180), High: n(210), Close: n(190)}, + {Open: n(180), Low: n(140), High: n(195), Close: n(150)}, + {Open: n(110), Low: n(105), High: n(160), Close: n(155)}, + } + ind = MeetingLines(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestMeetingLines Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/minval.go b/pkg/indicator/v2/minval.go new file mode 100644 index 0000000000..6c7be658e0 --- /dev/null +++ b/pkg/indicator/v2/minval.go @@ -0,0 +1,41 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" + "github.com/c9s/bbgo/pkg/types/bst" +) + +type MinValueStream struct { + *types.Float64Series + bst *bst.Tree + buffer []float64 + window int +} + +func MinValue(source types.Float64Source, window int) *MinValueStream { + s := &MinValueStream{ + Float64Series: types.NewFloat64Series(), + bst: bst.New(), + buffer: make([]float64, window), + window: window, + } + + s.Bind(source, s) + + return s +} + +func (s *MinValueStream) Calculate(v float64) float64 { + s.bst.Insert(v) + var i = s.Slice.Length() + if i > 0 { + s.bst.Remove(s.buffer[i%s.window]) + } + + s.buffer[i%s.window] = v + return s.bst.Min().(float64) +} + +func (s *MinValueStream) Truncate() { + s.Slice = s.Slice.Truncate(s.window + 100) +} diff --git a/pkg/indicator/v2/minval_test.go b/pkg/indicator/v2/minval_test.go new file mode 100644 index 0000000000..d607a1787e --- /dev/null +++ b/pkg/indicator/v2/minval_test.go @@ -0,0 +1,24 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestMinimumValueIndicator(t *testing.T) { + t.Run("rolling min series", func(t *testing.T) { + data := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + // expected := []float64{1, 1, 1, 1, 2, 3, 4, 5, 6, 7} + + source := types.NewFloat64Series() + ind := MinValue(source, 4) + + for _, d := range data { + source.PushAndEmit(d) + } + assert.Equal(t, 7.0, ind.Last(0)) + }) +} diff --git a/pkg/indicator/v2/morning_evening_star.go b/pkg/indicator/v2/morning_evening_star.go new file mode 100644 index 0000000000..723b2598f1 --- /dev/null +++ b/pkg/indicator/v2/morning_evening_star.go @@ -0,0 +1,81 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type MorningOrEveningStarStream struct { + *types.Float64Series + + window int +} + +// An evening star is a candlestick pattern that's used by technical analysts to predict future price reversals +// to the downside. +// The evening star pattern is rare but it's considered by traders to be a reliable technical indicator. +// The evening star is the opposite of the morning star. +// The candlestick pattern is bearish whereas the morning star pattern is bullish. +func MorningOrEveningStar(source KLineSubscription, direction Direction) *MorningOrEveningStarStream { + s := &MorningOrEveningStarStream{ + Float64Series: types.NewFloat64Series(), + window: 3, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + firstMidpoint = three.Open.Add(three.Close).Div(fixedpoint.Two).Float64() + ) + if direction == Bullish { + var ( + isFirstBearish = three.Close.Float64() < three.Open.Float64() + hasSmallBody = three.Low.Float64() > two.Low.Float64() && + three.Low.Float64() > two.High.Float64() + isThirdBullish = one.Open.Float64() < one.Close.Float64() + gapExists = two.High.Float64() < three.Low.Float64() && + two.Low.Float64() < three.Low.Float64() && + one.Open.Float64() > two.High.Float64() && + two.Close.Float64() < one.Open.Float64() + doesCloseAboveFirstMidpoint = one.Close.Float64() > firstMidpoint + ) + if isFirstBearish && hasSmallBody && gapExists && isThirdBullish && doesCloseAboveFirstMidpoint { + output = Bull // morning star + } + } else { + var ( + isFirstBullish = three.Close.Float64() > three.Open.Float64() + hasSmallBody = three.High.Float64() < two.Low.Float64() && + three.High.Float64() < two.High.Float64() + isThirdBearish = one.Open.Float64() > one.Close.Float64() + gapExists = two.High.Float64() > three.High.Float64() && + two.Low.Float64() > three.High.Float64() && + one.Open.Float64() < two.Low.Float64() && + two.Close.Float64() > one.Open.Float64() + doesCloseBelowFirstMidpoint = one.Close.Float64() < firstMidpoint + ) + if isFirstBullish && hasSmallBody && gapExists && isThirdBearish && doesCloseBelowFirstMidpoint { + output = Bear // evening star + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *MorningOrEveningStarStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/morning_evening_star_test.go b/pkg/indicator/v2/morning_evening_star_test.go new file mode 100644 index 0000000000..92e7b00360 --- /dev/null +++ b/pkg/indicator/v2/morning_evening_star_test.go @@ -0,0 +1,46 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestMorningOrEveningStar(t *testing.T) { + ts := []types.KLine{ + {Open: n(18.35), Low: n(18.13), High: n(21.60), Close: n(21.30)}, + {Open: n(22.20), Low: n(21.87), High: n(22.70), Close: n(22.52)}, + {Open: n(21.60), Low: n(19.30), High: n(22.05), Close: n(19.45)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := MorningOrEveningStar(kLines, Bearish) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestEveningStar Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(22.20), Low: n(20.65), High: n(22.50), Close: n(20.80)}, + {Open: n(20.30), Low: n(19.60), High: n(20.45), Close: n(19.80)}, + {Open: n(20.70), Low: n(20.40), High: n(21.82), Close: n(21.58)}, + } + stream = &types.StandardStream{} + kLines = KLines(stream, "", "") + ind = MorningOrEveningStar(kLines, Bullish) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestMorningStar unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/negative_volume_index.go b/pkg/indicator/v2/negative_volume_index.go new file mode 100644 index 0000000000..e6f4a5de8c --- /dev/null +++ b/pkg/indicator/v2/negative_volume_index.go @@ -0,0 +1,54 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +// Starting value for NVI. +var NVI_STARTING_VALUE = fixedpoint.NewFromInt(1000) + +// Negative Volume Index (NVI) +type NVIStream struct { + *types.Float64Series +} + +// The [NegativeVolumeIndex](https://pkg.go.dev/github.com/cinar/indicator#NegativeVolumeIndex) +// function calculates a cumulative indicator using the change in volume to decide when the smart money is active. +// +// If Volume is greather than Previous Volume: +// NVI = Previous NVI +// Otherwise: +// NVI = Previous NVI + (((Closing - Previous Closing) / Previous Closing) * Previous NVI) +func NegativeVolumeIndex(source KLineSubscription) *NVIStream { + s := &NVIStream{ + Float64Series: types.NewFloat64Series(), + } + + source.AddSubscriber(func(v types.KLine) { + var nvi = fixedpoint.Zero + + if s.Length() == 0 { + nvi = NVI_STARTING_VALUE + } else { + var ( + prev = source.Last(1) + prevNVI = fixedpoint.NewFromFloat(s.Slice.Last(0)) + ) + if v.Volume > prev.Volume { + nvi = prevNVI + } else { + inner := v.Close.Sub(prev.Close).Div(prev.Close) + nvi = prevNVI.Add(inner.Mul(prevNVI)) + } + } + + s.PushAndEmit(nvi.Float64()) + }) + + return s +} + +func (s *NVIStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/negative_volume_index_test.go b/pkg/indicator/v2/negative_volume_index_test.go new file mode 100644 index 0000000000..a044ae6d80 --- /dev/null +++ b/pkg/indicator/v2/negative_volume_index_test.go @@ -0,0 +1,34 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestNegativeVolumeIndex(t *testing.T) { + ts := []types.KLine{ + {Volume: n(100), Close: n(9)}, + {Volume: n(110), Close: n(11)}, + {Volume: n(80), Close: n(7)}, + {Volume: n(120), Close: n(10)}, + {Volume: n(90), Close: n(8)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := NegativeVolumeIndex(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + + assert.InDelta(t, 509.09, ind.Last(0), 0.01) + assert.InDelta(t, 636.36, ind.Last(1), 0.01) + assert.InDelta(t, 636.36, ind.Last(2), 0.01) + assert.InDelta(t, 1000.0, ind.Last(3), 0.01) + assert.InDelta(t, 1000.0, ind.Last(4), 0.01) + +} diff --git a/pkg/indicator/v2/obv.go b/pkg/indicator/v2/obv.go new file mode 100644 index 0000000000..6f10c58922 --- /dev/null +++ b/pkg/indicator/v2/obv.go @@ -0,0 +1,40 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type OBVStream struct { + *types.Float64Series +} + +// The [Obv](https://pkg.go.dev/github.com/cinar/indicator#Obv) function calculates a technical +// trading momentum indicator that uses volume flow to predict changes in stock price. +func OBV(source KLineSubscription) *OBVStream { + s := &OBVStream{ + Float64Series: types.NewFloat64Series(), + } + + source.AddSubscriber(func(v types.KLine) { + var obv = .0 + + if source.Length() > 1 { + prev := source.Last(1) + obv = s.Slice.Last(0) + + if v.Close.Float64() > prev.Close.Float64() { + obv += v.Volume.Float64() + } else if v.Close.Float64() < prev.Close.Float64() { + obv -= v.Volume.Float64() + } + } + + s.PushAndEmit(obv) + }) + + return s +} + +func (s *OBVStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/obv_test.go b/pkg/indicator/v2/obv_test.go new file mode 100644 index 0000000000..dd15fe1bef --- /dev/null +++ b/pkg/indicator/v2/obv_test.go @@ -0,0 +1,38 @@ +package indicatorv2 + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/fixedpoint" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestOBV(t *testing.T) { + close := []byte(`[53.26,53.30,53.32,53.72,54.19,53.92,54.65,54.60,54.21,54.53,53.79,53.66,53.56,53.57,53.94,53.27]`) + volume := []byte(`[88888,8200,8100,8300,8900,9200,13300,10300,9900,10100,11300,12600,10700,11500,23800,14600]`) + buildKLines := func(close, volume []fixedpoint.Value) (kLines []types.KLine) { + for i := range close { + kLines = append(kLines, types.KLine{Close: close[i], Volume: volume[i]}) + } + return kLines + } + var c, v []fixedpoint.Value + _ = json.Unmarshal(close, &c) + _ = json.Unmarshal(volume, &v) + + expected := []float64{0, 8200, 16300, 24600, 33500, 24300, 37600, 27300, 17400, 27500, 16200, 3600, -7100, 4400, 28200, 13600} + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := OBV(kLines) + k := buildKLines(c, v) + for _, candle := range k { + stream.EmitKLineClosed(candle) + } + for i, v := range expected { + assert.InDelta(t, v, ind.Slice[i], 0.01, "Expected OBV.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } +} diff --git a/pkg/indicator/v2/parabolic_sar.go b/pkg/indicator/v2/parabolic_sar.go new file mode 100644 index 0000000000..a53f766f11 --- /dev/null +++ b/pkg/indicator/v2/parabolic_sar.go @@ -0,0 +1,119 @@ +package indicatorv2 + +import ( + "math" + + "github.com/c9s/bbgo/pkg/types" +) + +// Parabolic SAR. It is a popular technical indicator for identifying the trend +// and as a trailing stop. +// +// PSAR = PSAR[i - 1] - ((PSAR[i - 1] - EP) * AF) +// +// If the trend is Falling: +// - PSAR is the maximum of PSAR or the previous two high values. +// - If the current high is greather than or equals to PSAR, use EP. +// +// If the trend is Rising: +// - PSAR is the minimum of PSAR or the previous two low values. +// - If the current low is less than or equals to PSAR, use EP. +// +// If PSAR is greater than the closing, trend is falling, and the EP +// is set to the minimum of EP or the low. +// +// If PSAR is lower than or equals to the closing, trend is rising, and the EP +// is set to the maximum of EP or the high. +// +// If the trend is the same, and AF is less than 0.20, increment it by 0.02. +// If the trend is not the same, set AF to 0.02. +// +// Based on video https://www.youtube.com/watch?v=MuEpGBAH7pw&t=0s. + +// Trend indicator. +type Trend int + +const ( + Falling Trend = -1 + Rising Trend = 1 + psarAfStep = 0.02 + psarAfMax = 0.20 +) + +type PSARStream struct { + *types.Float64Series + Trend []Trend + af, ep float64 +} + +func ParabolicSar(source KLineSubscription) *PSARStream { + var ( + low = LowPrices(source) + high = HighPrices(source) + closing = ClosePrices(source) + s = &PSARStream{ + Float64Series: types.NewFloat64Series(), + Trend: []Trend{}, + } + ) + source.AddSubscriber(func(v types.KLine) { + var ( + psar float64 + i = source.Length() - 1 + ) + if i == 0 { + s.Trend = append(s.Trend, Falling) + psar = high.Last(0) + s.af = psarAfStep + s.ep = low.Last(0) + s.PushAndEmit(psar) + return + } + + var prevPsar = s.Slice.Last(0) + + psar = prevPsar - ((prevPsar - s.ep) * s.af) + + if s.Trend[i-1] == Falling { + psar = math.Max(psar, high.Last(1)) + if i > 1 { + psar = math.Max(psar, high.Last(2)) + } + if high.Last(0) >= psar { + psar = s.ep + } + } else { + psar = math.Min(psar, low.Last(1)) + if i > 1 { + psar = math.Min(psar, low.Last(2)) + } + if low.Last(0) <= psar { + psar = s.ep + } + } + + var prevEp = s.ep + + if psar > closing.Last(0) { + s.Trend = append(s.Trend, Falling) + s.ep = math.Min(s.ep, low.Last(0)) + } else { + s.Trend = append(s.Trend, Rising) + s.ep = math.Max(s.ep, high.Last(0)) + } + + if s.Trend[i] != s.Trend[i-1] { + s.af = psarAfStep + } else if prevEp != s.ep && s.af < psarAfMax { + s.af += psarAfStep + } + + s.PushAndEmit(psar) + }) + + return s +} + +func (s *PSARStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/parabolic_sar_test.go b/pkg/indicator/v2/parabolic_sar_test.go new file mode 100644 index 0000000000..ac7f95d8e5 --- /dev/null +++ b/pkg/indicator/v2/parabolic_sar_test.go @@ -0,0 +1,61 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestParabolicSAR(t *testing.T) { + ts := []types.KLine{ + {Open: n(3836.86), High: n(3836.86), Low: n(3643.25), Close: n(3790.55)}, + {Open: n(3766.57), High: n(3766.57), Low: n(3542.73), Close: n(3546.20)}, + {Open: n(3576.17), High: n(3576.17), Low: n(3371.75), Close: n(3507.31)}, + {Open: n(3513.55), High: n(3513.55), Low: n(3334.02), Close: n(3340.81)}, + {Open: n(3529.75), High: n(3529.75), Low: n(3314.75), Close: n(3529.60)}, + {Open: n(3756.17), High: n(3756.17), Low: n(3558.21), Close: n(3717.41)}, + {Open: n(3717.17), High: n(3717.17), Low: n(3517.79), Close: n(3544.35)}, + {Open: n(3572.62), High: n(3572.62), Low: n(3447.90), Close: n(3478.14)}, + {Open: n(3612.43), High: n(3612.43), Low: n(3494.39), Close: n(3612.08)}, + } + + expectedValues := []float64{ + 3836.86, + 3836.86, + 3836.86, + 3808.95, + 3770.96, + 3314.75, + 3314.75, + 3323.58, + 3332.23, + } + + expectedTrend := []Trend{ + Falling, + Falling, + Falling, + Falling, + Falling, + Rising, + Rising, + Rising, + Rising, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := ParabolicSar(kLines) + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + for i, v := range expectedValues { + assert.InDelta(t, v, ind.Slice[i], 0.01, "Expected PSAR.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } + + for i, v := range expectedTrend { + assert.Equal(t, v, ind.Trend[i], "Expected PSAR.Trend[%d] to be %v, but got %v", i, v, ind.Trend[i]) + } +} diff --git a/pkg/indicator/v2/pattern.go b/pkg/indicator/v2/pattern.go new file mode 100644 index 0000000000..f4e98dc3f2 --- /dev/null +++ b/pkg/indicator/v2/pattern.go @@ -0,0 +1,24 @@ +package indicatorv2 + +import "github.com/c9s/bbgo/pkg/fixedpoint" + +// OrderSide represents the side of an order: Buy (long) or Sell (short). +type Direction int + +const ( + MaxNumOfPattern = 5_000 + Bullish Direction = iota + 1 + Bearish +) + +var ( + Neutral = .0 + Bull = 1. + Bear = -1. + threshold = .1 + limit = .2 +) + +func n(n float64) fixedpoint.Value { + return fixedpoint.NewFromFloat(float64(n)) +} diff --git a/pkg/indicator/v2/piercing_line.go b/pkg/indicator/v2/piercing_line.go new file mode 100644 index 0000000000..5f618f8fca --- /dev/null +++ b/pkg/indicator/v2/piercing_line.go @@ -0,0 +1,52 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type PiercingLineStream struct { + *types.Float64Series + + window int +} + +func PiercingLine(source KLineSubscription) *PiercingLineStream { + s := &PiercingLineStream{ + Float64Series: types.NewFloat64Series(), + window: 2, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + two = source.Last(1) + one = source.Last(0) + firstMidpoint = two.Open.Add(two.Close).Div(fixedpoint.Two).Float64() + isDowntrend = one.Low.Float64() < two.Low.Float64() + isFirstBearish = two.Close.Float64() < two.Open.Float64() + isSecondBullish = one.Close.Float64() > one.Open.Float64() + isPiercingLine = two.Low.Float64() > one.Open.Float64() && + one.Close.Float64() > firstMidpoint + ) + if isDowntrend && isFirstBearish && isSecondBullish && isPiercingLine { + output = Bull + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *PiercingLineStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/piercing_line_test.go b/pkg/indicator/v2/piercing_line_test.go new file mode 100644 index 0000000000..46b3c5bdb8 --- /dev/null +++ b/pkg/indicator/v2/piercing_line_test.go @@ -0,0 +1,27 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestPiercingLine(t *testing.T) { + ts := []types.KLine{ + {Open: n(42.70), Low: n(41.45), High: n(42.82), Close: n(41.60)}, + {Open: n(41.33), Low: n(41.15), High: n(42.50), Close: n(42.34)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := PiercingLine(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestPiercingLine Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/price.go b/pkg/indicator/v2/price.go index 6f48439e04..c30a32a3d3 100644 --- a/pkg/indicator/v2/price.go +++ b/pkg/indicator/v2/price.go @@ -1,6 +1,8 @@ package indicatorv2 import ( + "math" + "github.com/c9s/bbgo/pkg/types" ) @@ -69,3 +71,36 @@ func Volumes(source KLineSubscription) *PriceStream { func HLC3(source KLineSubscription) *PriceStream { return Price(source, types.KLineHLC3Mapper) } + +func HLC3MulVolume(source KLineSubscription) *PriceStream { + return Price(source, types.KLineHLC3xVMapper) +} + +func HL2(source KLineSubscription) *PriceStream { + return Price(source, types.KLineHL2Mapper) +} + +func CloseMulVolume(source KLineSubscription) *PriceStream { + return Price(source, types.KLineCxVMapper) +} + +func CloseSubOpen(source KLineSubscription) *PriceStream { + return Price(source, types.KLineCOMapper) +} + +func DiffClose(source KLineSubscription) *PriceStream { + s := &PriceStream{ + Float64Series: types.NewFloat64Series(), + } + + if source == nil { + return s + } + + source.AddSubscriber(func(k types.KLine) { + if source.Length() > 0 { + s.PushAndEmit(math.Log(k.Close.Div(source.Last(0).Close).Float64())) + } + }) + return s +} diff --git a/pkg/indicator/v2/qstick.go b/pkg/indicator/v2/qstick.go new file mode 100644 index 0000000000..96068852ca --- /dev/null +++ b/pkg/indicator/v2/qstick.go @@ -0,0 +1,27 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type QstickStream struct { + *types.Float64Series + sma *SMAStream +} + +// The Qstick function calculates the ratio of recent up and down bars. +// +// QS = Sma(Closing - Opening) +func Qstick(source KLineSubscription, window int) *QstickStream { + var ( + s = &QstickStream{ + Float64Series: types.NewFloat64Series(), + sma: SMA(CloseSubOpen(source), window), + } + ) + source.AddSubscriber(func(v types.KLine) { + s.PushAndEmit(s.sma.Last(0)) + }) + + return s +} diff --git a/pkg/indicator/v2/qstick_test.go b/pkg/indicator/v2/qstick_test.go new file mode 100644 index 0000000000..d066b9dcb5 --- /dev/null +++ b/pkg/indicator/v2/qstick_test.go @@ -0,0 +1,33 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestQstick(t *testing.T) { + ts := []types.KLine{ + {Open: n(10), Close: n(20)}, + {Open: n(20), Close: n(15)}, + {Open: n(15), Close: n(50)}, + {Open: n(50), Close: n(55)}, + {Open: n(40), Close: n(42)}, + {Open: n(41), Close: n(30)}, + {Open: n(43), Close: n(31)}, + {Open: n(80), Close: n(70)}, + } + expected := []float64{10, 2.5, 13.33, 11.25, 9.4, 5.2, 3.8, -5.2} + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := Qstick(kLines, 5) + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + for i, v := range expected { + assert.InDelta(t, v, ind.Slice[i], 0.01, "Expected QStick.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } +} diff --git a/pkg/indicator/v2/rma.go b/pkg/indicator/v2/rma.go index b943cf6f8d..f40082ae6c 100644 --- a/pkg/indicator/v2/rma.go +++ b/pkg/indicator/v2/rma.go @@ -51,9 +51,7 @@ func (s *RMAStream) Calculate(x float64) float64 { } func (s *RMAStream) Truncate() { - if len(s.Slice) > MaxNumOfRMA { - s.Slice = s.Slice[MaxNumOfRMATruncateSize-1:] - } + s.Slice = s.Slice.Truncate(MaxNumOfMA) } func checkWindow(window int) { diff --git a/pkg/indicator/v2/rsi.go b/pkg/indicator/v2/rsi.go index d4ca1489d8..f3654bd04e 100644 --- a/pkg/indicator/v2/rsi.go +++ b/pkg/indicator/v2/rsi.go @@ -17,8 +17,8 @@ type RSIStream struct { func RSI2(source types.Float64Source, window int) *RSIStream { s := &RSIStream{ - source: source, Float64Series: types.NewFloat64Series(), + source: source, window: window, } s.Bind(source, s) @@ -28,7 +28,7 @@ func RSI2(source types.Float64Source, window int) *RSIStream { func (s *RSIStream) Calculate(_ float64) float64 { var gainSum, lossSum float64 var sourceLen = s.source.Length() - var limit = min(s.window, sourceLen) + var limit = Min(s.window, sourceLen) for i := 0; i < limit; i++ { value := s.source.Last(i) prev := s.source.Last(i + 1) @@ -46,17 +46,3 @@ func (s *RSIStream) Calculate(_ float64) float64 { rsi := 100.0 - (100.0 / (1.0 + rs)) return rsi } - -func max(x, y int) int { - if x > y { - return x - } - return y -} - -func min(x, y int) int { - if x < y { - return x - } - return y -} diff --git a/pkg/indicator/v2/separating_lines.go b/pkg/indicator/v2/separating_lines.go new file mode 100644 index 0000000000..eb027e7fa4 --- /dev/null +++ b/pkg/indicator/v2/separating_lines.go @@ -0,0 +1,70 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type SeparatingLinesStream struct { + *types.Float64Series + + window int +} + +func SeparatingLines(source KLineSubscription, maxDiff float64) *SeparatingLinesStream { + s := &SeparatingLinesStream{ + Float64Series: types.NewFloat64Series(), + window: 3, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + + var ( + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + ) + // BEAR + if three.Open.Float64() > three.Close.Float64() { + if two.Open.Float64() < two.Close.Float64() { + if one.Open.Float64() > one.Close.Float64() { + if fixedpoint.Abs(two.Open.Sub(one.Open).Div(one.Open)).Float64() < threshold { + if fixedpoint.ApproxEqual(one.Open, one.High, maxDiff) { + output = Bear + } + } + } + } + } + + // BULL + if three.Open.Float64() < three.Close.Float64() { + if two.Open.Float64() > two.Close.Float64() { + if one.Open.Float64() < one.Close.Float64() { + if fixedpoint.Abs(two.Open.Sub(one.Open).Div(one.Open)).Float64() < threshold { + if fixedpoint.ApproxEqual(one.Open, one.Low, maxDiff) { + output = Bull + } + } + } + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *SeparatingLinesStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/separating_lines_test.go b/pkg/indicator/v2/separating_lines_test.go new file mode 100644 index 0000000000..82afa8b0ec --- /dev/null +++ b/pkg/indicator/v2/separating_lines_test.go @@ -0,0 +1,43 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestSeparatingLines(t *testing.T) { + ts := []types.KLine{ + {Open: n(200), Low: n(160), High: n(210), Close: n(170)}, + {Open: n(150), Low: n(140), High: n(190), Close: n(180)}, + {Open: n(152), Low: n(120), High: n(152), Close: n(130)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := SeparatingLines(kLines, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestSeparatingLines Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(50), Low: n(40), High: n(80), Close: n(70)}, + {Open: n(100), Low: n(70), High: n(110), Close: n(80)}, + {Open: n(102), Low: n(102), High: n(130), Close: n(120)}, + } + ind = SeparatingLines(kLines, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestSeparatingLines Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/side_by_side_whitelines.go b/pkg/indicator/v2/side_by_side_whitelines.go new file mode 100644 index 0000000000..cc8e57b12c --- /dev/null +++ b/pkg/indicator/v2/side_by_side_whitelines.go @@ -0,0 +1,83 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type SideBySideWhiteLinesStream struct { + *types.Float64Series + + window int +} + +func SideBySideWhiteLines(source KLineSubscription, maxDiff float64) *SideBySideWhiteLinesStream { + s := &SideBySideWhiteLinesStream{ + Float64Series: types.NewFloat64Series(), + window: 4, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + four = source.Last(3) + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + ) + + // BEAR + if four.Open.Float64() > four.Close.Float64() { + if three.Open.Float64() > three.Close.Float64() { + if three.Low.Float64() > two.High.Float64() { + if two.Open.Float64() < two.Close.Float64() { + if one.Open.Float64() < one.Close.Float64() { + if fixedpoint.Abs(two.Open.Sub(one.Open).Div(one.Open)).Float64() < threshold { + if fixedpoint.Abs(two.Close.Sub(one.Close).Div(one.Close)).Float64() < threshold { + if fixedpoint.ApproxEqual(two.Open, one.Open, maxDiff) { + output = Bear + } + } + } + } + } + } + } + } + + // BULL + if four.Open.Float64() < four.Close.Float64() { + if three.Open.Float64() < three.Close.Float64() { + if three.Low.Float64() < two.High.Float64() { + if two.Open.Float64() < two.Close.Float64() { + if one.Open.Float64() < one.Close.Float64() { + if fixedpoint.Abs(two.Open.Sub(one.Open).Div(one.Open)).Float64() < threshold { + if fixedpoint.Abs(two.Close.Sub(one.Close).Div(one.Close)).Float64() < threshold { + if fixedpoint.ApproxEqual(two.Open, one.Open, maxDiff) { + output = Bull + } + } + } + } + } + } + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *SideBySideWhiteLinesStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/side_by_side_whitelines_test.go b/pkg/indicator/v2/side_by_side_whitelines_test.go new file mode 100644 index 0000000000..0fcc872fa2 --- /dev/null +++ b/pkg/indicator/v2/side_by_side_whitelines_test.go @@ -0,0 +1,45 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestSideBySideWhiteLines(t *testing.T) { + ts := []types.KLine{ + {Open: n(130), Low: n(110), High: n(140), Close: n(120)}, + {Open: n(110), Low: n(85), High: n(115), Close: n(90)}, + {Open: n(50), Low: n(45), High: n(75), Close: n(70)}, + {Open: n(50), Low: n(42), High: n(77), Close: n(68)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := SideBySideWhiteLines(kLines, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestSideBySideWhiteLines Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(70), Low: n(60), High: n(90), Close: n(80)}, + {Open: n(100), Low: n(90), High: n(130), Close: n(120)}, + {Open: n(150), Low: n(140), High: n(210), Close: n(185)}, + {Open: n(150), Low: n(135), High: n(200), Close: n(190)}, + } + ind = SideBySideWhiteLines(kLines, 0.01) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestSideBySideWhiteLines Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/sma.go b/pkg/indicator/v2/sma.go index 5afd4f8250..f844a895e5 100644 --- a/pkg/indicator/v2/sma.go +++ b/pkg/indicator/v2/sma.go @@ -4,7 +4,7 @@ import ( "github.com/c9s/bbgo/pkg/types" ) -const MaxNumOfSMA = 5_000 +const MaxNumOfMA = 5_000 type SMAStream struct { *types.Float64Series @@ -29,5 +29,5 @@ func (s *SMAStream) Calculate(v float64) float64 { } func (s *SMAStream) Truncate() { - s.Slice = s.Slice.Truncate(MaxNumOfSMA) + s.Slice = s.Slice.Truncate(MaxNumOfMA) } diff --git a/pkg/indicator/v2/spinning_top.go b/pkg/indicator/v2/spinning_top.go new file mode 100644 index 0000000000..78f84eab26 --- /dev/null +++ b/pkg/indicator/v2/spinning_top.go @@ -0,0 +1,60 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" +) + +type SpinningTopStream struct { + *types.Float64Series +} + +// A spinning top is a candlestick pattern that has a short real body that's vertically centered +// between long upper and lower shadows. +// The real body should be small, showing little difference between the open and close prices. +// Since buyers and sellers both pushed the price, but couldn't maintain it, the pattern shows +// indecision. More sideways movement could follow. +func SpinningTop(source KLineSubscription, direction Direction) *SpinningTopStream { + s := &SpinningTopStream{ + Float64Series: types.NewFloat64Series(), + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + output = Neutral + one = source.Last(0) + bodyLength = fixedpoint.Abs(one.Close - one.Open) + ) + if direction == Bullish { + var ( + upperShadowLength = fixedpoint.Abs(one.High - one.Close).Float64() + lowerShadowLength = fixedpoint.Abs(one.Open - one.Low).Float64() + isSpinningTop = bodyLength.Float64() < upperShadowLength && + bodyLength.Float64() < lowerShadowLength + ) + + if isSpinningTop { + output = Bull + } + } else { + var ( + upperShadowLength = fixedpoint.Abs(one.High - one.Open).Float64() + lowerShadowLength = fixedpoint.Abs(one.High - one.Low).Float64() + isBearishSpinningTop = bodyLength.Float64() < upperShadowLength && + bodyLength.Float64() < lowerShadowLength + ) + if isBearishSpinningTop { + output = Bear + } + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *SpinningTopStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/spinning_top_test.go b/pkg/indicator/v2/spinning_top_test.go new file mode 100644 index 0000000000..6abb1bee3d --- /dev/null +++ b/pkg/indicator/v2/spinning_top_test.go @@ -0,0 +1,39 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestSpinningTop(t *testing.T) { + ts := []types.KLine{ + {Open: n(20.50), Low: n(20.23), High: n(20.87), Close: n(20.62)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := SpinningTop(kLines, Bearish) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestSpinningTop Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(20.62), Low: n(20.34), High: n(20.75), Close: n(20.50)}, + } + ind = SpinningTop(kLines, Bullish) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestSpinningTop Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/squared_average.go b/pkg/indicator/v2/squared_average.go new file mode 100644 index 0000000000..e3625f4d13 --- /dev/null +++ b/pkg/indicator/v2/squared_average.go @@ -0,0 +1,37 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type SquaredAverageStream struct { + *types.Float64Series + max *MaxValueStream + window int +} + +func SquaredAverage(source types.Float64Source, window int) *SquaredAverageStream { + s := &SquaredAverageStream{ + Float64Series: types.NewFloat64Series(), + max: MaxValue(source, window), + window: window, + } + + s.Bind(source, s) + + return s +} + +func (s *SquaredAverageStream) Calculate(v float64) float64 { + var ( + max = s.max.Last(0) + percentageDrawdown = (v - max) / max * 100 + squaredAverage = percentageDrawdown * percentageDrawdown + ) + + return squaredAverage +} + +func (s *SquaredAverageStream) Truncate() { + s.Slice = s.Slice.Truncate(s.window) +} diff --git a/pkg/indicator/v2/stoch.go b/pkg/indicator/v2/stoch.go index fe0d300867..ab4d4d122b 100644 --- a/pkg/indicator/v2/stoch.go +++ b/pkg/indicator/v2/stoch.go @@ -2,6 +2,7 @@ package indicatorv2 import ( "github.com/c9s/bbgo/pkg/datatype/floats" + "github.com/c9s/bbgo/pkg/types" ) diff --git a/pkg/indicator/v2/stoch_test.go b/pkg/indicator/v2/stoch_test.go index 73a3becca2..06792d78f5 100644 --- a/pkg/indicator/v2/stoch_test.go +++ b/pkg/indicator/v2/stoch_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/c9s/bbgo/pkg/fixedpoint" + "github.com/c9s/bbgo/pkg/types" ) diff --git a/pkg/indicator/v2/testdata/BTCUSDT-1m-2022-05-06.csv b/pkg/indicator/v2/testdata/BTCUSDT-1m-2022-05-06.csv new file mode 100644 index 0000000000..903858f076 --- /dev/null +++ b/pkg/indicator/v2/testdata/BTCUSDT-1m-2022-05-06.csv @@ -0,0 +1,1440 @@ +1651795200000,36533.70,36540.00,36501.00,36505.20,264.779,1651795259999,9670700.33840,3057,71.011,2593768.86330,0 +1651795260000,36506.30,36523.10,36492.30,36522.70,180.741,1651795319999,6598288.01340,2214,70.811,2585241.60220,0 +1651795320000,36522.70,36559.10,36518.90,36549.60,280.910,1651795379999,10263878.29160,2898,155.711,5689249.26850,0 +1651795380000,36549.90,36550.00,36490.00,36534.40,235.291,1651795439999,8591157.31110,2690,78.925,2881502.53680,0 +1651795440000,36534.40,36577.50,36534.40,36574.80,218.490,1651795499999,7988553.23400,2184,133.092,4866125.50710,0 +1651795500000,36574.90,36679.30,36561.40,36611.60,1180.452,1651795559999,43233700.14416,8720,852.525,31228536.48026,0 +1651795560000,36611.60,36614.60,36588.20,36612.70,252.435,1651795619999,9240546.27360,2494,104.381,3821126.58030,0 +1651795620000,36612.80,36647.10,36586.10,36594.50,361.987,1651795679999,13254573.37270,3565,220.110,8060195.17170,0 +1651795680000,36594.60,36598.10,36543.00,36566.60,236.064,1651795739999,8631772.05423,2650,66.766,2441168.29810,0 +1651795740000,36565.90,36565.90,36525.90,36530.80,129.389,1651795799999,4728306.04240,1697,45.836,1674990.33390,0 +1651795800000,36530.90,36544.80,36502.00,36502.60,196.733,1651795859999,7186350.43160,2201,66.478,2428519.05280,0 +1651795860000,36502.60,36542.60,36502.00,36536.00,170.532,1651795919999,6228765.93180,1868,100.253,3661624.33820,0 +1651795920000,36535.90,36544.90,36501.00,36502.10,144.590,1651795979999,5281843.54940,1853,66.928,2445167.78170,0 +1651795980000,36502.10,36508.20,36481.40,36486.20,194.894,1651796039999,7111668.26234,2557,56.351,2056283.27610,0 +1651796040000,36486.10,36488.00,36456.50,36468.90,278.855,1651796099999,10170352.26920,2866,91.712,3344833.82150,0 +1651796100000,36469.00,36498.30,36452.00,36494.00,205.859,1651796159999,7508030.44498,2539,74.080,2701853.99640,0 +1651796160000,36494.90,36499.60,36418.80,36424.30,483.092,1651796219999,17606740.32775,3542,95.065,3464379.92190,0 +1651796220000,36424.30,36455.00,36417.90,36455.00,219.514,1651796279999,7997504.99510,2220,84.997,3097016.83090,0 +1651796280000,36454.90,36454.90,36428.20,36438.80,133.064,1651796339999,4848458.28430,1249,74.445,2712369.98730,0 +1651796340000,36438.80,36488.60,36436.90,36457.10,376.722,1651796399999,13735227.71760,2589,227.732,8302818.25310,0 +1651796400000,36457.10,36464.00,36445.00,36445.90,149.496,1651796459999,5449391.89170,1175,77.283,2817065.22480,0 +1651796460000,36445.80,36475.40,36445.80,36475.40,108.687,1651796519999,3962204.94830,874,77.699,2832552.99970,0 +1651796520000,36475.40,36480.30,36461.70,36472.70,87.026,1651796579999,3174101.78460,951,49.218,1795082.06140,0 +1651796580000,36472.60,36483.70,36461.70,36478.70,96.456,1651796639999,3518032.27920,989,61.587,2246217.65980,0 +1651796640000,36478.80,36495.30,36473.60,36482.30,85.992,1651796699999,3137374.46600,1115,61.956,2260457.10180,0 +1651796700000,36482.40,36486.40,36462.50,36476.50,74.050,1651796759999,2700987.22470,888,31.387,1144836.66790,0 +1651796760000,36476.50,36479.80,36465.40,36479.70,46.815,1651796819999,1707348.69810,684,25.715,937841.92720,0 +1651796820000,36479.80,36479.80,36464.20,36478.60,65.664,1651796879999,2394912.08810,707,13.269,483958.81900,0 +1651796880000,36478.60,36498.80,36475.80,36498.80,79.499,1651796939999,2900676.11680,803,62.808,2291686.49300,0 +1651796940000,36498.80,36499.80,36481.80,36482.00,56.618,1651796999999,2065888.34370,846,24.081,878660.14630,0 +1651797000000,36481.30,36481.30,36439.90,36445.30,135.685,1651797059999,4947441.82440,1564,45.904,1673734.94190,0 +1651797060000,36445.80,36445.90,36383.20,36400.60,433.285,1651797119999,15773318.26744,3938,83.709,3047007.74480,0 +1651797120000,36400.70,36424.50,36400.60,36406.60,143.237,1651797179999,5215141.71090,1520,59.665,2172272.79150,0 +1651797180000,36406.60,36416.10,36393.90,36416.00,92.201,1651797239999,3356784.32980,1074,24.218,881762.32920,0 +1651797240000,36416.10,36418.40,36394.50,36412.40,108.663,1651797299999,3956196.41830,1292,38.120,1387776.00970,0 +1651797300000,36412.50,36418.80,36408.10,36418.80,67.796,1651797359999,2468624.73700,796,35.281,1284691.47910,0 +1651797360000,36418.80,36419.10,36395.20,36398.60,101.303,1651797419999,3688322.49200,988,24.971,909296.11060,0 +1651797420000,36398.60,36417.60,36398.60,36398.90,139.441,1651797479999,5076907.14100,1391,45.262,1647963.58790,0 +1651797480000,36398.80,36398.90,36380.50,36380.50,143.699,1651797539999,5229193.75480,1312,56.849,2068732.10180,0 +1651797540000,36380.60,36389.60,36363.80,36376.60,333.643,1651797599999,12136309.65340,2233,100.031,3638542.00880,0 +1651797600000,36376.60,36378.50,36360.70,36378.40,249.297,1651797659999,9067201.22970,1656,129.592,4713570.41420,0 +1651797660000,36378.50,36394.10,36378.40,36390.10,63.818,1651797719999,2322207.72400,915,44.050,1602907.20930,0 +1651797720000,36390.10,36407.90,36379.50,36388.40,130.506,1651797779999,4749650.64330,1033,98.201,3573964.16720,0 +1651797780000,36388.40,36400.00,36385.70,36400.00,53.061,1651797839999,1930958.06780,628,35.802,1302897.52000,0 +1651797840000,36400.00,36400.00,36382.30,36389.00,48.983,1651797899999,1782772.21580,769,27.983,1018477.55470,0 +1651797900000,36388.90,36389.00,36351.50,36356.30,170.158,1651797959999,6187308.51365,1494,71.221,2589626.15340,0 +1651797960000,36356.20,36361.30,36340.70,36359.20,195.326,1651798019999,7100334.52990,1622,108.045,3927658.33770,0 +1651798020000,36359.20,36382.80,36355.90,36381.90,117.594,1651798079999,4276955.11370,1075,80.318,2921232.21010,0 +1651798080000,36381.90,36381.90,36348.60,36354.00,169.616,1651798139999,6167483.74750,1175,85.296,3101564.11340,0 +1651798140000,36354.10,36364.00,36352.00,36352.00,37.685,1651798199999,1370091.31320,683,14.911,542116.96270,0 +1651798200000,36352.00,36362.70,36351.20,36357.30,88.140,1651798259999,3204551.03610,780,69.818,2538388.30020,0 +1651798260000,36357.30,36363.60,36343.00,36349.40,115.286,1651798319999,4190832.12120,1134,53.529,1945901.69410,0 +1651798320000,36349.40,36349.50,36282.00,36300.40,466.357,1651798379999,16930016.16279,4867,160.017,5808420.36660,0 +1651798380000,36300.30,36314.40,36282.00,36307.30,212.435,1651798439999,7710996.56950,1905,114.704,4163542.85360,0 +1651798440000,36307.40,36317.10,36268.00,36281.60,198.972,1651798499999,7220735.36590,2260,96.473,3501760.31110,0 +1651798500000,36281.70,36310.10,36274.60,36296.70,161.226,1651798559999,5851642.92180,1702,90.985,3302286.56950,0 +1651798560000,36296.70,36334.30,36296.70,36321.90,150.261,1651798619999,5457394.39220,1594,83.146,3019754.11620,0 +1651798620000,36321.90,36345.80,36321.90,36329.10,132.897,1651798679999,4828732.67990,1353,82.831,3009695.85570,0 +1651798680000,36329.20,36358.80,36328.00,36356.00,102.925,1651798739999,3740465.58510,1369,77.196,2805387.21430,0 +1651798740000,36356.00,36377.10,36349.90,36351.50,160.102,1651798799999,5822564.87880,1662,86.983,3163246.08060,0 +1651798800000,36351.50,36383.00,36348.00,36369.20,212.140,1651798859999,7714816.98010,1831,114.774,4174111.82980,0 +1651798860000,36369.20,36376.30,36367.90,36370.00,95.248,1651798919999,3464200.08070,906,65.901,2396858.83460,0 +1651798920000,36370.00,36383.30,36310.00,36313.10,178.433,1651798979999,6485235.42570,1794,68.796,2500480.92740,0 +1651798980000,36313.00,36340.00,36302.00,36339.90,137.832,1651799039999,5006424.32840,1626,82.235,2986720.38710,0 +1651799040000,36339.90,36347.50,36330.00,36339.60,89.692,1651799099999,3259345.69880,946,41.533,1509292.49850,0 +1651799100000,36339.60,36339.60,36300.10,36307.20,82.471,1651799159999,2995105.24240,1072,29.223,1061255.54760,0 +1651799160000,36307.30,36333.00,36285.00,36330.20,104.928,1651799219999,3809482.68420,1425,54.716,1986491.93320,0 +1651799220000,36330.20,36344.70,36324.70,36329.70,105.905,1651799279999,3848199.18090,1171,66.736,2424851.34300,0 +1651799280000,36329.80,36329.80,36301.90,36304.90,68.483,1651799339999,2486955.84840,873,22.275,808855.25140,0 +1651799340000,36304.90,36311.70,36296.60,36303.70,100.212,1651799399999,3638075.29780,948,77.299,2806282.65780,0 +1651799400000,36303.80,36320.90,36299.30,36308.70,61.795,1651799459999,2243691.03970,1002,31.736,1152225.76660,0 +1651799460000,36308.60,36318.60,36308.60,36312.00,50.410,1651799519999,1830494.53520,830,27.731,1006950.75950,0 +1651799520000,36312.00,36314.10,36303.50,36307.00,57.545,1651799579999,2089484.27060,872,23.562,855544.25040,0 +1651799580000,36307.00,36307.10,36281.00,36281.00,127.081,1651799639999,4612157.38410,1099,22.310,809630.51380,0 +1651799640000,36281.10,36288.10,36249.70,36255.90,267.356,1651799699999,9696782.30389,2585,90.061,3266385.35380,0 +1651799700000,36255.90,36277.00,36248.70,36263.90,211.770,1651799759999,7679197.72070,2104,117.985,4278436.50450,0 +1651799760000,36263.90,36308.00,36262.50,36301.90,110.535,1651799819999,4010364.26910,1191,84.119,3051872.67780,0 +1651799820000,36302.00,36343.90,36301.90,36322.90,139.926,1651799879999,5082858.83280,1865,80.179,2912339.99850,0 +1651799880000,36322.90,36323.00,36300.00,36302.10,72.012,1651799939999,2614947.89340,916,32.262,1171529.10500,0 +1651799940000,36302.10,36302.10,36276.50,36276.50,61.924,1651799999999,2247380.76230,1039,24.090,874283.76660,0 +1651800000000,36276.60,36290.10,36267.70,36269.90,89.058,1651800059999,3230726.57900,1230,41.114,1491489.50170,0 +1651800060000,36270.00,36289.50,36220.30,36230.60,329.222,1651800119999,11934465.45660,3421,92.281,3345685.96640,0 +1651800120000,36230.50,36257.10,36217.00,36224.70,180.799,1651800179999,6551016.09890,2379,75.490,2735280.23000,0 +1651800180000,36224.70,36224.70,36205.20,36221.00,184.256,1651800239999,6672835.36700,2019,83.897,3038496.28540,0 +1651800240000,36221.10,36224.70,36131.40,36144.30,672.904,1651800299999,24340492.73441,4653,113.289,4100130.37900,0 +1651800300000,36144.30,36218.80,36128.80,36211.90,531.095,1651800359999,19212066.12150,4623,259.757,9397916.03530,0 +1651800360000,36211.80,36253.50,36191.70,36199.00,268.792,1651800419999,9736346.15606,2727,183.218,6636713.19356,0 +1651800420000,36198.90,36263.00,36198.90,36254.30,254.899,1651800479999,9234678.06530,2090,187.846,6804930.14250,0 +1651800480000,36254.40,36263.30,36235.90,36242.20,176.673,1651800539999,6404361.00150,1381,51.371,1862474.59930,0 +1651800540000,36242.20,36266.00,36225.30,36236.50,130.639,1651800599999,4734958.53700,1354,89.859,3256893.85040,0 +1651800600000,36236.50,36249.80,36206.60,36248.60,179.159,1651800659999,6491166.30960,1518,94.573,3426744.42270,0 +1651800660000,36248.50,36257.60,36242.60,36256.50,58.693,1651800719999,2127658.76460,1130,33.871,1227860.93190,0 +1651800720000,36256.50,36266.00,36249.10,36249.20,72.034,1651800779999,2611764.16000,957,38.843,1408356.18270,0 +1651800780000,36249.10,36249.20,36206.00,36215.00,106.001,1651800839999,3839574.22070,1412,33.318,1206772.16930,0 +1651800840000,36215.00,36215.00,36184.60,36191.90,118.649,1651800899999,4295218.39470,1371,34.700,1256258.80810,0 +1651800900000,36191.80,36213.80,36191.30,36212.10,82.786,1651800959999,2997105.99350,1130,39.094,1415302.46440,0 +1651800960000,36212.20,36213.90,36194.40,36213.00,110.766,1651801019999,4010467.05460,1221,39.725,1438332.91640,0 +1651801020000,36213.00,36232.80,36210.20,36218.80,63.085,1651801079999,2285165.79400,986,24.693,894455.06630,0 +1651801080000,36218.70,36227.00,36213.10,36223.80,114.877,1651801139999,4160931.30750,1014,78.703,2850701.06390,0 +1651801140000,36223.70,36227.30,36218.30,36220.90,59.258,1651801199999,2146505.47630,818,30.348,1099255.77110,0 +1651801200000,36220.90,36246.50,36220.00,36243.70,88.571,1651801259999,3209524.68580,1052,79.095,2866145.95830,0 +1651801260000,36243.70,36269.00,36243.60,36256.90,165.340,1651801319999,5995175.41810,1456,95.706,3470163.76500,0 +1651801320000,36257.00,36275.90,36255.20,36269.70,175.535,1651801379999,6365773.26710,1322,100.856,3657660.84300,0 +1651801380000,36269.70,36271.00,36241.00,36244.80,110.364,1651801439999,4001044.98890,1102,38.881,1409460.56940,0 +1651801440000,36244.70,36248.50,36222.00,36244.20,95.926,1651801499999,3475877.92450,1067,50.878,1843604.39660,0 +1651801500000,36244.20,36244.20,36226.80,36226.80,39.434,1651801559999,1428833.10250,777,22.863,828380.72770,0 +1651801560000,36226.00,36237.10,36217.00,36236.80,91.337,1651801619999,3308504.67400,875,41.425,1500585.68540,0 +1651801620000,36236.70,36254.90,36236.70,36246.90,74.877,1651801679999,2714063.04460,1050,48.894,1772229.81450,0 +1651801680000,36246.80,36273.60,36246.80,36256.80,124.189,1651801739999,4503054.13240,1329,75.585,2740696.86630,0 +1651801740000,36257.70,36262.40,36211.70,36219.20,256.616,1651801799999,9296070.16680,1833,43.114,1562009.04340,0 +1651801800000,36219.30,36219.30,36193.90,36194.00,125.352,1651801859999,4538976.86280,1291,61.905,2241576.19410,0 +1651801860000,36193.90,36204.00,36192.20,36198.00,63.088,1651801919999,2283688.15940,782,30.265,1095508.91230,0 +1651801920000,36198.00,36208.10,36190.10,36205.10,59.293,1651801979999,2146347.40760,846,27.529,996534.70250,0 +1651801980000,36205.10,36223.20,36203.40,36223.20,54.865,1651802039999,1986782.43680,825,31.405,1137264.45330,0 +1651802040000,36223.20,36248.70,36217.10,36240.60,88.056,1651802099999,3190341.67590,851,43.296,1568589.45210,0 +1651802100000,36240.50,36253.40,36240.50,36253.30,50.685,1651802159999,1837168.33650,612,29.192,1058111.46500,0 +1651802160000,36253.40,36263.90,36245.50,36263.90,95.049,1651802219999,3445984.66560,892,49.955,1811132.38910,0 +1651802220000,36263.90,36276.00,36261.00,36275.90,232.561,1651802279999,8435177.96550,1286,114.259,4144296.63460,0 +1651802280000,36275.90,36285.70,36275.90,36285.70,92.406,1651802339999,3352568.85010,892,66.040,2395979.86540,0 +1651802340000,36285.60,36296.40,36283.50,36293.00,100.288,1651802399999,3639285.28140,985,57.736,2095171.92680,0 +1651802400000,36293.00,36297.70,36292.00,36292.60,134.336,1651802459999,4875547.81250,835,78.003,2831039.45190,0 +1651802460000,36292.50,36316.10,36290.80,36316.10,155.536,1651802519999,5645585.87519,1346,103.651,3762375.81899,0 +1651802520000,36316.10,36329.00,36296.20,36303.70,269.417,1651802579999,9784558.01420,2113,126.101,4579934.57530,0 +1651802580000,36303.70,36313.30,36294.60,36313.30,90.393,1651802639999,3281541.42980,939,43.293,1571700.77150,0 +1651802640000,36313.20,36326.80,36313.20,36317.10,224.759,1651802699999,8163367.84760,1360,120.476,4375567.29900,0 +1651802700000,36317.10,36362.00,36317.10,36348.90,320.718,1651802759999,11656896.15041,2370,213.009,7742161.03501,0 +1651802760000,36348.90,36350.00,36345.00,36345.10,65.178,1651802819999,2369019.10670,1141,33.998,1235701.51360,0 +1651802820000,36345.10,36365.90,36345.00,36362.10,95.984,1651802879999,3489790.71190,1225,68.404,2487037.82800,0 +1651802880000,36362.00,36362.10,36341.20,36361.10,159.341,1651802939999,5792545.07540,1008,44.951,1634133.81570,0 +1651802940000,36361.10,36386.40,36359.20,36379.30,217.826,1651802999999,7922852.22080,2031,94.194,3426287.28770,0 +1651803000000,36379.30,36411.50,36370.30,36409.10,352.490,1651803059999,12826655.51742,3142,180.732,6577203.97902,0 +1651803060000,36409.20,36413.30,36377.70,36408.80,240.542,1651803119999,8755188.64690,2490,73.607,2679455.54910,0 +1651803120000,36408.70,36413.00,36387.50,36407.10,135.680,1651803179999,4938901.26860,1744,87.232,3175474.57130,0 +1651803180000,36407.10,36428.50,36407.10,36418.90,180.713,1651803239999,6581339.83792,1839,130.773,4762587.52992,0 +1651803240000,36418.90,36439.20,36418.90,36433.40,122.685,1651803299999,4469769.05266,1258,66.323,2416243.82686,0 +1651803300000,36433.50,36445.00,36419.90,36443.80,219.117,1651803359999,7983510.69692,2640,94.563,3445458.35322,0 +1651803360000,36443.80,36499.20,36438.80,36494.20,425.714,1651803419999,15524857.30971,3550,311.144,11347337.07461,0 +1651803420000,36494.30,36515.20,36467.10,36494.40,359.740,1651803479999,13126778.31435,3580,126.866,4630215.84725,0 +1651803480000,36494.50,36507.90,36474.30,36474.90,333.269,1651803539999,12162009.79070,2677,135.139,4931972.34620,0 +1651803540000,36474.30,36486.10,36450.00,36450.10,183.873,1651803599999,6705601.19480,2388,41.212,1503082.08870,0 +1651803600000,36450.10,36460.40,36423.50,36460.40,221.455,1651803659999,8068992.00370,2525,103.066,3755478.37970,0 +1651803660000,36460.40,36464.70,36438.80,36439.80,63.607,1651803719999,2318748.12310,1023,22.471,819214.73370,0 +1651803720000,36439.70,36496.90,36439.70,36496.80,136.025,1651803779999,4959997.26230,1678,99.838,3640536.12800,0 +1651803780000,36496.90,36496.90,36451.60,36464.80,115.505,1651803839999,4212100.20650,1617,22.733,829040.16470,0 +1651803840000,36464.70,36496.70,36450.80,36480.50,190.813,1651803899999,6960635.33650,2343,62.510,2280413.39020,0 +1651803900000,36480.50,36485.90,36474.10,36481.00,107.528,1651803959999,3922765.68130,1155,56.193,2049981.84280,0 +1651803960000,36481.00,36481.00,36456.90,36474.30,150.678,1651804019999,5495154.48660,1653,52.837,1926829.14480,0 +1651804020000,36474.30,36494.30,36464.00,36494.30,98.011,1651804079999,3575084.19100,1088,75.070,2738412.19140,0 +1651804080000,36494.30,36520.90,36491.40,36505.60,287.629,1651804139999,10500100.77670,2442,182.008,6643765.11580,0 +1651804140000,36504.60,36529.90,36501.50,36524.60,184.312,1651804199999,6730638.77404,1997,84.333,3079816.07224,0 +1651804200000,36524.50,36528.90,36495.70,36495.80,108.193,1651804259999,3950233.37620,1586,42.750,1560933.22270,0 +1651804260000,36495.70,36505.00,36455.90,36475.60,351.842,1651804319999,12834329.70360,2904,66.204,2415234.55950,0 +1651804320000,36475.60,36479.10,36455.90,36455.90,115.193,1651804379999,4200992.63960,1254,31.162,1136508.81600,0 +1651804380000,36455.90,36480.00,36453.00,36469.70,196.254,1651804439999,7156333.97900,1280,145.584,5308480.19000,0 +1651804440000,36469.70,36475.00,36460.00,36474.90,63.627,1651804499999,2320373.13740,681,27.116,988905.34630,0 +1651804500000,36475.00,36479.90,36466.00,36469.00,78.625,1651804559999,2867788.82030,924,21.462,782786.77050,0 +1651804560000,36469.10,36469.10,36460.00,36460.50,64.746,1651804619999,2360947.58440,648,16.249,592488.61320,0 +1651804620000,36460.60,36473.70,36450.00,36464.80,276.074,1651804679999,10065277.80870,1466,71.188,2595502.82560,0 +1651804680000,36464.80,36470.70,36461.90,36463.80,59.683,1651804739999,2176460.69660,568,27.995,1020888.16360,0 +1651804740000,36463.80,36463.90,36451.90,36463.60,46.344,1651804799999,1689616.30610,648,11.551,421118.69030,0 +1651804800000,36463.50,36463.60,36441.20,36444.20,156.292,1651804859999,5696943.68830,1177,27.227,992452.06470,0 +1651804860000,36444.20,36467.00,36441.20,36467.00,78.095,1651804919999,2846767.29010,812,52.790,1924425.72380,0 +1651804920000,36466.90,36467.00,36457.50,36463.90,85.604,1651804979999,3121411.18010,731,43.074,1570550.00580,0 +1651804980000,36463.80,36466.00,36457.80,36461.80,44.081,1651805039999,1607368.91490,484,23.137,843659.46890,0 +1651805040000,36461.80,36479.10,36461.00,36478.30,76.226,1651805099999,2779965.77400,709,44.358,1617788.93130,0 +1651805100000,36478.20,36479.60,36467.10,36477.90,50.141,1651805159999,1828749.62960,614,12.646,461229.15400,0 +1651805160000,36478.40,36481.40,36464.90,36470.90,68.530,1651805219999,2499716.88650,719,20.826,759647.29400,0 +1651805220000,36470.90,36471.00,36456.80,36456.80,38.089,1651805279999,1388959.21940,558,8.040,293198.14770,0 +1651805280000,36456.80,36468.00,36450.40,36454.00,57.361,1651805339999,2091365.45880,779,21.890,798059.50180,0 +1651805340000,36453.90,36454.00,36446.00,36450.80,63.397,1651805399999,2310779.98840,651,36.482,1329733.32560,0 +1651805400000,36450.90,36461.70,36445.50,36461.70,74.985,1651805459999,2733347.98760,877,43.225,1575647.93620,0 +1651805460000,36461.60,36477.80,36461.60,36477.60,75.511,1651805519999,2753846.85450,624,49.152,1792631.79850,0 +1651805520000,36477.60,36482.30,36473.50,36480.20,63.060,1651805579999,2300340.78840,688,35.982,1312567.26840,0 +1651805580000,36480.30,36480.30,36437.90,36441.30,149.648,1651805639999,5457121.29650,1186,26.953,982799.36920,0 +1651805640000,36441.30,36445.40,36417.90,36441.00,302.826,1651805699999,11032234.00680,1648,136.411,4969384.13830,0 +1651805700000,36441.00,36449.20,36401.00,36412.20,224.999,1651805759999,8194438.50660,1780,48.267,1758107.67380,0 +1651805760000,36412.20,36420.10,36405.70,36413.50,88.098,1651805819999,3207939.16450,1079,51.083,1860112.55750,0 +1651805820000,36413.80,36417.70,36409.40,36412.40,66.601,1651805879999,2425154.79050,668,16.714,608617.35950,0 +1651805880000,36412.40,36426.90,36408.70,36410.00,67.922,1651805939999,2473510.67420,817,26.267,956550.02370,0 +1651805940000,36410.10,36415.90,36396.50,36415.90,100.542,1651805999999,3660196.90360,980,25.788,938886.33970,0 +1651806000000,36415.80,36426.90,36415.80,36417.30,91.949,1651806059999,3349018.44910,855,30.687,1117637.79870,0 +1651806060000,36416.10,36421.00,36412.80,36412.90,69.211,1651806119999,2520330.58330,653,34.736,1264897.91040,0 +1651806120000,36413.00,36429.90,36412.90,36429.90,40.931,1651806179999,1490555.25980,567,30.005,1092683.04540,0 +1651806180000,36429.90,36453.40,36429.80,36452.40,157.470,1651806239999,5739269.16240,1237,87.155,3176461.45990,0 +1651806240000,36452.30,36452.30,36427.50,36433.00,68.669,1651806299999,2502026.38500,897,38.201,1391766.54250,0 +1651806300000,36433.00,36433.10,36422.40,36425.30,69.334,1651806359999,2525748.70550,786,35.670,1299390.21150,0 +1651806360000,36425.30,36438.50,36425.30,36437.90,55.935,1651806419999,2037820.74840,797,41.718,1519841.36520,0 +1651806420000,36437.90,36452.00,36437.90,36440.10,51.743,1651806479999,1885887.12020,768,36.048,1313815.98200,0 +1651806480000,36440.00,36450.40,36440.00,36444.80,52.522,1651806539999,1914176.63380,639,35.494,1293557.40880,0 +1651806540000,36444.80,36448.70,36427.80,36433.30,23.821,1651806599999,867998.81420,688,12.184,443942.05660,0 +1651806600000,36433.40,36433.40,36388.40,36392.60,187.899,1651806659999,6840825.29290,1571,56.172,2045216.24740,0 +1651806660000,36392.80,36399.80,36367.00,36367.10,221.185,1651806719999,8046695.98850,2257,68.486,2491695.65480,0 +1651806720000,36367.10,36408.80,36367.10,36398.30,105.069,1651806779999,3823020.71360,1307,53.680,1952964.92800,0 +1651806780000,36398.30,36408.00,36398.30,36407.40,38.354,1651806839999,1396252.39630,672,14.481,527170.07250,0 +1651806840000,36407.40,36416.00,36398.80,36402.70,48.172,1651806899999,1753693.18440,656,29.832,1086022.22300,0 +1651806900000,36402.80,36402.80,36379.40,36387.30,61.032,1651806959999,2220966.73010,1009,18.652,678736.84760,0 +1651806960000,36388.20,36399.20,36388.10,36390.80,62.299,1651807019999,2267383.03640,803,27.593,1004184.67630,0 +1651807020000,36390.80,36390.90,36362.70,36366.40,119.476,1651807079999,4346048.51540,1384,44.096,1603845.09360,0 +1651807080000,36366.50,36382.50,36366.40,36382.50,76.207,1651807139999,2771734.35520,955,54.010,1964417.16630,0 +1651807140000,36382.50,36400.60,36382.40,36396.30,49.620,1651807199999,1805859.77040,727,29.986,1091311.45670,0 +1651807200000,36396.40,36396.80,36371.80,36381.80,48.593,1651807259999,1768071.14640,721,11.711,426087.09760,0 +1651807260000,36381.70,36382.20,36375.50,36377.20,25.523,1651807319999,928550.52690,527,6.865,249757.64950,0 +1651807320000,36377.20,36377.30,36360.30,36360.30,36.364,1651807379999,1322538.46000,664,6.871,249899.02360,0 +1651807380000,36360.30,36382.10,36360.30,36378.90,71.449,1651807439999,2598425.44580,975,22.912,833249.47450,0 +1651807440000,36378.90,36381.30,36367.30,36379.30,59.554,1651807499999,2166302.39080,801,25.449,925701.56740,0 +1651807500000,36379.40,36379.40,36351.10,36359.90,113.911,1651807559999,4141960.58645,1144,29.111,1058514.64910,0 +1651807560000,36360.00,36360.00,36345.00,36357.30,83.656,1651807619999,3040903.25710,1303,26.644,968521.89560,0 +1651807620000,36357.40,36357.40,36348.90,36353.70,44.869,1651807679999,1631111.78530,659,18.213,662081.54430,0 +1651807680000,36353.70,36366.90,36353.70,36364.70,39.684,1651807739999,1443013.46780,602,27.089,985011.54530,0 +1651807740000,36364.80,36365.90,36360.70,36363.50,35.885,1651807799999,1304940.35880,462,19.256,700238.17270,0 +1651807800000,36363.40,36372.90,36345.90,36372.90,91.328,1651807859999,3320469.51930,1040,57.715,2098467.63840,0 +1651807860000,36372.90,36401.80,36372.80,36400.40,247.654,1651807919999,9012506.59790,1803,206.928,7530457.82700,0 +1651807920000,36400.40,36406.70,36396.10,36396.20,71.965,1651807979999,2619721.32250,980,36.542,1330222.40390,0 +1651807980000,36396.20,36411.80,36396.10,36400.90,61.964,1651808039999,2255717.39130,818,36.328,1322424.31340,0 +1651808040000,36400.80,36406.10,36396.20,36406.10,59.434,1651808099999,2163420.47130,617,37.658,1370793.35090,0 +1651808100000,36406.00,36423.10,36406.00,36413.00,106.970,1651808159999,3895352.81530,1207,83.804,3051744.45770,0 +1651808160000,36413.00,36437.70,36413.00,36416.70,131.748,1651808219999,4799070.30890,1254,89.957,3276809.57570,0 +1651808220000,36416.80,36416.80,36400.00,36408.00,92.966,1651808279999,3384683.28880,848,27.021,983729.94340,0 +1651808280000,36407.90,36408.00,36404.80,36405.00,46.900,1651808339999,1707448.40280,532,36.556,1330859.60590,0 +1651808340000,36404.90,36410.20,36404.80,36406.10,30.001,1651808399999,1092212.11690,511,21.084,767584.57770,0 +1651808400000,36406.10,36406.20,36400.20,36400.90,31.122,1651808459999,1132931.47530,490,14.530,528939.84730,0 +1651808460000,36400.90,36400.90,36382.00,36395.00,76.950,1651808519999,2800412.97800,858,24.518,892261.24700,0 +1651808520000,36395.00,36395.00,36352.00,36370.70,132.977,1651808579999,4836220.15710,1302,40.275,1464624.29190,0 +1651808580000,36370.60,36370.70,36336.60,36353.00,173.247,1651808639999,6297106.75996,1481,45.415,1650765.76550,0 +1651808640000,36353.00,36353.10,36343.10,36348.10,68.123,1651808699999,2476026.84420,1003,27.345,993889.05460,0 +1651808700000,36348.20,36359.40,36347.40,36354.90,93.789,1651808759999,3409399.38420,917,64.667,2350733.79850,0 +1651808760000,36355.00,36395.00,36354.90,36394.90,70.706,1651808819999,2571966.69250,1100,48.963,1781022.15390,0 +1651808820000,36395.00,36395.70,36371.00,36377.00,77.079,1651808879999,2804417.74050,789,22.526,819415.35500,0 +1651808880000,36376.90,36390.00,36376.90,36390.00,25.832,1651808939999,939851.93430,442,21.780,792410.51890,0 +1651808940000,36389.90,36394.00,36385.00,36394.00,78.167,1651808999999,2844413.52490,553,47.384,1724238.56700,0 +1651809000000,36395.60,36411.70,36395.60,36400.90,134.660,1651809059999,4902168.80410,1133,66.284,2412957.50360,0 +1651809060000,36400.80,36406.40,36397.00,36402.30,100.836,1651809119999,3670603.01920,725,68.925,2508959.61330,0 +1651809120000,36402.60,36428.50,36402.60,36424.80,94.918,1651809179999,3457121.83630,1176,62.606,2280162.90080,0 +1651809180000,36423.70,36434.20,36419.60,36430.60,78.490,1651809239999,2859329.14070,905,56.485,2057694.81870,0 +1651809240000,36430.70,36430.90,36421.70,36424.00,40.518,1651809299999,1475891.24150,610,23.309,849044.11850,0 +1651809300000,36423.90,36432.70,36415.40,36418.00,84.898,1651809359999,3092404.26920,886,40.553,1477179.28450,0 +1651809360000,36418.00,36418.10,36361.20,36364.30,60.523,1651809419999,2202477.71210,933,11.379,414105.34730,0 +1651809420000,36364.30,36376.30,36364.10,36371.20,95.059,1651809479999,3457181.95130,922,63.903,2324080.94360,0 +1651809480000,36371.20,36382.80,36369.40,36382.20,97.743,1651809539999,3555453.03340,755,78.611,2859529.91570,0 +1651809540000,36382.20,36446.40,36382.20,36441.40,202.423,1651809599999,7372805.87640,1436,130.802,4763418.21820,0 +1651809600000,36442.50,36459.70,36437.70,36437.70,192.268,1651809659999,7008155.54411,1782,115.449,4208159.29701,0 +1651809660000,36437.70,36438.50,36406.40,36421.10,133.001,1651809719999,4844446.17380,1314,42.978,1565303.85330,0 +1651809720000,36421.00,36428.00,36421.00,36421.10,30.169,1651809779999,1098866.18900,457,16.619,605318.42450,0 +1651809780000,36421.20,36430.00,36420.00,36426.20,64.832,1651809839999,2361417.26970,640,17.319,630838.50740,0 +1651809840000,36426.10,36428.90,36413.80,36425.20,43.564,1651809899999,1586638.05810,729,29.188,1063037.32220,0 +1651809900000,36425.20,36428.80,36421.20,36421.20,36.984,1651809959999,1347122.82860,650,8.500,309610.60280,0 +1651809960000,36421.30,36421.30,36390.60,36390.60,88.827,1651810019999,3233500.13680,1073,42.133,1533599.49710,0 +1651810020000,36390.60,36390.70,36366.50,36368.70,79.669,1651810079999,2898294.81480,1005,36.310,1320878.55380,0 +1651810080000,36368.80,36397.00,36368.80,36385.40,118.638,1651810139999,4316447.45540,1116,73.876,2687923.59970,0 +1651810140000,36385.50,36390.30,36357.30,36369.50,86.821,1651810199999,3157554.51180,943,37.990,1381657.57130,0 +1651810200000,36369.60,36377.30,36361.30,36361.40,67.388,1651810259999,2450815.17180,898,23.944,870822.75420,0 +1651810260000,36361.30,36361.40,36325.90,36346.40,308.944,1651810319999,11227675.49925,2160,63.994,2325617.34540,0 +1651810320000,36346.40,36347.70,36324.50,36329.00,81.281,1651810379999,2953237.85900,1150,33.594,1220615.57550,0 +1651810380000,36329.00,36348.00,36323.90,36344.90,144.182,1651810439999,5238821.54950,1418,55.452,2014988.61040,0 +1651810440000,36345.00,36356.60,36312.40,36317.90,107.353,1651810499999,3900635.86784,1465,49.861,1811701.84630,0 +1651810500000,36317.90,36355.20,36311.80,36345.30,101.003,1651810559999,3670313.12600,1193,53.272,1935980.42220,0 +1651810560000,36345.20,36358.00,36336.40,36358.00,65.418,1651810619999,2378073.45400,931,38.624,1404058.26130,0 +1651810620000,36358.00,36368.00,36357.90,36359.00,59.113,1651810679999,2149487.32580,686,32.982,1199307.40680,0 +1651810680000,36359.10,36359.10,36331.00,36334.80,84.126,1651810739999,3057179.22140,868,14.827,538815.11720,0 +1651810740000,36334.80,36348.00,36259.80,36292.10,975.860,1651810799999,35422341.30717,4763,354.341,12860864.80620,0 +1651810800000,36292.00,36311.50,36288.80,36303.40,396.578,1651810859999,14395610.88660,2239,241.303,8759106.10940,0 +1651810860000,36303.40,36310.00,36299.00,36310.00,53.499,1651810919999,1942359.97850,612,35.242,1279555.50400,0 +1651810920000,36310.00,36310.00,36297.30,36299.90,33.091,1651810979999,1201345.66400,535,12.729,462128.09020,0 +1651810980000,36300.00,36321.00,36300.00,36321.00,55.838,1651811039999,2027553.39890,627,38.710,1405575.02880,0 +1651811040000,36321.00,36327.00,36311.50,36312.40,43.014,1651811099999,1562305.01070,614,21.503,781024.63670,0 +1651811100000,36312.40,36335.20,36312.40,36328.20,61.025,1651811159999,2216896.60930,710,44.479,1615775.03830,0 +1651811160000,36328.20,36328.20,36308.60,36318.00,69.464,1651811219999,2522685.44230,929,19.704,715598.49670,0 +1651811220000,36318.00,36318.60,36309.70,36309.70,39.510,1651811279999,1434823.74300,604,12.342,448226.78220,0 +1651811280000,36309.80,36325.00,36309.70,36315.10,46.530,1651811339999,1689783.55520,686,30.891,1121785.24150,0 +1651811340000,36315.00,36339.40,36315.00,36318.50,67.743,1651811399999,2461076.34130,825,44.607,1620634.96540,0 +1651811400000,36318.40,36332.60,36318.40,36331.00,37.288,1651811459999,1354571.66550,603,26.886,976688.19110,0 +1651811460000,36330.90,36334.10,36320.20,36323.40,56.425,1651811519999,2049610.35650,606,25.918,941440.15320,0 +1651811520000,36323.30,36334.70,36313.40,36313.40,69.543,1651811579999,2525959.10240,765,25.266,917716.71220,0 +1651811580000,36313.40,36328.00,36313.40,36328.00,24.042,1651811639999,873213.37910,377,16.125,585644.77650,0 +1651811640000,36328.00,36339.50,36327.90,36338.30,29.309,1651811699999,1064976.32070,480,18.422,669369.85070,0 +1651811700000,36338.30,36353.00,36338.20,36343.90,42.231,1651811759999,1534927.08640,712,25.871,940290.96580,0 +1651811760000,36343.90,36348.30,36322.40,36331.90,82.585,1651811819999,3000764.11380,889,38.401,1395332.76330,0 +1651811820000,36331.80,36337.70,36323.90,36325.80,22.804,1651811879999,828523.22030,499,13.911,505422.70930,0 +1651811880000,36325.90,36332.00,36321.10,36321.10,38.601,1651811939999,1402164.36600,568,16.320,592826.22120,0 +1651811940000,36321.10,36323.10,36301.70,36305.10,89.656,1651811999999,3255564.08900,924,16.778,609248.81810,0 +1651812000000,36305.10,36317.60,36302.30,36317.20,75.405,1651812059999,2738083.74110,1083,34.035,1235837.62590,0 +1651812060000,36317.10,36345.10,36317.10,36338.30,46.374,1651812119999,1685019.23980,586,35.638,1294899.73630,0 +1651812120000,36338.30,36338.30,36320.10,36328.90,33.835,1651812179999,1229258.50830,437,20.046,728260.18100,0 +1651812180000,36328.80,36337.40,36324.60,36334.40,40.409,1651812239999,1468119.26830,499,25.779,936611.43890,0 +1651812240000,36334.40,36340.00,36331.00,36340.00,33.290,1651812299999,1209588.49960,469,19.814,719944.12700,0 +1651812300000,36340.00,36365.90,36329.20,36356.90,74.920,1651812359999,2723440.61780,966,43.463,1580004.84910,0 +1651812360000,36356.90,36378.60,36356.80,36376.60,104.895,1651812419999,3815013.77660,930,89.291,3247562.47980,0 +1651812420000,36376.50,36376.90,36354.10,36365.30,67.322,1651812479999,2448174.13360,758,36.540,1328813.97360,0 +1651812480000,36365.20,36387.10,36365.20,36376.50,108.556,1651812539999,3949049.64460,1135,86.296,3139274.80990,0 +1651812540000,36376.50,36393.40,36376.40,36391.40,60.943,1651812599999,2217424.91190,689,45.260,1646854.15330,0 +1651812600000,36391.50,36397.70,36373.30,36373.40,87.568,1651812659999,3186384.04900,976,27.702,1008035.52520,0 +1651812660000,36373.40,36373.40,36356.90,36358.70,46.748,1651812719999,1700042.91320,663,14.396,523502.38790,0 +1651812720000,36358.70,36369.00,36350.00,36357.40,77.106,1651812779999,2803306.90130,840,23.541,855865.98870,0 +1651812780000,36357.30,36357.40,36339.00,36339.00,25.620,1651812839999,931203.08000,457,8.855,321827.37890,0 +1651812840000,36339.10,36339.10,36327.00,36327.20,52.352,1651812899999,1902005.56580,780,31.383,1140172.71740,0 +1651812900000,36327.20,36330.00,36313.00,36313.10,92.339,1651812959999,3354098.16860,806,18.407,668630.36210,0 +1651812960000,36313.00,36359.40,36311.50,36350.40,241.158,1651813019999,8761642.52560,1642,172.686,6274016.46810,0 +1651813020000,36350.30,36389.40,36350.30,36367.70,73.767,1651813079999,2683358.09290,842,46.949,1707849.41850,0 +1651813080000,36367.60,36384.30,36367.60,36373.30,72.202,1651813139999,2626522.00710,699,37.209,1353504.60720,0 +1651813140000,36373.40,36417.20,36367.60,36414.90,182.811,1651813199999,6653343.64670,1834,124.275,4523264.75010,0 +1651813200000,36414.80,36414.90,36392.60,36393.80,110.668,1651813259999,4028959.51190,1289,48.359,1760677.94110,0 +1651813260000,36393.80,36434.10,36391.10,36432.90,154.803,1651813319999,5637965.63450,1770,73.996,2695047.25680,0 +1651813320000,36433.90,36442.30,36411.60,36414.40,96.336,1651813379999,3509724.10790,1101,39.828,1451049.69810,0 +1651813380000,36414.40,36414.40,36405.10,36410.30,57.977,1651813439999,2110875.90310,845,23.616,859817.95640,0 +1651813440000,36410.20,36440.70,36409.50,36429.00,143.904,1651813499999,5241632.27770,1064,115.147,4194189.93170,0 +1651813500000,36429.00,36449.80,36403.20,36408.00,106.389,1651813559999,3875900.76256,1250,39.360,1434150.23146,0 +1651813560000,36407.90,36407.90,36395.00,36395.10,46.276,1651813619999,1684446.67310,696,17.691,643942.38010,0 +1651813620000,36395.10,36395.20,36379.00,36381.70,109.187,1651813679999,3972767.34930,928,15.136,550701.54360,0 +1651813680000,36381.60,36381.70,36373.10,36378.60,61.367,1651813739999,2232362.90900,761,24.750,900291.40190,0 +1651813740000,36378.70,36382.30,36358.70,36378.10,126.782,1651813799999,4611134.36590,1152,81.349,2958860.74810,0 +1651813800000,36378.10,36385.00,36363.90,36366.70,76.407,1651813859999,2779320.26400,949,35.251,1282303.88700,0 +1651813860000,36366.80,36404.10,36366.70,36397.70,63.141,1651813919999,2298056.29180,928,45.960,1672800.75680,0 +1651813920000,36397.80,36400.00,36397.70,36400.00,16.959,1651813979999,617276.52490,267,11.459,417084.82770,0 +1651813980000,36399.90,36430.00,36389.70,36424.20,120.501,1651814039999,4387919.21080,1337,75.035,2732260.64710,0 +1651814040000,36424.20,36424.20,36403.50,36408.90,62.914,1651814099999,2290910.93270,729,21.178,771154.17600,0 +1651814100000,36409.00,36424.00,36398.90,36398.90,61.820,1651814159999,2250791.00110,715,33.114,1205687.72700,0 +1651814160000,36399.00,36407.00,36392.50,36406.90,36.594,1651814219999,1331968.37340,546,13.898,505878.78200,0 +1651814220000,36406.90,36443.70,36406.90,36432.60,101.748,1651814279999,3707094.66600,947,79.255,2887544.07980,0 +1651814280000,36432.60,36434.30,36412.40,36430.00,184.206,1651814339999,6709386.70050,1140,43.048,1567899.80530,0 +1651814340000,36429.90,36441.10,36420.00,36423.70,75.684,1651814399999,2757010.28610,748,25.252,919940.67690,0 +1651814400000,36423.80,36449.00,36423.10,36439.20,76.667,1651814459999,2793522.72590,1007,49.013,1785974.69770,0 +1651814460000,36439.20,36460.50,36438.90,36452.40,121.384,1651814519999,4424300.25461,1066,101.460,3698199.59991,0 +1651814520000,36452.50,36460.60,36445.00,36445.00,103.491,1651814579999,3772416.05810,997,35.851,1306846.44100,0 +1651814580000,36445.00,36450.20,36437.90,36446.60,69.413,1651814639999,2529671.03550,873,29.345,1069473.18720,0 +1651814640000,36446.60,36449.00,36437.70,36442.00,35.338,1651814699999,1287863.10210,515,20.608,751041.83870,0 +1651814700000,36442.00,36450.00,36441.90,36450.00,28.548,1651814759999,1040516.03540,421,17.273,629563.12660,0 +1651814760000,36449.90,36450.00,36435.00,36441.80,106.474,1651814819999,3880185.60340,869,25.817,940892.06150,0 +1651814820000,36441.90,36441.90,36441.00,36441.00,21.553,1651814879999,785427.80180,353,7.902,287963.34320,0 +1651814880000,36441.10,36450.70,36424.90,36443.00,75.316,1651814939999,2744101.99060,687,26.493,965352.49060,0 +1651814940000,36443.10,36449.10,36441.60,36445.40,41.806,1651814999999,1523666.42150,586,18.592,677617.98270,0 +1651815000000,36445.40,36457.40,36413.20,36415.60,129.268,1651815059999,4709897.63010,1318,69.035,2515332.50450,0 +1651815060000,36415.70,36425.40,36405.40,36411.70,126.949,1651815119999,4622792.31840,1016,44.411,1617315.79930,0 +1651815120000,36411.70,36442.00,36411.60,36442.00,62.503,1651815179999,2276758.83700,831,40.880,1489200.55880,0 +1651815180000,36442.00,36454.30,36434.50,36443.10,51.247,1651815239999,1867836.37300,923,13.700,499321.11520,0 +1651815240000,36443.10,36450.00,36420.20,36422.40,71.741,1651815299999,2613947.52480,830,22.497,819692.96170,0 +1651815300000,36422.80,36448.50,36422.70,36448.40,48.048,1651815359999,1750471.35200,803,30.195,1100040.62890,0 +1651815360000,36448.50,36453.70,36444.10,36447.20,68.171,1651815419999,2484854.58820,662,44.462,1620647.38910,0 +1651815420000,36447.20,36458.10,36438.20,36450.00,48.299,1651815479999,1760555.01890,830,26.588,969174.19620,0 +1651815480000,36450.10,36475.40,36450.00,36470.70,59.778,1651815539999,2179660.71710,908,43.152,1573438.84760,0 +1651815540000,36470.70,36477.50,36470.60,36470.60,82.202,1651815599999,2998314.74680,793,57.639,2102391.90280,0 +1651815600000,36470.60,36471.70,36454.70,36461.70,71.879,1651815659999,2621079.06300,839,44.301,1615539.32520,0 +1651815660000,36461.60,36471.60,36454.80,36466.00,56.182,1651815719999,2048625.24120,760,27.478,1001948.51830,0 +1651815720000,36465.80,36471.60,36462.20,36465.80,18.810,1651815779999,685938.79730,452,6.169,224963.83670,0 +1651815780000,36465.80,36465.90,36457.90,36462.80,46.733,1651815839999,1703964.12650,518,8.038,293079.56540,0 +1651815840000,36461.00,36468.80,36453.30,36453.40,73.370,1651815899999,2675169.53830,744,37.510,1367687.73840,0 +1651815900000,36453.40,36456.20,36448.00,36449.90,62.807,1651815959999,2289447.66500,741,25.804,940613.88050,0 +1651815960000,36450.00,36450.00,36439.20,36444.70,51.960,1651816019999,1893662.63070,680,20.048,730647.99480,0 +1651816020000,36444.80,36450.00,36441.00,36448.80,26.267,1651816079999,957306.23640,559,11.595,422585.73500,0 +1651816080000,36448.70,36475.00,36446.60,36475.00,69.010,1651816139999,2515735.80140,675,55.030,2006113.13700,0 +1651816140000,36475.00,36475.00,36456.80,36456.80,41.412,1651816199999,1510256.68790,648,14.219,518547.12770,0 +1651816200000,36456.90,36473.10,36456.90,36467.10,94.904,1651816259999,3460881.41200,749,42.112,1535618.15240,0 +1651816260000,36467.10,36494.90,36466.70,36484.30,255.906,1651816319999,9336355.23024,1460,221.080,8065805.61764,0 +1651816320000,36484.30,36484.30,36463.00,36463.00,76.934,1651816379999,2805894.13340,756,19.075,695718.22880,0 +1651816380000,36463.10,36463.10,36441.70,36441.70,45.404,1651816439999,1655079.23770,630,13.692,499161.82740,0 +1651816440000,36441.70,36460.40,36440.00,36449.90,118.224,1651816499999,4309458.10140,1144,54.821,1998315.67900,0 +1651816500000,36449.90,36456.30,36448.00,36449.60,29.345,1651816559999,1069694.42030,559,16.827,613369.78040,0 +1651816560000,36449.50,36455.20,36428.80,36451.60,89.257,1651816619999,3252715.70750,1025,41.181,1500701.24810,0 +1651816620000,36451.60,36483.10,36451.60,36479.40,55.551,1651816679999,2026059.97150,784,33.376,1217288.99870,0 +1651816680000,36479.30,36485.20,36474.50,36478.50,40.260,1651816739999,1468771.78780,623,23.128,843772.58460,0 +1651816740000,36479.60,36485.00,36477.20,36482.60,42.394,1651816799999,1546634.00170,591,18.779,685087.18710,0 +1651816800000,36482.60,36492.00,36482.50,36487.80,109.958,1651816859999,4012141.93390,1130,39.136,1427998.68830,0 +1651816860000,36487.80,36487.80,36471.20,36474.90,76.023,1651816919999,2773269.89990,826,17.752,647577.13530,0 +1651816920000,36475.00,36482.70,36466.60,36471.40,47.349,1651816979999,1727109.22690,704,18.286,667002.14340,0 +1651816980000,36471.30,36486.70,36471.30,36477.30,34.865,1651817039999,1271840.35040,644,22.920,836116.41380,0 +1651817040000,36478.10,36513.40,36478.10,36511.40,244.331,1651817099999,8918159.70348,2383,194.088,7084180.54658,0 +1651817100000,36511.40,36521.40,36504.20,36519.30,175.223,1651817159999,6397847.78325,1799,69.071,2522050.67495,0 +1651817160000,36519.30,36532.60,36505.60,36522.20,186.082,1651817219999,6795479.80224,1880,130.784,4776300.74514,0 +1651817220000,36522.20,36535.90,36516.20,36520.50,121.092,1651817279999,4422960.51111,1570,71.578,2614468.49791,0 +1651817280000,36520.50,36520.60,36475.30,36477.50,124.372,1651817339999,4539187.51100,1574,30.316,1106424.95210,0 +1651817340000,36477.60,36482.20,36456.60,36480.00,113.117,1651817399999,4125249.48050,1427,40.488,1476457.08610,0 +1651817400000,36480.00,36498.70,36479.90,36488.60,67.462,1651817459999,2461663.77590,965,34.696,1266032.29170,0 +1651817460000,36488.60,36492.70,36475.50,36477.40,47.751,1651817519999,1742166.07110,811,14.407,525631.40100,0 +1651817520000,36477.30,36482.00,36450.00,36451.50,277.480,1651817579999,10119539.71570,1677,43.665,1592039.50580,0 +1651817580000,36451.50,36458.60,36442.10,36456.70,112.631,1651817639999,4105277.38660,966,38.378,1398881.57180,0 +1651817640000,36456.70,36468.70,36449.70,36449.70,93.964,1651817699999,3425757.84230,930,48.351,1762935.73100,0 +1651817700000,36449.60,36449.60,36425.90,36432.40,147.076,1651817759999,5358863.09990,1698,47.453,1729014.90430,0 +1651817760000,36432.40,36436.70,36419.50,36420.60,77.035,1651817819999,2806352.88620,1157,20.662,752684.49350,0 +1651817820000,36420.50,36424.70,36400.00,36416.90,152.411,1651817879999,5549722.13910,1689,51.331,1869081.04330,0 +1651817880000,36416.80,36424.20,36409.10,36413.40,78.520,1651817939999,2859471.17280,1053,45.182,1645441.61250,0 +1651817940000,36413.40,36433.00,36413.30,36427.90,72.954,1651817999999,2657400.56310,697,62.209,2266032.17260,0 +1651818000000,36427.90,36434.90,36418.40,36434.20,95.445,1651818059999,3476752.86960,843,49.863,1816422.44190,0 +1651818060000,36434.20,36435.60,36426.00,36435.50,38.842,1651818119999,1415030.50000,535,13.869,505258.13200,0 +1651818120000,36435.50,36462.90,36432.80,36456.80,99.986,1651818179999,3644106.00760,910,82.174,2994876.84140,0 +1651818180000,36456.80,36471.80,36437.50,36440.90,102.304,1651818239999,3729731.14770,1223,58.275,2124660.39620,0 +1651818240000,36440.90,36462.00,36440.90,36458.40,95.695,1651818299999,3488091.58040,844,48.681,1774554.17940,0 +1651818300000,36458.40,36459.10,36424.50,36433.40,94.363,1651818359999,3439003.08620,1082,30.939,1127527.00220,0 +1651818360000,36433.40,36434.80,36423.00,36428.50,57.920,1651818419999,2110053.00690,616,28.507,1038554.79120,0 +1651818420000,36428.50,36435.70,36415.00,36419.60,53.386,1651818479999,1944471.03970,706,15.152,551951.50060,0 +1651818480000,36419.70,36419.80,36413.00,36419.70,47.737,1651818539999,1738470.44010,548,19.295,702689.76670,0 +1651818540000,36419.80,36419.80,36383.60,36397.00,174.323,1651818599999,6345358.50230,1847,46.741,1701258.64440,0 +1651818600000,36397.00,36410.40,36380.70,36409.30,92.554,1651818659999,3368434.43450,1065,33.214,1208955.22790,0 +1651818660000,36409.40,36410.00,36401.20,36403.90,48.695,1651818719999,1772724.48910,669,19.358,704719.32590,0 +1651818720000,36403.90,36417.80,36403.70,36403.70,51.045,1651818779999,1858637.82920,586,13.863,504737.72980,0 +1651818780000,36403.80,36418.40,36403.80,36407.50,41.737,1651818839999,1519709.87810,506,24.239,882594.11160,0 +1651818840000,36407.50,36419.40,36407.40,36419.40,37.957,1651818899999,1382089.44680,450,17.845,649800.42460,0 +1651818900000,36419.30,36420.80,36412.30,36412.60,60.744,1651818959999,2212219.30810,508,39.997,1456637.01760,0 +1651818960000,36412.50,36412.60,36395.60,36411.50,74.209,1651819019999,2701329.95190,922,39.940,1453823.03700,0 +1651819020000,36411.50,36445.00,36411.50,36438.50,63.389,1651819079999,2309589.57200,977,40.637,1480649.55340,0 +1651819080000,36438.50,36442.10,36425.20,36438.90,64.816,1651819139999,2361616.91520,616,20.380,742594.88030,0 +1651819140000,36438.90,36450.00,36436.20,36440.10,55.252,1651819199999,2013663.71570,647,42.732,1557372.36280,0 +1651819200000,36440.10,36451.40,36440.00,36447.10,78.188,1651819259999,2849757.36230,636,68.906,2511470.97690,0 +1651819260000,36447.10,36449.30,36442.10,36449.30,20.940,1651819319999,763160.16470,307,10.498,382599.08220,0 +1651819320000,36449.20,36449.30,36400.00,36400.10,129.207,1651819379999,4707182.88320,862,10.855,395375.42110,0 +1651819380000,36400.10,36422.20,36400.00,36418.00,47.893,1651819439999,1743930.01330,697,26.885,978868.71800,0 +1651819440000,36418.00,36426.00,36418.00,36419.70,16.077,1651819499999,585558.80680,308,7.082,257933.24640,0 +1651819500000,36419.70,36426.40,36419.70,36424.20,24.714,1651819559999,900168.45590,374,14.500,528143.98570,0 +1651819560000,36424.10,36426.30,36417.30,36426.30,77.500,1651819619999,2822571.08960,487,38.872,1415738.80680,0 +1651819620000,36426.30,36427.70,36422.60,36425.70,23.325,1651819679999,849654.77980,366,6.154,224172.93160,0 +1651819680000,36426.70,36430.90,36416.60,36430.10,28.786,1651819739999,1048544.59320,555,16.276,592862.67480,0 +1651819740000,36430.10,36430.10,36400.00,36400.10,39.271,1651819799999,1430041.07850,535,12.963,472069.55540,0 +1651819800000,36400.00,36415.30,36400.00,36400.10,61.257,1651819859999,2230008.28550,772,30.279,1102295.94140,0 +1651819860000,36400.10,36400.90,36389.80,36397.80,80.261,1651819919999,2921021.83820,706,44.955,1636005.75360,0 +1651819920000,36397.70,36398.10,36390.70,36398.00,42.486,1651819979999,1546254.28250,558,15.066,548309.12850,0 +1651819980000,36398.00,36398.10,36390.10,36390.10,41.764,1651820039999,1520005.94580,527,17.791,647487.00540,0 +1651820040000,36390.20,36395.90,36369.50,36383.60,147.852,1651820099999,5378849.53440,1306,39.287,1429241.10410,0 +1651820100000,36383.50,36398.00,36383.50,36395.60,68.579,1651820159999,2495757.57640,792,38.057,1384949.60540,0 +1651820160000,36395.60,36398.90,36390.70,36398.80,87.312,1651820219999,3177891.79790,577,51.822,1886139.07490,0 +1651820220000,36398.80,36398.80,36383.00,36391.70,33.112,1651820279999,1204926.13320,591,15.581,566946.77570,0 +1651820280000,36391.70,36406.20,36390.70,36392.10,165.135,1651820339999,6010725.70360,991,129.095,4699052.70940,0 +1651820340000,36392.90,36398.30,36387.00,36397.80,40.673,1651820399999,1480269.82290,620,18.357,668097.45430,0 +1651820400000,36397.80,36407.30,36359.90,36380.90,367.645,1651820459999,13375096.72710,2317,92.444,3363694.65380,0 +1651820460000,36380.90,36383.00,36349.00,36363.00,219.689,1651820519999,7988653.71525,2040,78.967,2871571.40510,0 +1651820520000,36362.90,36371.30,36335.30,36371.20,265.619,1651820579999,9655222.01296,2501,100.681,3659953.46060,0 +1651820580000,36371.30,36387.60,36371.20,36378.50,82.900,1651820639999,3015653.79690,979,45.767,1664851.65710,0 +1651820640000,36378.40,36393.20,36378.40,36388.00,102.673,1651820699999,3735806.35480,1061,71.135,2588300.55700,0 +1651820700000,36387.90,36405.10,36371.00,36371.10,78.619,1651820759999,2860992.31320,1183,38.404,1397648.60340,0 +1651820760000,36371.10,36382.10,36351.40,36371.70,124.390,1651820819999,4523523.92620,1310,60.059,2183950.69730,0 +1651820820000,36371.80,36393.90,36371.70,36386.30,87.691,1651820879999,3190654.72070,1029,34.866,1268525.40520,0 +1651820880000,36386.30,36406.20,36380.00,36383.40,77.261,1651820939999,2811629.87050,960,47.023,1711233.31660,0 +1651820940000,36383.30,36392.60,36383.30,36388.10,17.056,1651820999999,620681.76260,440,8.303,302149.91040,0 +1651821000000,36388.10,36388.10,36369.60,36376.50,62.569,1651821059999,2276125.66900,764,26.864,977272.44860,0 +1651821060000,36376.40,36384.60,36361.20,36371.20,142.316,1651821119999,5176089.30010,966,23.345,849139.71860,0 +1651821120000,36371.20,36390.00,36364.10,36386.00,82.949,1651821179999,3017510.58870,867,37.663,1370155.08150,0 +1651821180000,36386.10,36400.00,36386.00,36389.50,59.065,1651821239999,2149724.86010,629,38.608,1405144.42840,0 +1651821240000,36389.50,36398.40,36378.90,36398.40,54.171,1651821299999,1971129.41050,678,33.962,1235788.29110,0 +1651821300000,36398.70,36404.10,36389.70,36404.10,91.057,1651821359999,3314173.69930,1037,63.668,2317239.73460,0 +1651821360000,36404.10,36425.20,36402.40,36417.80,102.215,1651821419999,3722458.16780,1417,73.615,2680875.69550,0 +1651821420000,36417.80,36423.30,36411.80,36416.10,57.568,1651821479999,2096547.09280,643,34.984,1274101.03900,0 +1651821480000,36416.10,36416.10,36401.90,36405.30,32.315,1651821539999,1176463.69570,510,15.563,566567.89310,0 +1651821540000,36405.20,36428.10,36405.20,36428.10,48.716,1651821599999,1774201.07120,754,34.444,1254397.21170,0 +1651821600000,36428.00,36437.00,36411.60,36411.70,99.723,1651821659999,3632791.59880,1177,60.910,2219016.89150,0 +1651821660000,36411.70,36411.70,36394.10,36400.70,89.131,1651821719999,3244716.47850,834,30.947,1126607.46830,0 +1651821720000,36400.80,36400.80,36388.00,36388.10,59.337,1651821779999,2159521.88430,612,23.293,847760.13050,0 +1651821780000,36388.00,36390.00,36371.00,36371.00,75.130,1651821839999,2733245.93090,1004,32.624,1186887.23270,0 +1651821840000,36371.00,36389.90,36369.40,36369.40,64.567,1651821899999,2348877.51690,815,23.064,838986.87060,0 +1651821900000,36369.40,36389.00,36360.20,36382.30,81.234,1651821959999,2954643.92350,1141,46.117,1677469.72260,0 +1651821960000,36382.30,36415.60,36379.50,36394.30,99.897,1651822019999,3636226.97130,989,65.431,2381614.01140,0 +1651822020000,36394.40,36401.50,36360.00,36369.10,72.751,1651822079999,2646329.07430,965,26.783,974238.86670,0 +1651822080000,36369.00,36370.90,36339.90,36353.30,126.135,1651822139999,4585140.79050,1313,49.421,1796479.91060,0 +1651822140000,36353.40,36366.80,36340.10,36352.70,140.090,1651822199999,5092382.68660,1169,54.947,1997455.52390,0 +1651822200000,36352.70,36352.80,36323.00,36324.60,158.354,1651822259999,5754219.11390,1842,38.250,1390039.33320,0 +1651822260000,36324.70,36349.40,36320.00,36346.80,131.283,1651822319999,4770157.51640,1512,58.892,2140101.17520,0 +1651822320000,36346.80,36364.80,36341.90,36364.60,83.096,1651822379999,3020605.00130,1046,60.850,2211933.15430,0 +1651822380000,36364.70,36364.70,36338.10,36347.10,160.494,1651822439999,5833569.06490,1332,61.756,2244688.54620,0 +1651822440000,36347.10,36347.50,36332.60,36334.40,120.655,1651822499999,4384624.30160,1254,29.593,1075424.27660,0 +1651822500000,36334.40,36346.20,36228.90,36234.50,2103.637,1651822559999,76314497.55144,9345,649.269,23547996.15360,0 +1651822560000,36234.50,36255.30,36133.00,36169.20,1623.453,1651822619999,58759047.15661,13000,601.161,21761902.02910,0 +1651822620000,36169.20,36237.30,36150.30,36205.60,675.534,1651822679999,24455460.79820,6125,361.672,13093667.85370,0 +1651822680000,36205.50,36229.50,36161.10,36167.30,361.998,1651822739999,13105317.60200,3660,136.562,4945144.84250,0 +1651822740000,36167.20,36172.00,35900.00,36055.00,3027.017,1651822799999,109086971.02914,22607,1006.876,36285014.32849,0 +1651822800000,36055.00,36066.10,35860.00,35958.10,2570.629,1651822859999,92394978.18656,20124,1070.068,38465177.44761,0 +1651822860000,35958.10,35972.40,35800.00,35877.90,1836.788,1651822919999,65877678.45485,15830,691.052,24792907.07060,0 +1651822920000,35877.90,35944.90,35861.50,35899.70,1078.876,1651822979999,38738264.08420,8584,712.183,25573926.13090,0 +1651822980000,35899.60,35950.00,35873.90,35885.80,774.734,1651823039999,27822748.34300,6704,389.319,13983291.16150,0 +1651823040000,35885.90,35932.60,35881.20,35897.90,533.327,1651823099999,19149686.15780,4809,272.193,9773746.59210,0 +1651823100000,35897.60,35915.80,35826.30,35867.50,1013.349,1651823159999,36352595.22010,7440,401.929,14423900.79720,0 +1651823160000,35867.60,35919.40,35828.80,35848.50,634.748,1651823219999,22771155.74810,5511,280.024,10047855.96520,0 +1651823220000,35848.50,35874.50,35828.10,35871.30,579.391,1651823279999,20774212.73640,6049,323.850,11612251.13410,0 +1651823280000,35871.30,35929.80,35871.20,35917.80,1166.661,1651823339999,41881859.96920,9617,698.101,25061615.50240,0 +1651823340000,35917.80,36125.00,35911.00,36079.80,2467.453,1651823399999,88934679.70420,16016,1751.127,63104342.89760,0 +1651823400000,36080.10,36251.40,36074.50,36210.10,1534.121,1651823459999,55470660.82428,11945,1084.816,39229889.35058,0 +1651823460000,36210.00,36235.40,36133.10,36173.70,846.715,1651823519999,30630550.97570,7387,322.078,11650841.92300,0 +1651823520000,36173.80,36173.80,36108.00,36160.90,656.366,1651823579999,23723249.50360,5979,319.879,11560705.51220,0 +1651823580000,36160.80,36344.10,36157.50,36337.10,1401.948,1651823639999,50839946.76349,11019,1011.196,36673023.57399,0 +1651823640000,36337.20,36422.40,36305.00,36327.70,1497.522,1651823699999,54437306.78026,12400,739.919,26903043.43226,0 +1651823700000,36327.70,36432.90,36320.30,36386.80,1162.203,1651823759999,42292118.85640,8799,699.186,25445210.10770,0 +1651823760000,36385.40,36431.80,36359.50,36369.30,605.644,1651823819999,22044384.17750,5952,320.534,11669189.64890,0 +1651823820000,36369.30,36419.30,36369.10,36398.70,446.891,1651823879999,16263386.71890,4478,263.015,9571871.45820,0 +1651823880000,36401.00,36414.30,36369.10,36390.80,368.331,1651823939999,13405200.56570,3790,153.873,5600205.90980,0 +1651823940000,36390.80,36390.80,36359.20,36359.30,311.452,1651823999999,11327560.77630,3230,120.592,4385961.78680,0 +1651824000000,36359.30,36362.60,36216.30,36264.90,1473.299,1651824059999,53470087.55005,10944,505.685,18354662.06410,0 +1651824060000,36264.90,36289.60,36185.90,36256.30,873.351,1651824119999,31647224.08622,7402,368.866,13369445.60360,0 +1651824120000,36256.20,36264.50,36151.10,36183.70,718.686,1651824179999,26010941.00068,6096,322.058,11655471.64940,0 +1651824180000,36183.70,36229.90,36148.20,36229.80,555.653,1651824239999,20102363.32090,5611,314.554,11380787.21240,0 +1651824240000,36229.90,36276.00,36195.60,36211.90,422.981,1651824299999,15327248.28486,4808,261.023,9459833.73546,0 +1651824300000,36212.00,36220.40,36185.60,36204.60,200.826,1651824359999,7270355.85240,2856,102.004,3692821.19280,0 +1651824360000,36204.70,36324.60,36204.60,36314.10,559.344,1651824419999,20295574.22179,5258,392.043,14224426.16719,0 +1651824420000,36314.10,36510.80,36314.10,36464.90,1937.687,1651824479999,70602220.70936,14301,1470.131,53565274.67456,0 +1651824480000,36464.30,36468.10,36376.50,36382.70,638.506,1651824539999,23252633.31680,6216,200.515,7303715.46530,0 +1651824540000,36382.70,36398.90,36325.30,36381.50,464.080,1651824599999,16876843.03691,4977,210.655,7661667.49590,0 +1651824600000,36381.40,36403.30,36354.50,36355.30,238.206,1651824659999,8665465.98040,2595,73.900,2688376.87040,0 +1651824660000,36355.40,36398.10,36350.00,36379.90,150.977,1651824719999,5491962.98430,2314,68.060,2475922.49700,0 +1651824720000,36380.00,36386.90,36332.60,36333.00,230.429,1651824779999,8379483.10310,2436,84.227,3063422.47900,0 +1651824780000,36333.00,36333.30,36230.40,36245.10,541.968,1651824839999,19662717.90510,5333,166.199,6030917.18470,0 +1651824840000,36244.90,36288.60,36240.40,36243.50,463.046,1651824899999,16791038.44840,3839,238.881,8663316.22930,0 +1651824900000,36243.60,36285.00,36242.10,36244.40,274.113,1651824959999,9939398.75350,3248,145.344,5270256.50790,0 +1651824960000,36244.50,36244.50,36196.60,36217.10,652.455,1651825019999,23628957.74278,5187,235.165,8515788.36950,0 +1651825020000,36217.20,36240.00,36201.10,36225.20,266.301,1651825079999,9645314.35800,2706,103.677,3755716.55460,0 +1651825080000,36225.20,36239.80,36171.20,36234.10,312.447,1651825139999,11310114.93665,3916,115.721,4189778.68770,0 +1651825140000,36234.20,36359.20,36222.20,36273.80,700.434,1651825199999,25426971.06025,6386,440.073,15974469.00945,0 +1651825200000,36273.90,36320.00,36226.40,36239.90,285.626,1651825259999,10361497.44106,3376,157.614,5718364.61090,0 +1651825260000,36240.00,36264.00,36213.50,36255.70,174.352,1651825319999,6318187.49900,2561,72.647,2632771.83390,0 +1651825320000,36255.80,36305.40,36255.80,36281.70,173.308,1651825379999,6287370.39030,2155,108.766,3945999.21170,0 +1651825380000,36281.80,36327.10,36281.70,36307.10,189.336,1651825439999,6874597.95290,2112,147.156,5343006.74450,0 +1651825440000,36307.10,36319.30,36278.30,36305.50,360.872,1651825499999,13098210.78630,3080,106.344,3860139.48110,0 +1651825500000,36305.50,36339.30,36305.40,36329.50,173.068,1651825559999,6286661.82320,2071,122.857,4462788.98450,0 +1651825560000,36329.50,36345.60,36307.10,36310.10,165.968,1651825619999,6028860.32460,1984,84.517,3070360.25280,0 +1651825620000,36310.10,36312.60,36265.50,36269.50,215.346,1651825679999,7813385.51650,2113,74.454,2701055.37820,0 +1651825680000,36269.60,36302.20,36265.00,36296.70,125.634,1651825739999,4558112.60820,1738,57.243,2076905.87420,0 +1651825740000,36296.70,36308.60,36278.50,36289.40,132.509,1651825799999,4809658.69470,1631,76.957,2793407.00420,0 +1651825800000,36288.40,36329.90,36281.30,36315.90,115.973,1651825859999,4210839.90830,1645,69.323,2517087.99450,0 +1651825860000,36315.90,36320.40,36286.10,36295.80,76.330,1651825919999,2770691.35040,1348,33.023,1198803.75010,0 +1651825920000,36295.90,36378.40,36295.90,36360.00,369.807,1651825979999,13443611.68795,3763,291.854,10609744.34755,0 +1651825980000,36360.00,36398.50,36342.50,36350.60,265.792,1651826039999,9666166.16030,3215,143.030,5202152.15660,0 +1651826040000,36350.60,36366.50,36327.30,36338.70,88.004,1651826099999,3198336.11450,1530,41.612,1512355.14400,0 +1651826100000,36338.60,36353.50,36323.40,36335.10,131.189,1651826159999,4766830.56560,1631,40.915,1486657.75220,0 +1651826160000,36335.10,36350.30,36325.60,36328.10,85.539,1651826219999,3107940.46600,1178,43.566,1582900.20290,0 +1651826220000,36329.70,36334.80,36279.00,36284.70,310.810,1651826279999,11283228.04410,2106,162.628,5903620.29180,0 +1651826280000,36284.80,36298.80,36266.10,36274.10,139.971,1651826339999,5078503.31900,1685,44.431,1612110.15750,0 +1651826340000,36274.00,36285.50,36244.00,36259.90,189.214,1651826399999,6860977.93596,2099,65.712,2383018.59320,0 +1651826400000,36259.80,36273.20,36208.20,36216.50,356.661,1651826459999,12925188.53610,3347,102.335,3708855.67450,0 +1651826460000,36216.60,36221.80,36200.00,36221.80,206.648,1651826519999,7482462.00630,2314,86.060,3116303.68910,0 +1651826520000,36221.80,36260.20,36220.00,36231.90,153.925,1651826579999,5578078.38570,2039,100.307,3635034.14660,0 +1651826580000,36232.00,36232.00,36182.00,36214.10,319.666,1651826639999,11573240.94438,2848,151.701,5492010.10080,0 +1651826640000,36214.00,36244.10,36202.20,36213.70,138.131,1651826699999,5004002.34030,1646,85.152,3084785.10090,0 +1651826700000,36213.70,36215.00,36168.40,36184.30,344.912,1651826759999,12483089.74451,2928,117.095,4237354.75650,0 +1651826760000,36184.40,36211.90,36168.00,36197.70,350.276,1651826819999,12676260.21741,3229,234.106,8472026.07720,0 +1651826820000,36197.70,36262.70,36193.30,36249.20,263.503,1651826879999,9547405.43570,3087,165.670,6002691.77360,0 +1651826880000,36249.20,36249.20,36202.30,36241.10,105.492,1651826939999,3821312.94920,1659,54.839,1986181.85010,0 +1651826940000,36241.10,36251.80,36215.20,36231.70,90.486,1651826999999,3278511.80600,1604,37.717,1366600.88390,0 +1651827000000,36231.80,36246.70,36209.80,36246.60,99.894,1651827059999,3618998.41890,1536,40.771,1476996.84350,0 +1651827060000,36247.40,36249.90,36224.50,36224.60,167.523,1651827119999,6070522.90040,1696,55.191,1999998.38870,0 +1651827120000,36224.50,36290.40,36212.60,36283.60,185.669,1651827179999,6732230.57410,2439,116.512,4225051.30370,0 +1651827180000,36283.70,36284.00,36256.20,36264.30,139.461,1651827239999,5058396.68610,1458,58.636,2126798.60240,0 +1651827240000,36264.30,36264.40,36185.90,36193.00,279.093,1651827299999,10106268.90620,2436,38.766,1403546.31010,0 +1651827300000,36192.90,36217.00,36185.90,36202.20,182.199,1651827359999,6595846.56600,2209,79.830,2889882.16170,0 +1651827360000,36202.10,36231.80,36194.00,36231.80,100.647,1651827419999,3644585.27990,1402,45.543,1649266.67280,0 +1651827420000,36231.80,36251.70,36231.80,36249.10,74.778,1651827479999,2710218.98490,1172,39.038,1414881.78400,0 +1651827480000,36249.10,36249.10,36198.10,36206.40,127.458,1651827539999,4616532.91360,1695,46.192,1673235.67040,0 +1651827540000,36206.30,36220.00,36191.50,36197.50,153.547,1651827599999,5558763.40240,1633,53.076,1921650.29110,0 +1651827600000,36197.50,36229.90,36191.90,36217.50,106.162,1651827659999,3844328.93080,1512,56.280,2037972.84280,0 +1651827660000,36217.50,36221.00,36200.00,36200.70,97.920,1651827719999,3545876.04890,1026,25.993,941194.99660,0 +1651827720000,36200.80,36216.70,36193.10,36201.10,102.260,1651827779999,3702286.74820,1236,55.631,2014134.94030,0 +1651827780000,36201.00,36239.40,36201.00,36231.50,77.063,1651827839999,2791983.45620,1199,49.679,1799869.31850,0 +1651827840000,36231.60,36248.00,36231.50,36248.00,62.705,1651827899999,2272532.48820,997,43.355,1571256.48770,0 +1651827900000,36247.90,36248.00,36223.10,36241.40,85.979,1651827959999,3115749.98640,1235,33.102,1199613.00910,0 +1651827960000,36241.30,36242.60,36226.60,36238.80,91.678,1651828019999,3322027.50920,1103,19.475,705734.89920,0 +1651828020000,36238.80,36244.50,36205.20,36205.90,126.508,1651828079999,4583079.05300,1225,24.098,873150.40880,0 +1651828080000,36205.80,36205.90,36165.80,36171.00,236.425,1651828139999,8553834.96690,2841,112.627,4074519.51610,0 +1651828140000,36171.10,36208.30,36166.00,36199.10,154.105,1651828199999,5577061.36530,1946,102.729,3717928.14630,0 +1651828200000,36199.10,36228.50,36199.10,36216.70,174.074,1651828259999,6303998.96700,1787,87.397,3165029.22900,0 +1651828260000,36216.70,36225.20,36203.60,36203.90,50.527,1651828319999,1829872.14030,906,14.701,532399.93570,0 +1651828320000,36204.00,36209.90,36188.00,36192.00,87.932,1651828379999,3182967.67570,1409,48.393,1751718.24340,0 +1651828380000,36191.90,36197.20,36171.30,36176.00,108.083,1651828439999,3910628.84760,1547,48.478,1754028.69440,0 +1651828440000,36176.00,36207.20,36175.90,36205.00,127.999,1651828499999,4632988.72050,1618,85.132,3081302.22320,0 +1651828500000,36204.90,36210.00,36196.70,36201.20,128.108,1651828559999,4638076.04080,1310,83.700,3030362.91920,0 +1651828560000,36200.40,36238.00,36196.10,36196.10,114.742,1651828619999,4156614.01420,1491,54.415,1971327.27860,0 +1651828620000,36196.50,36196.50,36150.90,36168.50,311.834,1651828679999,11277490.93977,3275,117.352,4243928.97100,0 +1651828680000,36168.40,36179.90,36151.40,36169.40,137.040,1651828739999,4956056.41340,1682,58.966,2132519.46570,0 +1651828740000,36169.50,36215.00,36169.40,36210.00,154.096,1651828799999,5576847.42990,1612,89.386,3235007.26650,0 +1651828800000,36209.90,36267.30,36203.80,36256.70,402.644,1651828859999,14593357.56736,3465,307.584,11148057.91196,0 +1651828860000,36256.70,36256.70,36241.30,36244.00,160.826,1651828919999,5829927.90040,1611,80.912,2933078.01210,0 +1651828920000,36244.00,36246.00,36222.00,36245.90,91.071,1651828979999,3299603.74670,1142,31.589,1144606.44910,0 +1651828980000,36245.90,36262.50,36238.60,36241.20,87.775,1651829039999,3181869.82900,1204,50.765,1840322.78280,0 +1651829040000,36241.20,36264.50,36241.10,36256.80,83.793,1651829099999,3038047.27460,1150,46.260,1677261.24480,0 +1651829100000,36256.90,36315.50,36256.80,36294.20,328.581,1651829159999,11925050.12169,3736,260.773,9463876.95159,0 +1651829160000,36294.30,36321.90,36294.20,36308.90,367.719,1651829219999,13350856.16170,3046,219.023,7952020.44410,0 +1651829220000,36308.90,36315.00,36300.00,36300.00,268.837,1651829279999,9759361.33240,1135,31.271,1135269.75760,0 +1651829280000,36300.10,36325.00,36276.40,36314.30,517.473,1651829339999,18783858.17420,2271,106.428,3863374.24840,0 +1651829340000,36314.20,36314.30,36286.70,36298.80,82.900,1651829399999,3009008.41400,1184,29.928,1086226.95770,0 +1651829400000,36298.70,36311.70,36285.40,36307.00,71.380,1651829459999,2591195.64440,1219,38.633,1402397.22720,0 +1651829460000,36307.10,36329.00,36305.60,36323.90,122.610,1651829519999,4453249.42850,1452,92.638,3364677.03800,0 +1651829520000,36323.90,36346.00,36317.10,36337.10,174.219,1651829579999,6329959.66286,2116,116.182,4221263.94886,0 +1651829580000,36337.00,36349.40,36336.00,36336.00,125.878,1651829639999,4574856.65530,1740,77.224,2806595.36840,0 +1651829640000,36336.10,36342.40,36326.80,36329.90,99.938,1651829699999,3631217.88610,1262,26.310,956029.76070,0 +1651829700000,36329.60,36356.00,36329.50,36345.10,306.545,1651829759999,11141886.47955,2311,198.349,7209324.43045,0 +1651829760000,36345.20,36360.00,36334.50,36334.50,90.806,1651829819999,3300393.56290,1416,37.709,1370673.68800,0 +1651829820000,36334.50,36358.70,36334.50,36358.60,94.819,1651829879999,3446108.79820,1106,64.898,2358711.53750,0 +1651829880000,36358.70,36391.20,36348.00,36381.30,244.249,1651829939999,8884405.59030,2591,160.626,5842917.13100,0 +1651829940000,36381.30,36400.00,36367.60,36381.40,364.252,1651829999999,13253865.88854,2084,261.319,9508821.26834,0 +1651830000000,36381.40,36416.90,36370.00,36370.00,280.455,1651830059999,10206943.71532,2717,150.929,5493722.79502,0 +1651830060000,36370.00,36370.00,36337.00,36355.50,249.851,1651830119999,9083003.88540,2613,90.197,3278939.99110,0 +1651830120000,36355.50,36357.10,36334.60,36343.90,145.860,1651830179999,5301152.83890,1390,54.476,1979910.45420,0 +1651830180000,36343.80,36346.40,36310.40,36319.20,191.601,1651830239999,6959960.52920,2130,69.260,2515853.11870,0 +1651830240000,36319.20,36339.80,36319.20,36339.80,88.589,1651830299999,3218378.44350,1122,50.222,1824497.46510,0 +1651830300000,36339.80,36353.50,36319.80,36349.90,201.370,1651830359999,7316003.55850,1550,126.073,4580410.36820,0 +1651830360000,36350.00,36363.30,36347.90,36363.10,126.280,1651830419999,4590869.77590,1360,88.939,3233336.44560,0 +1651830420000,36363.10,36365.90,36338.00,36341.90,91.895,1651830479999,3340526.50330,976,44.261,1608911.72420,0 +1651830480000,36341.90,36364.60,36341.80,36358.30,103.947,1651830539999,3779075.07620,997,55.255,2008784.87960,0 +1651830540000,36358.40,36365.30,36350.00,36350.80,85.689,1651830599999,3115460.78850,837,49.667,1805811.62780,0 +1651830600000,36350.80,36362.30,36348.10,36353.20,52.836,1651830659999,1920819.69150,700,24.590,893931.46300,0 +1651830660000,36352.60,36358.90,36349.50,36350.80,58.551,1651830719999,2128557.98210,711,30.404,1105271.80250,0 +1651830720000,36350.70,36352.50,36320.00,36342.40,161.355,1651830779999,5862770.15750,1496,72.804,2645073.88050,0 +1651830780000,36342.40,36343.60,36330.60,36342.30,56.077,1651830839999,2037705.87200,805,28.182,1024036.23880,0 +1651830840000,36342.30,36362.40,36336.20,36356.10,66.852,1651830899999,2430017.91330,1081,32.995,1199340.15140,0 +1651830900000,36356.00,36356.10,36338.90,36339.00,81.075,1651830959999,2946830.81450,1034,47.189,1715228.03320,0 +1651830960000,36339.00,36358.30,36338.90,36348.00,92.678,1651831019999,3368672.76580,923,38.947,1415658.41380,0 +1651831020000,36348.00,36369.50,36345.80,36369.40,159.134,1651831079999,5785250.56890,1257,136.114,4948435.79390,0 +1651831080000,36369.40,36388.60,36369.40,36381.90,171.413,1651831139999,6236248.52900,1394,113.525,4130087.46160,0 +1651831140000,36381.90,36389.80,36375.40,36382.40,69.494,1651831199999,2528339.25960,770,45.448,1653514.29370,0 +1651831200000,36382.40,36404.40,36371.80,36397.90,172.352,1651831259999,6271569.48340,1914,91.097,3315019.89390,0 +1651831260000,36397.80,36400.00,36335.60,36335.70,134.476,1651831319999,4891109.83540,1729,38.825,1412513.11900,0 +1651831320000,36335.60,36339.40,36310.00,36333.70,416.395,1651831379999,15126047.75700,2716,122.956,4466927.48340,0 +1651831380000,36333.70,36348.10,36327.10,36329.10,66.512,1651831439999,2416686.55590,954,34.346,1247933.66620,0 +1651831440000,36329.00,36335.90,36300.20,36312.00,153.835,1651831499999,5586546.92120,1479,32.840,1192615.29220,0 +1651831500000,36311.90,36321.60,36297.10,36299.80,151.127,1651831559999,5486857.72690,1755,65.646,2383381.65660,0 +1651831560000,36299.80,36303.90,36262.50,36294.40,577.105,1651831619999,20937298.91680,4474,159.662,5792550.66940,0 +1651831620000,36294.40,36340.60,36294.30,36311.00,185.991,1651831679999,6754973.17930,1847,129.513,4703519.43350,0 +1651831680000,36311.00,36320.90,36305.10,36313.60,72.527,1651831739999,2633700.26040,1000,38.432,1395612.72790,0 +1651831740000,36313.70,36327.70,36313.70,36323.60,49.175,1651831799999,1786142.10670,831,25.327,919919.30880,0 +1651831800000,36323.60,36323.60,36300.00,36313.70,87.975,1651831859999,3194593.40100,1159,33.874,1229999.31400,0 +1651831860000,36314.30,36315.50,36300.00,36314.40,61.089,1651831919999,2217968.40460,860,9.176,333161.32930,0 +1651831920000,36314.50,36329.70,36308.90,36309.00,52.843,1651831979999,1919233.52780,835,23.928,869125.77780,0 +1651831980000,36309.00,36309.00,36256.50,36274.10,169.551,1651832039999,6150140.00920,2111,51.412,1864856.49240,0 +1651832040000,36275.50,36281.10,36250.00,36261.80,140.534,1651832099999,5096346.98165,1730,44.993,1631733.36000,0 +1651832100000,36261.90,36271.10,36254.30,36254.30,75.535,1651832159999,2739050.95500,1209,37.805,1370900.38120,0 +1651832160000,36254.40,36257.30,36230.20,36245.40,167.688,1651832219999,6077203.81470,2212,56.602,2051394.02790,0 +1651832220000,36245.40,36248.80,36234.60,36243.60,86.160,1651832279999,3122676.86020,1017,35.295,1279196.34590,0 +1651832280000,36244.70,36245.30,36210.40,36239.70,209.506,1651832339999,7589720.72050,2042,84.813,3072408.31100,0 +1651832340000,36239.60,36239.70,36221.30,36225.50,130.747,1651832399999,4737298.48490,1225,69.554,2520120.99180,0 +1651832400000,36225.40,36259.60,36218.00,36252.00,137.093,1651832459999,4968832.13360,1872,97.475,3532844.88180,0 +1651832460000,36251.90,36252.00,36238.60,36245.20,73.753,1651832519999,2673191.61920,1141,37.925,1374605.46560,0 +1651832520000,36245.20,36260.70,36242.00,36242.10,93.411,1651832579999,3386369.52840,1102,58.295,2113359.48330,0 +1651832580000,36242.10,36253.50,36240.00,36240.00,35.099,1651832639999,1272200.63020,731,11.889,430939.05170,0 +1651832640000,36240.00,36259.20,36240.00,36242.70,55.578,1651832699999,2014869.67870,854,28.347,1027625.05460,0 +1651832700000,36242.70,36246.50,36236.60,36241.00,73.092,1651832759999,2648883.41130,914,44.499,1612650.05080,0 +1651832760000,36240.90,36255.40,36235.00,36238.20,76.661,1651832819999,2778797.75070,1007,44.513,1613585.13890,0 +1651832820000,36238.30,36244.30,36236.30,36236.30,45.912,1651832879999,1663869.96740,707,16.837,610186.21270,0 +1651832880000,36236.20,36236.20,36222.00,36222.20,100.170,1651832939999,3628934.83650,1084,50.663,1835401.30730,0 +1651832940000,36222.20,36237.90,36200.00,36233.70,231.823,1651832999999,8395249.91810,2114,70.335,2547302.77960,0 +1651833000000,36233.80,36238.70,36201.00,36202.40,99.247,1651833059999,3594345.22350,1457,49.961,1809619.95320,0 +1651833060000,36202.30,36227.90,36200.00,36219.80,255.854,1651833119999,9265064.15170,2117,83.828,3035761.59230,0 +1651833120000,36219.80,36254.30,36219.80,36236.80,138.091,1651833179999,5004665.32800,1471,113.888,4127513.46550,0 +1651833180000,36236.80,36236.90,36203.80,36209.10,134.703,1651833239999,4878286.94530,1468,71.024,2572058.71160,0 +1651833240000,36209.20,36222.00,36200.00,36200.10,66.541,1651833299999,2409157.98600,1176,22.284,806831.93680,0 +1651833300000,36200.10,36216.40,36195.00,36197.40,169.487,1651833359999,6135807.58648,1531,41.978,1519735.38910,0 +1651833360000,36196.90,36216.00,36177.00,36212.50,330.697,1651833419999,11970227.64704,2686,196.940,7129007.90130,0 +1651833420000,36211.10,36215.10,36179.70,36198.90,193.063,1651833479999,6988013.76060,1655,112.631,4076925.13790,0 +1651833480000,36198.90,36213.00,36183.80,36210.20,103.021,1651833539999,3728715.46710,1278,59.039,2136836.05650,0 +1651833540000,36210.10,36211.00,36202.20,36204.60,48.473,1651833599999,1754974.85740,867,22.217,804365.25290,0 +1651833600000,36204.50,36210.10,36170.00,36187.40,163.348,1651833659999,5912061.17691,1855,67.694,2450227.16470,0 +1651833660000,36187.40,36205.00,35959.30,36054.20,2352.556,1651833719999,84888645.00099,16261,644.201,23260238.32320,0 +1651833720000,36056.40,36105.90,36030.80,36105.90,881.407,1651833779999,31790884.27980,7807,444.375,16030240.97840,0 +1651833780000,36105.90,36124.40,36098.20,36121.40,329.711,1651833839999,11905846.05380,2922,242.425,8754222.09600,0 +1651833840000,36121.30,36121.30,36078.30,36080.70,256.831,1651833899999,9270233.08060,2760,113.969,4113764.38050,0 +1651833900000,36082.80,36140.70,36077.90,36135.60,281.899,1651833959999,10181166.38360,3169,194.759,7034050.77740,0 +1651833960000,36135.50,36139.90,36110.00,36114.40,135.449,1651834019999,4893209.69500,2069,75.874,2741049.12850,0 +1651834020000,36114.40,36126.70,36073.80,36109.90,480.006,1651834079999,17327356.04540,2948,159.396,5753243.23530,0 +1651834080000,36109.80,36121.70,36109.40,36118.20,79.334,1651834139999,2865244.61780,984,54.790,1978781.62900,0 +1651834140000,36118.20,36121.60,36092.90,36093.40,131.196,1651834199999,4736828.81980,1507,62.526,2257480.90310,0 +1651834200000,36093.50,36093.50,36005.30,36006.80,680.409,1651834259999,24519179.48790,6069,168.682,6079171.57060,0 +1651834260000,36006.80,36059.60,35903.30,36055.90,1178.534,1651834319999,42395670.60040,10642,489.335,17612446.55659,0 +1651834320000,36055.80,36055.90,36007.50,36011.40,264.928,1651834379999,9546065.41820,3440,124.608,4490114.90450,0 +1651834380000,36011.40,36017.20,35976.00,35992.00,210.546,1651834439999,7579408.88550,2763,93.368,3361184.90970,0 +1651834440000,35991.10,36027.90,35988.60,36013.60,206.756,1651834499999,7444787.95510,2538,125.526,4520003.15780,0 +1651834500000,36012.20,36021.50,35966.10,35971.00,218.172,1651834559999,7852594.09330,2740,97.526,3510315.68090,0 +1651834560000,35971.00,35972.60,35811.00,35870.80,1548.487,1651834619999,55565684.54605,13258,438.554,15736518.81140,0 +1651834620000,35870.80,35916.40,35755.00,35807.70,1520.955,1651834679999,54505408.95396,12350,484.214,17360524.68460,0 +1651834680000,35807.90,35894.00,35781.30,35875.20,747.105,1651834739999,26774992.93099,7213,420.478,15071858.97109,0 +1651834740000,35875.10,35973.60,35862.80,35927.90,703.573,1651834799999,25268161.28160,6570,518.925,18637325.38960,0 +1651834800000,35928.00,35929.00,35822.70,35829.90,472.833,1651834859999,16966457.19339,5650,197.508,7087152.30650,0 +1651834860000,35829.90,35897.50,35824.60,35895.00,577.439,1651834919999,20710893.71280,5803,320.666,11502611.52370,0 +1651834920000,35895.70,36019.40,35895.60,35971.90,1258.570,1651834979999,45278939.33807,9618,800.924,28815086.02827,0 +1651834980000,35972.00,36112.60,35964.50,36045.60,942.930,1651835039999,33995523.33051,8652,630.440,22729181.46021,0 +1651835040000,36045.60,36048.40,35975.00,35987.60,423.372,1651835099999,15245840.52360,4692,151.066,5439501.64140,0 +1651835100000,35987.50,36048.90,35980.00,35999.10,381.255,1651835159999,13728823.04250,3963,201.429,7254747.71640,0 +1651835160000,35999.10,36002.50,35934.30,35959.00,351.744,1651835219999,12648036.52511,4074,126.562,4550935.21880,0 +1651835220000,35959.40,35964.30,35925.00,35951.40,224.784,1651835279999,8079668.44640,3193,101.958,3664876.25340,0 +1651835280000,35951.50,35954.90,35931.10,35931.80,184.429,1651835339999,6628964.62300,2252,92.457,3323144.13670,0 +1651835340000,35931.80,35935.00,35875.20,35882.70,514.062,1651835399999,18454064.94849,4612,116.359,4177243.14610,0 +1651835400000,35882.70,35883.50,35800.10,35816.70,842.464,1651835459999,30191722.34971,7946,223.258,8002323.54630,0 +1651835460000,35816.60,35818.80,35715.10,35810.60,1601.445,1651835519999,57286027.35322,13897,737.900,26402068.90910,0 +1651835520000,35810.70,35810.70,35672.10,35730.40,1813.760,1651835579999,64805530.50390,13176,654.502,23382439.36200,0 +1651835580000,35730.50,35811.50,35729.60,35790.70,779.817,1651835639999,27897775.34730,7514,521.491,18657110.70600,0 +1651835640000,35790.70,35861.30,35762.90,35779.10,716.375,1651835699999,25652378.99933,6979,498.806,17863466.20479,0 +1651835700000,35779.00,35837.10,35764.00,35790.30,415.303,1651835759999,14867600.17180,5160,242.486,8681540.51140,0 +1651835760000,35790.30,35813.80,35759.20,35781.70,506.939,1651835819999,18140943.25250,4817,160.946,5760234.03480,0 +1651835820000,35781.80,35830.00,35781.80,35803.10,249.925,1651835879999,8948384.10940,3702,145.363,5204603.35560,0 +1651835880000,35803.10,35826.40,35770.00,35800.20,418.863,1651835939999,14996309.41190,3773,270.843,9698737.81490,0 +1651835940000,35800.20,35813.70,35787.30,35804.80,145.509,1651835999999,5209431.43970,2093,69.102,2474009.64600,0 +1651836000000,35804.70,35836.70,35761.80,35771.00,329.989,1651836059999,11813733.13050,3290,194.935,6978471.44210,0 +1651836060000,35771.00,35827.80,35762.30,35813.30,329.787,1651836119999,11804635.00680,3264,204.548,7321268.58340,0 +1651836120000,35813.20,35834.20,35781.10,35793.90,149.365,1651836179999,5348507.76420,2368,72.536,2597688.99740,0 +1651836180000,35794.00,35804.30,35773.60,35789.10,244.783,1651836239999,8761105.98410,2342,165.742,5932351.15670,0 +1651836240000,35789.10,35809.20,35773.10,35791.60,180.407,1651836299999,6457559.32910,2398,89.304,3196711.94960,0 +1651836300000,35791.60,35802.70,35732.00,35739.00,333.320,1651836359999,11916168.79590,3816,128.047,4578012.87530,0 +1651836360000,35739.10,35740.50,35666.00,35666.60,1171.627,1651836419999,41817400.06180,11141,364.107,12995819.89640,0 +1651836420000,35666.50,35718.30,35640.00,35646.70,1045.087,1651836479999,37274184.72045,9900,395.308,14103006.73630,0 +1651836480000,35646.70,35699.40,35550.00,35669.80,2350.926,1651836539999,83725375.84487,17101,930.445,33151336.14365,0 +1651836540000,35669.80,35728.00,35669.60,35704.60,639.158,1651836599999,22817096.64712,6375,323.468,11547562.88102,0 +1651836600000,35704.60,35749.40,35672.00,35719.50,500.188,1651836659999,17867512.74710,5334,267.447,9554593.06340,0 +1651836660000,35717.10,35739.50,35624.30,35624.80,437.776,1651836719999,15618157.25520,5185,169.152,6035875.10030,0 +1651836720000,35624.80,35680.40,35600.20,35660.00,487.247,1651836779999,17363732.52900,5345,249.593,8895789.33850,0 +1651836780000,35662.30,35735.60,35658.70,35727.70,545.787,1651836839999,19485845.19340,5083,388.554,13872968.25720,0 +1651836840000,35727.80,35819.90,35727.70,35805.50,917.809,1651836899999,32825669.44104,7316,670.979,23997837.64764,0 +1651836900000,35805.50,35830.00,35767.80,35782.90,533.081,1651836959999,19086066.98590,5965,294.300,10537808.40910,0 +1651836960000,35782.50,35817.20,35770.60,35803.80,370.684,1651837019999,13267670.37950,3555,185.682,6646751.10640,0 +1651837020000,35803.70,35972.40,35799.90,35925.50,1440.671,1651837079999,51712152.83322,10616,1113.519,39969688.71002,0 +1651837080000,35925.50,35972.40,35845.00,35845.10,772.099,1651837139999,27727983.30849,7414,330.211,11861027.40140,0 +1651837140000,35845.10,35892.50,35823.90,35872.50,387.114,1651837199999,13880438.66040,4373,212.029,7603318.31240,0 +1651837200000,35872.40,35935.10,35837.30,35844.70,362.353,1651837259999,13005849.49160,4324,206.863,7425708.38570,0 +1651837260000,35844.90,35879.20,35800.00,35803.10,362.297,1651837319999,12982747.05510,3979,179.291,6426799.11220,0 +1651837320000,35803.10,35818.30,35764.30,35800.00,401.209,1651837379999,14358651.96740,4317,184.678,6609576.25340,0 +1651837380000,35799.90,35815.00,35780.00,35810.00,434.489,1651837439999,15553337.30880,3181,166.748,5969594.00770,0 +1651837440000,35809.90,35810.00,35781.70,35785.90,148.466,1651837499999,5314190.86250,1701,88.146,3155200.73900,0 +1651837500000,35786.00,35809.00,35769.10,35805.00,331.943,1651837559999,11880380.37130,2600,202.289,7240639.90010,0 +1651837560000,35805.10,35815.00,35796.70,35809.90,261.687,1651837619999,9371340.12280,2179,202.379,7247629.91950,0 +1651837620000,35810.00,35810.00,35779.90,35808.40,163.761,1651837679999,5861542.89560,1802,70.622,2527600.24610,0 +1651837680000,35808.50,35823.00,35803.60,35823.00,297.226,1651837739999,10645451.66790,1742,240.868,8627009.26350,0 +1651837740000,35822.90,35858.60,35811.30,35811.30,816.864,1651837799999,29269622.37140,4334,615.474,22053173.13260,0 +1651837800000,35811.30,35833.50,35811.00,35830.80,155.100,1651837859999,5556466.39100,1227,119.979,4298303.65080,0 +1651837860000,35830.70,35850.00,35825.10,35849.90,198.498,1651837919999,7113677.13580,1824,79.094,2834597.58890,0 +1651837920000,35850.00,35875.70,35835.60,35865.10,192.773,1651837979999,6912858.22180,2132,117.672,4219862.19930,0 +1651837980000,35865.10,35865.10,35773.70,35786.40,401.874,1651838039999,14396567.07790,3274,73.346,2627883.20710,0 +1651838040000,35786.50,35795.80,35751.00,35770.60,355.283,1651838099999,12709560.76138,3536,185.640,6641343.06390,0 +1651838100000,35770.70,35819.50,35765.00,35806.10,285.081,1651838159999,10205269.11660,2561,119.284,4269952.32420,0 +1651838160000,35806.10,35841.40,35806.10,35818.80,189.800,1651838219999,6799059.86440,2168,118.564,4247108.38460,0 +1651838220000,35818.80,35822.20,35767.80,35776.40,191.639,1651838279999,6860020.49520,2213,43.045,1540915.61010,0 +1651838280000,35776.00,35779.10,35750.10,35779.10,124.984,1651838339999,4470127.46590,1977,50.517,1806959.42070,0 +1651838340000,35779.00,35797.30,35762.90,35766.00,139.143,1651838399999,4978663.68520,1799,66.319,2373082.89640,0 +1651838400000,35765.90,35817.30,35758.00,35803.80,244.953,1651838459999,8766333.15970,3278,149.399,5346926.15370,0 +1651838460000,35803.80,35850.00,35803.70,35841.30,342.160,1651838519999,12261120.69910,3221,235.954,8455386.42610,0 +1651838520000,35841.30,35888.00,35829.50,35880.30,322.612,1651838579999,11570376.11849,3747,190.304,6825218.06099,0 +1651838580000,35880.30,35891.50,35854.90,35860.10,355.331,1651838639999,12746784.85540,3526,153.890,5520534.75620,0 +1651838640000,35860.00,35896.10,35850.60,35871.20,314.828,1651838699999,11292705.29260,2827,137.995,4950810.31000,0 +1651838700000,35871.10,35884.00,35812.90,35814.00,131.992,1651838759999,4731518.92110,2391,52.524,1883031.96010,0 +1651838760000,35813.90,35847.90,35800.80,35803.80,149.508,1651838819999,5355402.24970,2070,65.306,2339388.35910,0 +1651838820000,35803.70,35827.60,35800.00,35804.30,141.389,1651838879999,5063853.76140,2020,60.335,2161027.37070,0 +1651838880000,35804.20,35829.50,35788.30,35804.70,178.391,1651838939999,6388003.44190,2017,106.609,3817766.88710,0 +1651838940000,35804.70,35817.40,35786.70,35789.30,114.541,1651838999999,4100664.74510,1668,50.969,1824794.01380,0 +1651839000000,35789.30,35800.00,35780.00,35785.90,88.542,1651839059999,3169000.11250,1486,35.043,1254250.40330,0 +1651839060000,35786.00,35806.20,35754.20,35765.40,312.910,1651839119999,11194270.86624,2818,79.370,2839868.53290,0 +1651839120000,35766.60,35772.90,35712.00,35717.00,507.792,1651839179999,18146431.62582,4817,152.869,5463047.21880,0 +1651839180000,35717.00,35732.20,35679.90,35697.30,571.492,1651839239999,20404172.91170,4872,155.612,5557049.37580,0 +1651839240000,35697.30,35743.00,35685.60,35694.70,465.982,1651839299999,16640472.21480,4003,244.804,8743297.05850,0 +1651839300000,35694.60,35733.30,35690.20,35694.70,180.809,1651839359999,6457494.37530,2558,86.664,3095094.25580,0 +1651839360000,35694.70,35709.90,35680.00,35701.60,460.300,1651839419999,16431135.40930,3679,329.986,11780001.91750,0 +1651839420000,35701.60,35701.70,35671.30,35686.10,200.621,1651839479999,7158481.32510,2638,92.982,3317782.63770,0 +1651839480000,35686.00,35719.30,35665.90,35677.50,419.760,1651839539999,14983125.73960,3563,232.389,8297230.85950,0 +1651839540000,35677.60,35703.50,35674.00,35687.10,147.227,1651839599999,5254657.08860,2166,93.457,3335711.66850,0 +1651839600000,35687.20,35730.00,35687.10,35706.50,218.932,1651839659999,7819809.92440,2810,141.826,5065719.96900,0 +1651839660000,35706.40,35793.20,35706.40,35778.30,385.688,1651839719999,13794837.74490,3823,254.193,9091244.83960,0 +1651839720000,35778.30,35870.00,35778.20,35844.30,842.630,1651839779999,30198667.96260,6651,627.119,22474161.11240,0 +1651839780000,35844.30,35866.00,35827.70,35847.30,396.637,1651839839999,14218871.11340,3233,153.043,5486189.15570,0 +1651839840000,35847.30,35868.00,35834.70,35863.70,256.738,1651839899999,9205178.11570,2821,123.686,4434724.80790,0 +1651839900000,35863.60,35947.90,35845.00,35899.90,942.275,1651839959999,33836577.33959,7628,676.376,24288052.02839,0 +1651839960000,35899.90,35903.60,35824.60,35842.50,451.169,1651840019999,16182017.45070,4389,165.125,5924117.23240,0 +1651840020000,35842.50,35899.90,35820.00,35899.00,301.313,1651840079999,10803909.22140,3270,162.104,5812515.39340,0 +1651840080000,35899.00,36066.20,35891.20,35990.70,1887.676,1651840139999,67968068.72445,15463,1300.115,46813696.24085,0 +1651840140000,35990.80,36023.30,35929.20,35967.80,632.154,1651840199999,22744321.06271,6375,366.294,13180337.82710,0 +1651840200000,35967.90,36288.00,35967.90,36170.70,3908.047,1651840259999,141367719.68770,28457,2576.997,93213932.82510,0 +1651840260000,36170.80,36233.90,36029.00,36115.80,1857.556,1651840319999,67140198.79436,15674,878.095,31741631.70570,0 +1651840320000,36116.70,36238.70,36050.40,36197.70,1227.313,1651840379999,44348838.96503,11139,665.257,24043953.88443,0 +1651840380000,36197.70,36235.20,36140.30,36155.40,881.201,1651840439999,31886723.56980,8176,543.199,19658152.78680,0 +1651840440000,36159.20,36196.90,36136.50,36183.80,433.678,1651840499999,15685788.14790,4666,171.121,6189994.16900,0 +1651840500000,36183.70,36220.00,36158.00,36182.80,745.938,1651840559999,26988480.64720,5901,295.239,10683418.85840,0 +1651840560000,36182.70,36327.00,36182.70,36310.90,1638.654,1651840619999,59429120.16433,11893,1075.824,39021433.78863,0 +1651840620000,36311.00,36331.10,36230.50,36250.00,911.306,1651840679999,33058568.02740,8185,373.784,13560152.72750,0 +1651840680000,36250.10,36266.70,36235.00,36254.90,378.354,1651840739999,13715643.34720,4366,178.095,6455827.58220,0 +1651840740000,36255.00,36267.00,36185.60,36193.90,543.613,1651840799999,19691848.37560,5197,177.872,6444590.56470,0 +1651840800000,36193.90,36193.90,36106.90,36122.70,767.574,1651840859999,27749387.60618,7245,266.826,9646080.07590,0 +1651840860000,36122.70,36122.70,36050.00,36111.80,947.088,1651840919999,34171935.37553,8330,349.615,12616390.84980,0 +1651840920000,36111.80,36127.80,36044.50,36127.50,631.410,1651840979999,22782918.14390,6532,309.262,11160778.45220,0 +1651840980000,36126.30,36165.80,36069.60,36080.70,640.583,1651841039999,23135959.99674,5643,344.075,12428852.08924,0 +1651841040000,36080.60,36081.50,36052.90,36068.00,227.702,1651841099999,8212768.98349,2828,86.700,3127090.42520,0 +1651841100000,36068.00,36089.10,36050.00,36054.10,280.743,1651841159999,10124743.43150,3506,104.182,3757663.18070,0 +1651841160000,36054.00,36055.00,35895.70,35901.80,1474.260,1651841219999,53016917.96694,12196,410.981,14775942.38740,0 +1651841220000,35902.00,35960.50,35888.00,35894.20,719.385,1651841279999,25839221.84580,6812,399.628,14355564.00270,0 +1651841280000,35894.30,35979.00,35894.20,35941.60,739.164,1651841339999,26568640.72730,5761,496.571,17848982.07840,0 +1651841340000,35941.60,35959.40,35832.50,35863.30,1234.169,1651841399999,44283187.57183,8862,438.861,15747490.91390,0 +1651841400000,35863.40,35917.80,35850.00,35873.00,359.333,1651841459999,12894727.79660,4678,208.700,7489451.68140,0 +1651841460000,35873.10,35940.70,35873.00,35924.20,440.037,1651841519999,15801179.64750,3760,304.478,10932996.83090,0 +1651841520000,35924.20,35927.70,35875.00,35879.30,312.254,1651841579999,11207637.79860,3446,141.674,5084958.87470,0 +1651841580000,35879.30,35916.20,35753.40,35787.70,1122.215,1651841639999,40205894.58483,8506,220.156,7891547.42850,0 +1651841640000,35787.80,35866.70,35750.10,35845.40,796.147,1651841699999,28509309.99339,7684,529.583,18968461.14739,0 +1651841700000,35845.50,35877.00,35750.70,35792.30,474.798,1651841759999,17011144.73794,5247,213.507,7653264.14190,0 +1651841760000,35791.20,35835.40,35780.30,35799.20,264.754,1651841819999,9478589.67090,3563,135.783,4861381.07240,0 +1651841820000,35799.20,35864.00,35799.10,35852.70,373.053,1651841879999,13370123.14039,3388,278.356,9976404.37309,0 +1651841880000,35852.70,35920.40,35843.10,35878.70,536.379,1651841939999,19248174.36593,5282,342.087,12276405.76233,0 +1651841940000,35878.70,35890.10,35800.00,35810.20,566.321,1651841999999,20293993.24130,4807,138.651,4969579.16510,0 +1651842000000,35813.50,35898.20,35806.80,35851.50,583.359,1651842059999,20919910.44940,4751,375.800,13477073.13630,0 +1651842060000,35851.60,35856.00,35800.00,35823.70,349.364,1651842119999,12516566.55610,3660,106.235,3805722.52500,0 +1651842120000,35823.40,35824.70,35771.00,35777.10,734.745,1651842179999,26299775.28270,4692,428.162,15325849.76680,0 +1651842180000,35777.00,35809.50,35760.00,35795.90,536.947,1651842239999,19210459.08102,3853,152.481,5456157.81640,0 +1651842240000,35795.90,35800.00,35704.10,35707.20,657.386,1651842299999,23498964.24360,5898,168.893,6037853.73210,0 +1651842300000,35707.30,35820.00,35693.50,35813.10,820.477,1651842359999,29335418.70090,7560,488.720,17478190.74990,0 +1651842360000,35813.00,35823.30,35777.60,35823.30,236.368,1651842419999,8461663.92780,2881,147.318,5273895.59710,0 +1651842420000,35823.30,35863.00,35792.20,35809.30,422.122,1651842479999,15122099.18309,3752,216.474,7756226.60169,0 +1651842480000,35809.20,35809.80,35780.30,35787.00,169.197,1651842539999,6056449.07630,2320,56.019,2005196.23710,0 +1651842540000,35787.00,35795.30,35780.90,35795.30,94.516,1651842599999,3382712.01740,1520,60.372,2160680.35880,0 +1651842600000,35795.30,35870.50,35792.50,35844.20,401.137,1651842659999,14377324.22111,3703,308.365,11052167.65301,0 +1651842660000,35844.10,35857.60,35808.00,35841.10,194.758,1651842719999,6979136.09820,2359,110.274,3951450.96630,0 +1651842720000,35840.70,35958.70,35840.00,35939.60,934.564,1651842779999,33561607.36870,7721,715.350,25690050.04250,0 +1651842780000,35939.60,36000.00,35932.40,35936.70,642.389,1651842839999,23105467.88864,6770,400.813,14417336.54044,0 +1651842840000,35936.10,35966.60,35906.80,35913.30,284.339,1651842899999,10217767.04580,3412,141.683,5091832.93250,0 +1651842900000,35913.30,35946.10,35863.40,35889.90,347.392,1651842959999,12466647.43470,3828,132.318,4748923.68720,0 +1651842960000,35890.00,35921.70,35877.20,35921.60,208.493,1651843019999,7485213.15950,2097,141.479,5079415.08130,0 +1651843020000,35921.60,35938.00,35910.80,35927.30,148.148,1651843079999,5322538.98740,1770,55.612,1997896.57190,0 +1651843080000,35927.30,35927.40,35882.10,35917.10,162.672,1651843139999,5840730.66800,2146,61.866,2221114.04710,0 +1651843140000,35917.10,36023.80,35905.70,35987.90,602.978,1651843199999,21694599.26217,4595,360.309,12962508.46357,0 +1651843200000,35987.90,36008.50,35921.90,35946.70,222.310,1651843259999,7997027.98090,3069,85.432,3073342.48610,0 +1651843260000,35946.80,36016.40,35929.40,36007.70,264.984,1651843319999,9536567.67750,3018,171.991,6190177.02420,0 +1651843320000,36007.70,36020.60,35972.80,35977.40,283.614,1651843379999,10211143.32240,3255,138.452,4985266.17220,0 +1651843380000,35977.40,35984.90,35915.60,35960.80,273.201,1651843439999,9820110.15230,3055,78.394,2818689.61300,0 +1651843440000,35960.80,35966.70,35925.60,35946.30,283.371,1651843499999,10185787.54490,2322,91.192,3278146.84190,0 +1651843500000,35946.20,35978.40,35940.00,35952.40,208.425,1651843559999,7494781.48120,2340,151.021,5430333.98490,0 +1651843560000,35952.40,35987.90,35933.60,35980.10,149.145,1651843619999,5362770.73580,1652,92.964,3343061.56430,0 +1651843620000,35980.10,35984.60,35959.00,35973.00,117.565,1651843679999,4229169.94380,1717,60.697,2183449.36530,0 +1651843680000,35973.00,36009.30,35970.50,35994.70,304.054,1651843739999,10944022.28380,2883,242.378,8723778.14230,0 +1651843740000,35994.70,35999.20,35975.20,35982.30,207.780,1651843799999,7477788.62700,2343,67.880,2442989.52620,0 +1651843800000,35983.00,36054.40,35898.10,35903.30,1425.496,1651843859999,51299405.84620,10471,653.651,23529960.33917,0 +1651843860000,35901.20,35988.70,35846.80,35988.60,1360.956,1651843919999,48865874.69387,10857,615.690,22112979.85411,0 +1651843920000,35986.20,35986.30,35766.10,35774.00,1047.487,1651843979999,37568367.14441,9168,348.716,12513677.93310,0 +1651843980000,35774.00,35794.20,35642.70,35642.80,2166.672,1651844039999,77361989.95093,16354,835.015,29816847.44910,0 +1651844040000,35642.80,35728.00,35633.00,35650.70,1301.872,1651844099999,46446142.68124,12589,595.074,21232748.04930,0 +1651844100000,35650.70,35655.70,35455.00,35480.30,3674.586,1651844159999,130597669.40434,29543,1085.117,38577163.68320,0 +1651844160000,35481.90,35526.20,35243.90,35307.70,7105.874,1651844219999,251287073.38066,54078,2839.091,100426018.35157,0 +1651844220000,35307.70,35466.40,35200.00,35434.50,3772.999,1651844279999,133309348.49555,30590,1939.540,68568861.66645,0 +1651844280000,35433.00,35445.40,35319.90,35375.50,1402.968,1651844339999,49645611.65440,13121,740.486,26206086.11050,0 +1651844340000,35375.50,35410.00,35319.50,35396.60,1300.202,1651844399999,45973878.81230,10990,717.260,25361633.27460,0 +1651844400000,35396.60,35516.50,35378.30,35513.50,1593.788,1651844459999,56490437.85910,13974,1016.979,36048302.10820,0 +1651844460000,35513.60,35841.80,35513.60,35787.80,4367.423,1651844519999,155923624.05818,30917,2649.267,94563631.86488,0 +1651844520000,35787.90,35803.40,35659.90,35695.80,2121.387,1651844579999,75826548.62625,16713,1090.275,38978318.50440,0 +1651844580000,35695.70,35695.80,35530.00,35578.30,1881.774,1651844639999,66974066.80277,15140,773.499,27528374.68280,0 +1651844640000,35578.20,35586.80,35450.00,35487.40,1810.548,1651844699999,64261444.29880,14533,865.924,30735692.64300,0 +1651844700000,35482.70,35605.60,35418.60,35502.40,2197.218,1651844759999,78008138.48816,16123,1031.501,36639531.85103,0 +1651844760000,35502.40,35510.80,35385.00,35385.00,1196.516,1651844819999,42417865.08120,11050,572.552,20299933.34000,0 +1651844820000,35385.00,35465.90,35303.40,35366.80,1806.289,1651844879999,63914131.18223,16299,974.956,34504962.58570,0 +1651844880000,35366.80,35488.90,35354.60,35449.90,1233.361,1651844939999,43706377.97787,11023,604.860,21434587.67407,0 +1651844940000,35450.00,35649.70,35449.90,35584.40,1927.559,1651844999999,68532943.91304,15955,1295.116,46050363.84834,0 +1651845000000,35584.50,35712.70,35556.10,35582.50,1676.223,1651845059999,59719166.87360,14384,855.181,30475527.67497,0 +1651845060000,35582.10,35633.10,35508.00,35566.30,974.054,1651845119999,34643681.87230,9380,451.207,16049558.05300,0 +1651845120000,35567.10,35585.00,35475.30,35527.90,1115.189,1651845179999,39624478.88498,8918,454.632,16155548.26980,0 +1651845180000,35527.90,35610.40,35507.10,35576.00,1031.979,1651845239999,36688173.88230,7888,540.731,19224858.26190,0 +1651845240000,35576.00,35645.30,35561.10,35621.60,863.997,1651845299999,30764037.38953,8361,567.078,20192925.56983,0 +1651845300000,35621.90,35697.50,35586.70,35597.40,1071.396,1651845359999,38180885.91655,9074,505.346,18014642.90515,0 +1651845360000,35598.80,35741.20,35598.70,35623.90,1146.799,1651845419999,40922765.66270,9713,738.366,26352580.42870,0 +1651845420000,35623.80,35687.30,35615.40,35687.30,459.621,1651845479999,16386165.73790,5281,203.222,7246130.16000,0 +1651845480000,35687.90,35728.10,35654.50,35707.40,465.734,1651845539999,16618004.37440,5574,267.938,9561326.94240,0 +1651845540000,35708.30,35763.60,35688.40,35727.10,771.361,1651845599999,27555568.28186,7575,389.588,13917857.56636,0 +1651845600000,35728.80,35814.80,35714.90,35803.40,1099.456,1651845659999,39332408.09319,10224,777.928,27832959.05449,0 +1651845660000,35804.50,35955.00,35790.10,35835.50,1720.961,1651845719999,61738571.31705,16572,1047.523,37582471.37265,0 +1651845720000,35835.50,35835.50,35607.20,35616.90,1655.158,1651845779999,59101984.51710,15064,407.011,14536083.25730,0 +1651845780000,35616.90,35813.50,35606.70,35766.40,1949.970,1651845839999,69663668.92232,12036,998.729,35675510.76192,0 +1651845840000,35766.50,35830.00,35756.50,35812.60,660.508,1651845899999,23642648.13240,6598,391.456,14012926.53460,0 +1651845900000,35812.60,35950.00,35811.90,35924.50,1442.433,1651845959999,51789093.58795,12855,911.477,32722399.84235,0 +1651845960000,35924.60,35970.00,35863.50,35902.60,1023.808,1651846019999,36775474.97450,9074,605.911,21765510.51640,0 +1651846020000,35902.70,36077.10,35901.60,35952.90,1638.154,1651846079999,58959585.08280,14055,1088.227,39172688.83319,0 +1651846080000,35952.80,35958.00,35810.00,35830.50,1104.810,1651846139999,39639015.32659,10927,328.692,11793121.35140,0 +1651846140000,35831.50,35860.00,35791.20,35797.10,761.477,1651846199999,27281385.53173,8186,327.626,11739100.65430,0 +1651846200000,35797.20,35850.00,35704.10,35731.90,1385.808,1651846259999,49554587.85902,10612,478.897,17129774.66570,0 +1651846260000,35732.00,35862.00,35709.80,35834.00,841.818,1651846319999,30139565.29559,8422,538.635,19287212.53009,0 +1651846320000,35834.00,35924.40,35817.70,35882.70,879.439,1651846379999,31541573.77570,7748,478.499,17163953.38680,0 +1651846380000,35882.70,35967.60,35865.80,35952.10,1045.446,1651846439999,37548218.29761,9209,622.417,22357077.04271,0 +1651846440000,35952.10,35968.20,35861.10,35894.80,916.664,1651846499999,32932730.26150,8529,450.235,16175919.09610,0 +1651846500000,35894.80,35958.60,35873.30,35957.90,628.029,1651846559999,22560012.71720,5616,279.803,10051325.20460,0 +1651846560000,35958.00,35984.20,35911.50,35922.00,673.240,1651846619999,24202372.19210,5986,352.709,12680491.38430,0 +1651846620000,35922.10,36071.80,35912.50,36069.70,1304.012,1651846679999,46964817.59902,10498,1018.359,36677353.01572,0 +1651846680000,36069.70,36099.80,36001.60,36099.80,1022.014,1651846739999,36834143.96102,8805,515.988,18599331.29532,0 +1651846740000,36099.80,36182.00,36018.20,36028.90,1894.444,1651846799999,68426166.11921,17267,1011.063,36528745.15661,0 +1651846800000,36029.10,36054.70,35959.50,35971.30,909.851,1651846859999,32754752.72878,9581,327.528,11792124.14850,0 +1651846860000,35971.30,35983.60,35900.20,35940.10,859.485,1651846919999,30884613.13057,7829,283.492,10187793.25550,0 +1651846920000,35940.00,36071.80,35937.10,36035.00,809.867,1651846979999,29174108.11189,8306,491.369,17699801.46519,0 +1651846980000,36035.10,36035.10,35922.00,36016.10,754.779,1651847039999,27152319.21711,7899,295.530,10631426.21710,0 +1651847040000,36016.40,36043.70,35970.00,35994.00,564.461,1651847099999,20320750.36170,5774,282.125,10157909.80140,0 +1651847100000,35995.70,36048.00,35975.80,35986.20,732.900,1651847159999,26385442.51550,5533,325.218,11709118.88000,0 +1651847160000,35986.30,36063.10,35973.40,35994.30,572.071,1651847219999,20603045.41249,5468,268.952,9687823.38779,0 +1651847220000,35994.90,36010.70,35938.00,35938.10,446.282,1651847279999,16055733.64971,4968,173.613,6245996.92220,0 +1651847280000,35938.00,35974.90,35893.00,35893.00,489.911,1651847339999,17604385.69944,5828,201.246,7232960.83580,0 +1651847340000,35893.10,35924.20,35785.50,35800.00,1455.037,1651847399999,52181571.36434,11511,435.910,15639382.47460,0 +1651847400000,35802.40,35899.20,35773.70,35817.90,1178.823,1651847459999,42230717.16156,9737,490.568,17577741.82260,0 +1651847460000,35817.80,35934.10,35804.10,35892.90,701.082,1651847519999,25135145.94000,6545,471.651,16910655.46100,0 +1651847520000,35893.50,35944.50,35830.60,35831.20,609.972,1651847579999,21889457.91000,6130,288.257,10345807.88550,0 +1651847580000,35831.10,35854.50,35728.50,35754.70,883.502,1651847639999,31611810.04968,8438,274.219,9811213.69520,0 +1651847640000,35753.20,35797.90,35726.40,35794.80,1066.845,1651847699999,38152155.44560,7316,585.491,20939480.73640,0 +1651847700000,35794.70,35824.70,35739.50,35786.50,823.081,1651847759999,29448423.00300,7704,416.396,14899677.65940,0 +1651847760000,35787.50,35848.30,35768.10,35776.00,628.924,1651847819999,22524669.02200,6143,260.318,9323456.93080,0 +1651847820000,35775.90,35790.10,35736.30,35764.80,311.608,1651847879999,11144097.96340,4138,147.833,5287356.04270,0 +1651847880000,35764.90,35819.30,35737.50,35799.30,512.975,1651847939999,18353312.94140,4479,229.809,8222306.71770,0 +1651847940000,35798.30,35872.70,35798.20,35854.10,678.020,1651847999999,24299363.23029,5265,423.965,15194257.42119,0 +1651848000000,35849.90,35946.40,35817.30,35940.40,1009.693,1651848059999,36239684.63569,7811,684.673,24572137.89659,0 +1651848060000,35939.10,35972.40,35883.80,35919.30,800.093,1651848119999,28747340.54051,6641,496.036,17824759.96281,0 +1651848120000,35919.40,35919.40,35851.90,35895.50,319.588,1651848179999,11468040.41909,3836,124.679,4473679.74260,0 +1651848180000,35895.60,35960.00,35876.70,35940.80,254.566,1651848239999,9145057.32691,3738,146.982,5280898.88911,0 +1651848240000,35940.40,35972.40,35900.00,35902.50,431.869,1651848299999,15516367.17930,3818,198.957,7150199.95460,0 +1651848300000,35902.00,36070.20,35873.10,36032.60,1284.404,1651848359999,46224329.11282,10894,851.852,30663889.36512,0 +1651848360000,36032.50,36140.00,35990.20,36114.00,2071.367,1651848419999,74733764.43308,12784,1197.386,43199820.65258,0 +1651848420000,36114.10,36144.00,36060.50,36075.20,1417.053,1651848479999,51159982.74040,10166,790.211,28533889.07730,0 +1651848480000,36075.30,36107.60,36026.10,36059.90,734.044,1651848539999,26477929.92340,6651,262.643,9474022.26800,0 +1651848540000,36059.90,36110.00,36039.90,36103.20,440.897,1651848599999,15905823.96540,4784,224.288,8091659.41300,0 +1651848600000,36103.20,36162.80,36026.60,36070.50,854.538,1651848659999,30850975.26556,7873,494.980,17876372.31557,0 +1651848660000,36070.50,36077.90,36015.50,36044.40,569.266,1651848719999,20518955.49370,5140,243.223,8767106.34650,0 +1651848720000,36044.30,36070.80,35994.90,36009.00,481.821,1651848779999,17357487.36078,4866,170.266,6134426.96030,0 +1651848780000,36009.00,36076.30,36004.00,36055.00,389.485,1651848839999,14035170.93900,4229,250.430,9024431.35600,0 +1651848840000,36054.90,36117.20,36040.00,36102.90,451.484,1651848899999,16286543.16200,4559,273.484,9866289.37120,0 +1651848900000,36102.80,36103.00,36050.50,36072.40,308.579,1651848959999,11132561.71700,4166,142.936,5156464.25600,0 +1651848960000,36071.90,36105.50,36057.00,36068.40,447.458,1651849019999,16145418.63270,4292,188.921,6817495.23650,0 +1651849020000,36068.50,36133.00,36049.20,36122.30,478.304,1651849079999,17267562.76720,4491,255.487,9225523.00880,0 +1651849080000,36122.30,36158.50,36105.20,36150.70,473.630,1651849139999,17113502.83037,4716,310.589,11223029.13247,0 +1651849140000,36149.90,36241.80,36121.40,36125.80,1514.544,1651849199999,54808500.48970,12109,970.134,35112088.65730,0 +1651849200000,36125.80,36181.20,36099.60,36169.70,804.043,1651849259999,29047732.15180,6115,392.595,14185060.83130,0 +1651849260000,36168.40,36199.90,36131.00,36134.10,595.033,1651849319999,21520992.73740,5299,295.977,10706173.26130,0 +1651849320000,36134.20,36150.70,36094.40,36107.70,592.788,1651849379999,21410004.07050,5093,206.585,7462572.67660,0 +1651849380000,36107.60,36113.20,36050.70,36074.20,538.522,1651849439999,19429921.09949,5379,183.056,6605142.30160,0 +1651849440000,36074.20,36117.30,36072.40,36088.90,239.051,1651849499999,8628717.68760,3085,117.375,4236695.31150,0 +1651849500000,36088.80,36135.90,36069.30,36098.90,325.886,1651849559999,11764692.46750,3945,132.207,4772925.54390,0 +1651849560000,36098.80,36140.00,36079.50,36139.90,280.115,1651849619999,10113919.94890,2914,151.565,5472942.97970,0 +1651849620000,36140.00,36146.10,36070.90,36082.20,311.557,1651849679999,11249121.95570,3324,112.615,4066199.12830,0 +1651849680000,36084.20,36190.00,36083.90,36181.50,393.924,1651849739999,14232685.84644,3547,294.826,10652858.54034,0 +1651849740000,36181.40,36228.40,36172.00,36188.80,673.693,1651849799999,24385772.18218,5900,388.195,14052613.28608,0 +1651849800000,36188.70,36194.30,36090.10,36091.10,382.425,1651849859999,13817825.57510,4590,112.743,4074090.78920,0 +1651849860000,36093.80,36113.00,36085.00,36103.70,206.666,1651849919999,7460071.20850,2701,80.265,2897450.95060,0 +1651849920000,36103.60,36144.30,36080.00,36132.40,336.302,1651849979999,12145208.30310,3775,201.702,7284965.63380,0 +1651849980000,36132.40,36162.20,36114.80,36152.60,202.458,1651850039999,7318008.80900,2902,119.865,4332820.15080,0 +1651850040000,36152.60,36189.00,36144.00,36188.50,341.169,1651850099999,12339385.45980,3565,121.415,4391616.83070,0 +1651850100000,36188.60,36229.00,36150.00,36221.50,712.570,1651850159999,25794613.94870,5523,341.355,12358013.48890,0 +1651850160000,36221.40,36232.30,36148.90,36169.30,399.405,1651850219999,14453062.39820,4654,147.239,5329069.25550,0 +1651850220000,36169.30,36215.70,36152.00,36210.10,243.661,1651850279999,8817997.91500,3386,162.832,5892911.92150,0 +1651850280000,36210.00,36223.70,36166.00,36184.60,494.031,1651850339999,17887153.99270,4630,296.086,10721087.24880,0 +1651850340000,36184.60,36222.00,36166.00,36194.70,440.662,1651850399999,15948715.63550,3691,155.360,5624171.61180,0 +1651850400000,36195.70,36214.70,36153.90,36207.80,287.417,1651850459999,10399081.28580,3208,118.634,4292909.64030,0 +1651850460000,36208.40,36296.80,36200.80,36285.40,1271.793,1651850519999,46114706.82004,10522,797.986,28937125.76464,0 +1651850520000,36285.30,36341.90,36256.50,36289.40,1536.601,1651850579999,55777785.33392,12103,807.425,29314765.09812,0 +1651850580000,36289.40,36310.00,36258.30,36296.30,559.820,1651850639999,20314838.46190,5635,244.116,8859632.88920,0 +1651850640000,36299.40,36399.80,36289.10,36372.40,1505.838,1651850699999,54745232.85592,12251,915.495,33289049.68112,0 +1651850700000,36372.40,36426.40,36336.00,36397.90,1274.005,1651850759999,46343830.82408,9976,647.259,23549194.56258,0 +1651850760000,36398.00,36412.80,36277.90,36297.80,909.257,1651850819999,33041602.96100,8617,381.455,13864247.00200,0 +1651850820000,36297.70,36349.00,36285.30,36312.80,306.734,1651850879999,11139913.03850,4073,181.456,6590087.73540,0 +1651850880000,36312.70,36355.80,36286.00,36316.10,523.864,1651850939999,19024351.86620,4857,161.990,5883436.22940,0 +1651850940000,36316.10,36321.90,36256.00,36291.50,497.392,1651850999999,18047191.44550,4633,162.041,5879727.70370,0 +1651851000000,36291.60,36310.00,36246.20,36279.30,468.961,1651851059999,17009221.77865,4709,206.409,7487660.97470,0 +1651851060000,36281.40,36314.10,36229.40,36240.20,470.606,1651851119999,17069099.91600,5127,199.579,7240785.12030,0 +1651851120000,36240.20,36246.50,36166.00,36200.10,896.465,1651851179999,32448523.12553,7392,331.997,12017419.11280,0 +1651851180000,36200.10,36219.00,36130.80,36168.20,1029.988,1651851239999,37258346.41367,7147,481.405,17417981.14980,0 +1651851240000,36168.30,36175.30,36102.00,36117.30,711.833,1651851299999,25720576.50228,5719,192.445,6953728.52330,0 +1651851300000,36117.30,36167.50,36100.00,36123.00,810.342,1651851359999,29276674.24770,5796,521.214,18832009.04720,0 +1651851360000,36121.20,36165.40,36085.00,36131.60,440.536,1651851419999,15912728.19999,4532,178.406,6445847.65170,0 +1651851420000,36129.60,36150.00,36076.30,36076.30,393.586,1651851479999,14211185.33180,3328,141.906,5124770.87860,0 +1651851480000,36076.20,36108.00,35972.40,36005.40,1023.755,1651851539999,36893137.89044,9054,349.188,12583739.36370,0 +1651851540000,36005.50,36075.30,36003.50,36019.60,580.541,1651851599999,20923570.99260,5781,305.871,11025322.72220,0 +1651851600000,36019.50,36067.50,36006.70,36046.00,543.536,1651851659999,19585662.63820,4639,294.641,10617153.58130,0 +1651851660000,36046.30,36097.10,36040.20,36073.80,453.907,1651851719999,16374864.96760,3901,333.499,12030843.52900,0 +1651851720000,36072.50,36130.00,36072.40,36105.00,529.190,1651851779999,19107785.41030,4223,415.153,14990914.79940,0 +1651851780000,36105.10,36110.00,36060.00,36097.40,180.241,1651851839999,6504040.00610,2564,92.419,3335165.57760,0 +1651851840000,36097.50,36129.00,36070.00,36071.30,212.205,1651851899999,7660694.32430,2443,108.349,3912050.48810,0 +1651851900000,36071.30,36077.60,36019.20,36039.80,455.140,1651851959999,16403556.49430,4443,209.523,7551321.68190,0 +1651851960000,36040.00,36099.20,36030.00,36041.50,417.363,1651852019999,15051326.35710,4295,213.348,7695180.44130,0 +1651852020000,36041.60,36076.40,36040.00,36076.10,185.212,1651852079999,6676473.19590,2148,131.737,4748921.48530,0 +1651852080000,36076.10,36086.60,36050.30,36062.10,232.631,1651852139999,8391339.46310,2539,155.483,5608741.39740,0 +1651852140000,36062.00,36100.80,36055.70,36089.20,251.365,1651852199999,9070648.17390,2765,164.576,5939052.48010,0 +1651852200000,36088.70,36132.70,36060.00,36077.30,405.914,1651852259999,14652604.54880,3941,235.513,8502832.50800,0 +1651852260000,36077.10,36199.80,36071.60,36165.40,808.068,1651852319999,29213596.41790,6650,605.105,21874230.15800,0 +1651852320000,36165.40,36181.00,36084.60,36095.10,303.174,1651852379999,10955750.24100,3699,122.971,4444825.87760,0 +1651852380000,36095.00,36095.10,35927.90,35966.50,1234.867,1651852439999,44435238.54769,10311,282.525,10164383.02010,0 +1651852440000,35966.50,36014.70,35940.40,36009.60,372.531,1651852499999,13405748.09180,4475,218.644,7868619.18050,0 +1651852500000,36009.60,36040.40,36000.00,36015.70,224.266,1651852559999,8077892.35130,2923,120.673,4346442.97470,0 +1651852560000,36015.90,36047.00,36007.60,36014.90,202.334,1651852619999,7289745.03340,2448,123.043,4433005.89970,0 +1651852620000,36014.90,36047.70,35994.40,36045.70,245.270,1651852679999,8835182.59070,2482,153.000,5511785.85020,0 +1651852680000,36045.60,36045.60,35934.60,35939.90,377.187,1651852739999,13568698.86480,3948,98.304,3535965.68580,0 +1651852740000,35938.20,35990.00,35934.90,35989.10,245.719,1651852799999,8838662.47940,2847,132.772,4775955.11720,0 +1651852800000,35989.10,35997.00,35952.10,35979.00,317.133,1651852859999,11408421.99400,4264,162.162,5834461.92950,0 +1651852860000,35979.90,36059.50,35969.70,36026.50,542.230,1651852919999,19525952.63529,4124,408.477,14708160.15939,0 +1651852920000,36026.40,36097.60,35992.60,36064.50,863.476,1651852979999,31144791.61720,5170,566.112,20420219.12650,0 +1651852980000,36064.50,36096.60,36040.40,36073.30,463.607,1651853039999,16719764.31580,3599,236.899,8543956.78610,0 +1651853040000,36073.20,36097.30,36056.60,36090.00,171.353,1651853099999,6181944.39130,2163,79.287,2860321.37840,0 +1651853100000,36090.00,36134.70,36073.60,36108.00,320.869,1651853159999,11585403.07670,3595,195.035,7041738.45250,0 +1651853160000,36108.10,36179.60,36093.80,36166.10,431.527,1651853219999,15594119.46188,4712,244.881,8849875.76258,0 +1651853220000,36166.00,36192.00,36124.00,36144.40,536.161,1651853279999,19385639.93740,5104,238.966,8641185.75200,0 +1651853280000,36144.30,36152.20,36105.00,36118.30,264.188,1651853339999,9543486.27980,2764,80.446,2906125.52730,0 +1651853340000,36118.30,36118.30,36037.20,36051.40,443.717,1651853399999,16007635.09259,3310,104.923,3785592.45480,0 +1651853400000,36051.90,36130.00,36014.10,36116.60,710.894,1651853459999,25640430.82171,5402,446.431,16103598.24460,0 +1651853460000,36116.60,36116.60,36003.40,36010.60,578.330,1651853519999,20840678.75738,5028,165.807,5975559.99590,0 +1651853520000,36010.70,36031.90,35966.00,36007.60,506.685,1651853579999,18241948.64120,4366,175.989,6336262.14110,0 +1651853580000,36007.20,36007.20,35920.00,35949.50,829.357,1651853639999,29819265.01787,7494,315.873,11358025.38260,0 +1651853640000,35949.40,35971.40,35907.50,35964.10,539.472,1651853699999,19384762.65240,4833,238.916,8586972.77750,0 +1651853700000,35964.10,35968.30,35915.70,35939.90,414.126,1651853759999,14885380.76380,4296,226.401,8138187.99110,0 +1651853760000,35940.00,36012.80,35939.90,35975.00,271.175,1651853819999,9757767.20590,3289,177.824,6398700.23050,0 +1651853820000,35975.20,36021.60,35975.20,35998.70,163.913,1651853879999,5900963.90060,2115,108.826,3917730.73030,0 +1651853880000,35998.70,36040.00,35998.70,36022.50,189.667,1651853939999,6831406.56680,2233,127.761,4601744.91820,0 +1651853940000,36022.50,36111.30,35976.70,35984.20,616.179,1651853999999,22216348.92780,5430,405.561,14624647.83590,0 +1651854000000,35984.20,36080.00,35984.10,36064.10,252.233,1651854059999,9088026.60610,3022,155.880,5616001.78840,0 +1651854060000,36064.10,36100.00,36037.70,36062.50,252.772,1651854119999,9116051.26540,3061,125.764,4535838.94410,0 +1651854120000,36062.50,36130.30,36062.50,36125.00,580.677,1651854179999,20965653.34350,5047,347.323,12541150.08890,0 +1651854180000,36125.10,36160.00,36077.00,36094.00,427.426,1651854239999,15435934.22477,4403,250.278,9039309.65637,0 +1651854240000,36092.30,36122.10,36046.60,36063.60,317.033,1651854299999,11439835.80399,3238,103.673,3741553.02670,0 +1651854300000,36063.60,36153.90,36052.60,36109.10,410.560,1651854359999,14823146.92540,3518,256.217,9250023.59470,0 +1651854360000,36109.00,36122.80,36077.80,36108.20,173.968,1651854419999,6280659.48400,2062,66.175,2388972.52620,0 +1651854420000,36108.30,36108.30,36056.80,36075.50,196.256,1651854479999,7081377.87900,2210,68.784,2481850.58590,0 +1651854480000,36075.50,36103.10,36048.40,36091.20,178.029,1651854539999,6422198.08860,2088,94.317,3402860.12830,0 +1651854540000,36091.10,36129.30,36087.00,36087.10,157.240,1651854599999,5677507.27600,2263,74.532,2691039.37490,0 +1651854600000,36086.70,36105.50,36030.50,36067.10,432.975,1651854659999,15616998.90300,3553,212.372,7659803.20880,0 +1651854660000,36065.80,36132.00,36065.00,36115.10,228.722,1651854719999,8258408.68030,2987,151.723,5478240.22870,0 +1651854720000,36115.00,36133.30,36090.00,36099.90,240.746,1651854779999,8695738.88330,2689,142.870,5160715.32610,0 +1651854780000,36099.90,36129.30,36091.10,36107.20,118.682,1651854839999,4285740.70200,1461,64.102,2314870.90160,0 +1651854840000,36107.30,36171.60,36107.30,36163.60,365.829,1651854899999,13227135.96554,3705,211.829,7658588.31194,0 +1651854900000,36163.60,36180.00,36116.70,36153.60,311.737,1651854959999,11269233.31471,2659,102.964,3722927.94781,0 +1651854960000,36153.80,36181.90,36093.90,36096.70,278.696,1651855019999,10070807.55300,2815,108.974,3938623.66600,0 +1651855020000,36096.80,36098.00,36050.00,36075.10,279.354,1651855079999,10074934.65069,3199,105.842,3817043.83100,0 +1651855080000,36075.00,36111.00,36071.60,36110.60,160.677,1651855139999,5798985.70770,2071,86.080,3106668.24130,0 +1651855140000,36111.10,36169.20,36111.10,36134.40,293.038,1651855199999,10591202.32624,3047,178.532,6451979.75104,0 +1651855200000,36134.40,36144.30,36090.00,36139.40,233.736,1651855259999,8441107.99710,2720,108.654,3924371.46100,0 +1651855260000,36140.80,36155.80,36087.90,36106.30,190.147,1651855319999,6867549.68470,2061,87.944,3176404.52120,0 +1651855320000,36106.40,36120.00,36088.90,36112.60,135.662,1651855379999,4898169.47850,1545,57.361,2071010.35690,0 +1651855380000,36112.50,36129.90,36098.00,36122.40,162.093,1651855439999,5854340.09470,1690,75.229,2717040.86660,0 +1651855440000,36124.60,36131.10,36104.70,36124.00,82.921,1651855499999,2994906.85120,1133,40.697,1469768.78350,0 +1651855500000,36124.10,36149.20,36115.00,36119.60,173.370,1651855559999,6263825.56500,1985,91.996,3323661.44880,0 +1651855560000,36119.70,36127.90,36087.00,36094.00,107.568,1651855619999,3883136.80460,1475,33.355,1204084.33900,0 +1651855620000,36094.00,36104.90,36080.80,36088.80,98.926,1651855679999,3570238.77530,1476,54.118,1953190.98140,0 +1651855680000,36088.90,36126.10,36072.70,36124.30,160.142,1651855739999,5780531.87400,2017,85.690,3093434.73170,0 +1651855740000,36124.30,36130.00,36069.60,36069.60,111.564,1651855799999,4027977.34480,1397,36.523,1318729.59460,0 +1651855800000,36069.60,36078.30,36004.90,36069.30,553.186,1651855859999,19936277.98938,4509,216.414,7799868.61780,0 +1651855860000,36069.20,36081.70,36050.00,36073.10,109.900,1651855919999,3963784.86120,1510,44.970,1622020.79720,0 +1651855920000,36073.20,36166.10,36051.80,36138.00,301.339,1651855979999,10887313.87327,2927,224.063,8096187.66987,0 +1651855980000,36137.10,36234.60,36137.10,36175.50,985.146,1651856039999,35659914.64803,7955,692.333,25063170.00773,0 +1651856040000,36176.50,36195.50,36157.30,36165.70,161.103,1651856099999,5828021.53420,2265,80.178,2900615.46450,0 +1651856100000,36165.60,36175.00,36133.40,36163.40,164.298,1651856159999,5940174.15610,2097,64.979,2349213.41000,0 +1651856160000,36163.50,36222.00,36163.50,36180.20,231.982,1651856219999,8397028.38600,2962,151.123,5470150.50330,0 +1651856220000,36180.30,36200.00,36171.80,36188.60,217.187,1651856279999,7859360.19490,2482,88.651,3207882.48980,0 +1651856280000,36188.60,36215.00,36186.90,36196.20,159.851,1651856339999,5787444.12600,1873,91.394,3308877.22660,0 +1651856340000,36196.10,36222.50,36182.80,36200.80,254.594,1651856399999,9216836.65220,2468,140.992,5104312.38800,0 +1651856400000,36200.70,36211.40,36156.70,36165.50,209.892,1651856459999,7594157.42470,2347,64.589,2336651.04110,0 +1651856460000,36165.40,36185.00,36151.00,36153.20,118.411,1651856519999,4282764.99870,1522,50.535,1827904.16290,0 +1651856520000,36153.30,36182.30,36150.00,36150.00,83.562,1651856579999,3022238.00570,1256,38.172,1380642.28840,0 +1651856580000,36150.00,36172.00,36137.00,36164.80,140.294,1651856639999,5071635.07580,1751,60.948,2203480.82660,0 +1651856640000,36164.80,36225.00,36163.50,36196.10,247.878,1651856699999,8973956.36510,2726,183.751,6652348.82310,0 +1651856700000,36196.00,36196.60,36124.00,36129.20,185.021,1651856759999,6688512.08030,2234,65.359,2362693.64300,0 +1651856760000,36129.30,36137.70,36103.80,36123.30,285.215,1651856819999,10300990.98420,2496,87.424,3157433.78950,0 +1651856820000,36123.30,36140.70,36104.90,36134.80,131.672,1651856879999,4755545.35010,1597,70.819,2557806.20000,0 +1651856880000,36134.70,36174.00,36117.00,36151.50,81.465,1651856939999,2944468.26240,1293,49.046,1772739.03510,0 +1651856940000,36151.50,36178.10,36130.00,36145.10,136.092,1651856999999,4921013.73050,1636,92.317,3338451.99050,0 +1651857000000,36143.70,36151.20,36123.10,36136.40,89.166,1651857059999,3222147.91600,1354,47.450,1714757.20420,0 +1651857060000,36136.50,36151.50,36123.30,36151.10,92.001,1651857119999,3324644.87070,1082,47.152,1703925.31890,0 +1651857120000,36150.60,36165.80,36110.00,36113.10,115.554,1651857179999,4176064.10450,1549,51.545,1862894.67860,0 +1651857180000,36113.00,36115.70,36085.40,36110.70,177.538,1651857239999,6408799.54060,1937,43.580,1573282.91590,0 +1651857240000,36110.80,36110.80,36100.40,36102.30,113.499,1651857299999,4097927.16770,1040,73.733,2662119.69640,0 +1651857300000,36102.30,36127.10,36077.90,36077.90,176.786,1651857359999,6382409.21480,2208,77.457,2796779.34720,0 +1651857360000,36077.20,36094.90,36042.40,36092.70,510.463,1651857419999,18409759.06279,3806,120.874,4360166.77110,0 +1651857420000,36092.60,36107.80,36052.70,36068.30,148.026,1651857479999,5340531.26120,1725,53.761,1939638.14660,0 +1651857480000,36068.30,36068.30,36014.20,36032.90,274.778,1651857539999,9902490.98181,3268,114.983,4143977.86400,0 +1651857540000,36032.90,36062.40,36024.70,36056.60,217.185,1651857599999,7827576.73790,1994,141.527,5101064.34550,0 +1651857600000,36056.60,36056.60,35980.00,36041.40,496.972,1651857659999,17896412.90706,4768,183.017,6591484.33340,0 +1651857660000,36041.50,36092.00,36030.90,36072.90,121.206,1651857719999,4370252.01430,1887,81.354,2933431.72730,0 +1651857720000,36072.90,36072.90,36048.10,36058.00,75.127,1651857779999,2709122.11120,1281,42.438,1530346.16080,0 +1651857780000,36057.60,36154.50,36057.60,36086.20,317.056,1651857839999,11450057.85327,3542,212.895,7688562.04417,0 +1651857840000,36086.20,36113.20,36050.00,36050.10,184.567,1651857899999,6658303.50939,2124,83.133,2999363.51430,0 +1651857900000,36050.00,36082.50,36041.40,36057.30,116.331,1651857959999,4195311.22990,1550,50.002,1803179.96210,0 +1651857960000,36057.30,36057.30,36022.50,36040.60,161.041,1651858019999,5803876.64220,1699,69.359,2499752.04690,0 +1651858020000,36040.50,36091.00,36029.60,36091.00,106.420,1651858079999,3836659.01330,1346,70.047,2525268.23940,0 +1651858080000,36090.90,36121.00,36070.00,36077.70,171.393,1651858139999,6186868.77110,1881,82.286,2970386.76740,0 +1651858140000,36077.80,36083.10,36050.10,36078.40,103.065,1651858199999,3717341.96410,1348,52.989,1911131.71260,0 +1651858200000,36078.40,36099.60,36063.40,36099.50,323.307,1651858259999,11664956.74480,1578,248.062,8950015.55890,0 +1651858260000,36099.60,36109.60,36057.10,36069.60,127.458,1651858319999,4599490.91350,1747,55.977,2020246.50790,0 +1651858320000,36069.50,36069.60,36033.90,36041.40,141.845,1651858379999,5113316.27370,1559,72.701,2620664.66900,0 +1651858380000,36041.30,36053.20,36033.00,36050.60,116.570,1651858439999,4201791.98730,1384,58.423,2105830.80890,0 +1651858440000,36051.40,36063.70,36042.80,36051.90,100.716,1651858499999,3631186.08550,1293,33.568,1210338.92880,0 +1651858500000,36051.90,36086.80,36043.10,36084.60,80.811,1651858559999,2914992.87350,1192,45.958,1657821.98700,0 +1651858560000,36084.60,36106.80,36020.90,36046.00,283.015,1651858619999,10205575.07580,2348,112.221,4048135.25570,0 +1651858620000,36046.60,36050.50,35980.00,36000.10,366.379,1651858679999,13189271.61908,3638,94.179,3390145.14580,0 +1651858680000,36000.00,36021.80,35984.60,36004.40,160.639,1651858739999,5783156.32030,1763,82.851,2982872.85960,0 +1651858740000,36004.50,36022.00,36004.00,36008.20,102.458,1651858799999,3689638.87320,1212,60.575,2181387.15460,0 +1651858800000,36008.10,36083.20,36008.10,36044.50,345.389,1651858859999,12456636.46310,3353,251.173,9058766.68360,0 +1651858860000,36044.20,36063.90,35994.80,36004.10,216.805,1651858919999,7808604.78200,2251,68.408,2464434.88860,0 +1651858920000,36004.00,36022.70,35972.40,36007.20,348.328,1651858979999,12538836.57340,2907,113.679,4092400.59530,0 +1651858980000,36007.20,36049.90,36001.00,36003.00,176.080,1651859039999,6342798.62430,1943,109.627,3949055.46730,0 +1651859040000,36003.10,36018.70,35986.80,36004.70,129.984,1651859099999,4679232.35340,1561,69.887,2515898.46300,0 +1651859100000,36004.20,36035.80,36001.70,36009.40,139.197,1651859159999,5013187.82680,1620,79.917,2878193.56790,0 +1651859160000,36009.40,36017.00,35978.70,35987.50,211.143,1651859219999,7599719.69890,2418,87.910,3164214.75180,0 +1651859220000,35987.50,36020.90,35972.40,36001.00,143.586,1651859279999,5169291.19560,1691,82.533,2971487.41610,0 +1651859280000,36001.00,36023.90,35987.20,35988.40,107.791,1651859339999,3881328.46690,1599,55.934,2014122.42850,0 +1651859340000,35988.30,36014.10,35932.60,35959.90,985.273,1651859399999,35435537.88281,5263,217.130,7808174.68720,0 +1651859400000,35960.00,35968.20,35885.00,35914.90,599.110,1651859459999,21520550.74972,6147,181.649,6525945.15990,0 +1651859460000,35915.00,35945.80,35909.60,35923.20,339.478,1651859519999,12197104.86950,2837,205.981,7400891.27260,0 +1651859520000,35923.90,35933.90,35871.60,35907.70,459.968,1651859579999,16513547.77138,4191,150.074,5388704.85900,0 +1651859580000,35907.80,35907.80,35800.00,35872.30,1431.624,1651859639999,51329498.60515,9249,344.775,12359953.21740,0 +1651859640000,35872.30,35878.90,35820.80,35860.00,559.979,1651859699999,20075712.76670,3959,155.682,5582432.43020,0 +1651859700000,35860.00,35915.30,35846.10,35912.00,296.128,1651859759999,10623828.89780,3109,171.257,6144965.58090,0 +1651859760000,35912.00,35921.50,35868.50,35880.40,286.066,1651859819999,10265659.94850,2911,123.587,4435258.93540,0 +1651859820000,35880.30,35888.90,35863.90,35874.20,96.438,1651859879999,3459707.60200,1808,42.506,1524918.43760,0 +1651859880000,35874.20,35889.90,35870.40,35881.50,130.604,1651859939999,4686129.87130,1571,70.286,2521968.42500,0 +1651859940000,35881.90,35894.50,35850.00,35872.20,238.713,1651859999999,8562492.66630,2241,71.162,2553059.17440,0 +1651860000000,35872.20,35891.20,35845.30,35890.00,196.364,1651860059999,7043594.55120,2535,93.975,3371245.45340,0 +1651860060000,35890.10,35900.00,35878.90,35890.00,139.900,1651860119999,5021250.71120,1966,98.896,3549619.43530,0 +1651860120000,35889.90,35935.80,35886.00,35901.50,374.700,1651860179999,13456703.45670,3411,281.732,10118438.97790,0 +1651860180000,35901.50,35915.00,35870.00,35879.70,197.448,1651860239999,7086601.03330,2245,97.505,3499810.56170,0 +1651860240000,35879.80,35910.80,35850.90,35860.80,216.006,1651860299999,7752027.64690,2104,142.279,5106847.21820,0 +1651860300000,35860.70,35860.70,35822.20,35844.90,242.681,1651860359999,8697018.17130,2451,54.444,1951044.25110,0 +1651860360000,35844.80,35849.80,35827.10,35841.90,148.493,1651860419999,5321919.95500,1558,52.703,1888799.07140,0 +1651860420000,35841.90,35852.90,35825.60,35845.00,118.097,1651860479999,4232466.65400,1456,62.146,2227319.03340,0 +1651860480000,35845.10,35872.50,35845.00,35859.10,117.131,1651860539999,4200480.61280,1432,73.768,2645377.95870,0 +1651860540000,35859.10,35859.10,35831.00,35831.00,110.244,1651860599999,3951906.03780,1196,24.858,891122.98060,0 +1651860600000,35831.10,35870.60,35829.20,35867.20,106.094,1651860659999,3803321.44500,1500,63.857,2289244.94890,0 +1651860660000,35867.20,35912.10,35858.50,35901.60,371.360,1651860719999,13327158.35780,2496,287.073,10302285.17810,0 +1651860720000,35901.60,35923.70,35890.90,35897.50,260.398,1651860779999,9350297.43430,2355,184.306,6618224.13330,0 +1651860780000,35897.40,35913.60,35870.90,35870.90,322.586,1651860839999,11578639.16460,1202,129.622,4653561.65630,0 +1651860840000,35870.90,35891.30,35856.90,35888.80,202.446,1651860899999,7262411.28120,1360,54.855,1967947.26630,0 +1651860900000,35888.70,35960.10,35888.60,35950.20,485.728,1651860959999,17450915.17631,4054,371.960,13363672.21231,0 +1651860960000,35949.40,35949.40,35905.20,35907.50,122.287,1651861019999,4393578.93880,1600,62.752,2254598.44670,0 +1651861020000,35907.40,35970.60,35905.30,35936.40,148.084,1651861079999,5322111.26030,1947,109.566,3937969.01060,0 +1651861080000,35935.90,35963.60,35932.40,35950.70,151.631,1651861139999,5450332.52300,1778,97.319,3498181.00630,0 +1651861140000,35950.70,35965.50,35940.00,35947.20,127.424,1651861199999,4580815.98670,1409,69.096,2483890.12040,0 +1651861200000,35946.80,35946.80,35921.30,35930.20,104.612,1651861259999,3758911.93650,1427,62.194,2234684.83510,0 +1651861260000,35930.10,35937.40,35922.20,35922.30,69.223,1651861319999,2487137.66830,914,38.648,1388566.57600,0 +1651861320000,35922.30,35942.00,35917.40,35925.10,66.603,1651861379999,2392910.19050,1130,39.604,1422955.53570,0 +1651861380000,35925.10,35937.10,35898.20,35918.20,357.275,1651861439999,12830718.88140,2113,112.622,4044162.17030,0 +1651861440000,35918.20,35983.50,35911.30,35924.50,242.433,1651861499999,8716586.35980,2642,162.021,5825575.72990,0 +1651861500000,35924.60,35930.00,35880.70,35895.80,141.650,1651861559999,5086205.22330,1879,38.479,1381773.22090,0 +1651861560000,35895.80,35895.80,35866.70,35877.00,267.252,1651861619999,9588057.68490,1976,100.372,3600666.49300,0 +1651861620000,35877.00,35925.00,35875.30,35916.10,189.299,1651861679999,6796382.01130,2242,97.746,3509127.58970,0 +1651861680000,35916.50,35944.90,35899.30,35929.10,127.055,1651861739999,4564601.85230,1750,67.111,2411167.09670,0 +1651861740000,35929.10,35945.60,35907.80,35907.90,91.513,1651861799999,3288028.56960,1312,42.533,1528313.91330,0 +1651861800000,35907.80,35954.60,35894.90,35910.20,229.788,1651861859999,8253519.70460,2768,119.208,4281997.22830,0 +1651861860000,35910.90,35925.40,35889.50,35897.80,114.671,1651861919999,4117223.92520,1397,42.259,1517203.47720,0 +1651861920000,35897.70,35898.30,35841.40,35843.70,319.221,1651861979999,11447105.42529,2592,163.564,5864570.76800,0 +1651861980000,35843.80,35855.60,35801.10,35855.50,525.027,1651862039999,18808212.40070,3922,132.225,4737705.09030,0 +1651862040000,35855.50,35882.00,35846.90,35879.40,110.849,1651862099999,3975272.72190,1547,64.035,2296555.75550,0 +1651862100000,35879.40,35915.80,35863.20,35907.30,93.716,1651862159999,3363095.84970,1636,55.933,2007402.62300,0 +1651862160000,35907.20,35924.20,35887.80,35914.60,156.822,1651862219999,5631738.40350,1876,70.097,2516994.82070,0 +1651862220000,35914.50,35927.40,35903.90,35907.20,95.392,1651862279999,3426015.08150,1292,60.580,2175633.51390,0 +1651862280000,35907.20,35938.30,35906.10,35923.40,109.633,1651862339999,3938661.85440,1351,74.613,2680540.91770,0 +1651862340000,35923.30,35971.20,35911.60,35969.00,153.445,1651862399999,5514975.46481,1910,76.999,2767633.70161,0 +1651862400000,35967.50,35981.40,35951.10,35962.80,237.055,1651862459999,8526228.58680,2372,154.351,5551601.05590,0 +1651862460000,35962.70,35995.50,35957.10,35978.40,262.565,1651862519999,9446514.47520,2441,190.908,6868581.67740,0 +1651862520000,35978.50,35991.80,35961.40,35961.50,273.915,1651862579999,9854779.31850,1556,49.635,1785749.50540,0 +1651862580000,35961.40,35978.30,35953.00,35955.40,73.285,1651862639999,2635921.56130,1097,39.284,1412997.82450,0 +1651862640000,35955.40,35957.30,35935.60,35937.50,289.503,1651862699999,10407149.93960,1580,62.938,2262448.12470,0 +1651862700000,35937.40,35972.40,35933.60,35952.20,309.942,1651862759999,11144547.47560,1535,82.325,2960032.60160,0 +1651862760000,35952.30,35980.00,35943.50,35968.10,156.213,1651862819999,5616875.33530,1302,83.193,2991666.87030,0 +1651862820000,35968.00,35968.10,35921.20,35922.90,79.507,1651862879999,2857014.94270,1249,27.926,1003533.52440,0 +1651862880000,35922.30,35985.00,35920.00,35965.10,223.216,1651862939999,8024702.07220,1692,99.242,3568682.42100,0 +1651862940000,35965.10,36048.70,35965.10,36024.70,641.053,1651862999999,23093097.95985,5346,458.383,16512054.04945,0 +1651863000000,36023.50,36088.00,36017.00,36021.10,514.823,1651863059999,18564060.72936,4720,333.454,12024451.46716,0 +1651863060000,36021.00,36041.80,35983.30,35989.80,324.927,1651863119999,11699004.03880,2792,109.416,3939833.85370,0 +1651863120000,35989.80,36007.70,35971.80,35981.60,191.981,1651863179999,6909505.87180,1784,101.179,3641950.61580,0 +1651863180000,35982.40,36005.80,35975.00,36005.70,127.161,1651863239999,4576258.13000,1448,69.328,2495002.58190,0 +1651863240000,36005.70,36035.70,35998.50,36019.90,179.622,1651863299999,6469745.66070,1966,99.222,3573763.93420,0 +1651863300000,36020.20,36052.70,35980.50,35993.90,378.255,1651863359999,13624308.12350,2956,173.580,6255013.92320,0 +1651863360000,35993.90,36015.70,35975.00,35988.00,124.209,1651863419999,4470127.80930,1398,49.200,1770795.31950,0 +1651863420000,35988.00,36014.70,35979.90,35998.20,326.576,1651863479999,11753379.44540,2080,137.226,4938675.39600,0 +1651863480000,35998.20,36013.80,35981.90,35982.20,141.067,1651863539999,5078183.21600,1518,74.515,2682631.76480,0 +1651863540000,35982.30,35990.00,35913.60,35923.80,310.936,1651863599999,11176157.31127,2828,104.832,3767145.10970,0 +1651863600000,35923.90,35942.10,35883.40,35897.60,317.404,1651863659999,11396038.45273,3671,93.827,3369070.58460,0 +1651863660000,35897.70,35915.90,35877.80,35880.30,246.585,1651863719999,8850708.91850,2434,108.271,3886323.98300,0 +1651863720000,35880.30,35890.00,35856.80,35888.20,193.053,1651863779999,6925476.77610,2368,77.775,2790140.35470,0 +1651863780000,35888.10,35909.90,35888.10,35900.10,114.903,1651863839999,4124691.10000,1465,78.593,2821210.90070,0 +1651863840000,35900.10,35970.30,35900.10,35970.30,210.475,1651863899999,7565086.81611,2045,156.145,5612344.74911,0 +1651863900000,35970.20,36013.60,35934.80,35943.30,258.789,1651863959999,9311129.78068,2985,140.292,5048129.80308,0 +1651863960000,35943.20,35943.30,35883.20,35904.90,197.279,1651864019999,7084397.73470,2127,60.197,2161395.72050,0 +1651864020000,35904.90,35912.70,35897.80,35900.40,74.285,1651864079999,2667099.52100,975,39.523,1418962.09840,0 +1651864080000,35900.40,35909.90,35890.00,35906.00,107.607,1651864139999,3863413.43200,1344,55.717,2000384.08050,0 +1651864140000,35905.60,35912.70,35893.70,35903.80,224.316,1651864199999,8054225.64050,1267,43.684,1568358.35620,0 +1651864200000,35903.70,35956.40,35902.00,35916.70,245.356,1651864259999,8816300.46470,2261,185.001,6647554.48480,0 +1651864260000,35916.60,35943.60,35896.90,35929.20,99.110,1651864319999,3559803.82810,1392,51.676,1856263.48720,0 +1651864320000,35929.30,35971.60,35929.30,35960.20,100.780,1651864379999,3624046.76040,1242,54.868,1972973.65040,0 +1651864380000,35960.20,35977.40,35946.70,35969.30,118.901,1651864439999,4276325.32050,1314,69.497,2499517.82260,0 +1651864440000,35969.30,35974.40,35930.00,35931.80,117.588,1651864499999,4226548.86580,1224,33.027,1187182.58900,0 +1651864500000,35931.90,35945.80,35904.00,35908.10,73.418,1651864559999,2637681.63750,1119,36.466,1310070.80080,0 +1651864560000,35908.20,35917.60,35895.70,35904.10,182.813,1651864619999,6563597.14550,1211,76.964,2763100.61440,0 +1651864620000,35904.00,35923.60,35863.60,35875.00,333.730,1651864679999,11977461.16521,2347,64.017,2297910.96220,0 +1651864680000,35875.10,35915.50,35856.00,35902.50,260.397,1651864739999,9342698.17079,2287,112.660,4042396.63910,0 +1651864740000,35902.50,35963.50,35894.80,35926.00,191.195,1651864799999,6870192.91811,2215,131.149,4712745.96301,0 +1651864800000,35926.00,35928.20,35865.30,35865.50,159.000,1651864859999,5706784.37760,1927,66.210,2376420.97990,0 +1651864860000,35865.40,35882.80,35841.50,35882.70,312.565,1651864919999,11209128.85545,2577,190.860,6844813.16830,0 +1651864920000,35882.80,35899.30,35860.00,35867.30,94.047,1651864979999,3374007.55200,1378,44.771,1606200.50340,0 +1651864980000,35868.20,35875.20,35835.00,35845.80,350.777,1651865039999,12576616.50040,2105,77.052,2762566.95750,0 +1651865040000,35845.80,35858.00,35833.40,35855.00,144.740,1651865099999,5188475.38600,1409,77.826,2789990.60650,0 +1651865100000,35855.10,35869.00,35835.50,35840.70,150.852,1651865159999,5407735.63070,1679,90.461,3242724.88660,0 +1651865160000,35838.80,35883.00,35830.00,35879.10,142.539,1651865219999,5110265.33640,1421,93.098,3338134.44100,0 +1651865220000,35879.10,35942.80,35866.80,35935.60,208.322,1651865279999,7479271.71200,2029,171.961,6174325.44180,0 +1651865280000,35935.60,35972.10,35928.90,35961.20,324.535,1651865339999,11667211.59531,2879,219.545,7893063.90091,0 +1651865340000,35960.70,35969.30,35944.20,35951.60,119.334,1651865399999,4291052.73540,1548,56.619,2035912.86630,0 +1651865400000,35951.50,36007.10,35938.40,36007.00,424.525,1651865459999,15274934.82470,3641,307.251,11055970.01120,0 +1651865460000,36007.00,36025.00,36000.00,36004.70,220.357,1651865519999,7935589.64278,2708,103.719,3735307.35528,0 +1651865520000,36004.80,36020.00,35964.00,35982.00,220.087,1651865579999,7922516.45570,1903,50.254,1808813.45250,0 +1651865580000,35982.00,36015.00,35977.20,36003.00,137.913,1651865639999,4964880.63960,1476,84.501,3042114.62760,0 +1651865640000,36002.90,36026.90,36000.00,36022.40,203.843,1651865699999,7340980.74340,1792,131.226,4725817.72160,0 +1651865700000,36022.40,36067.00,36015.10,36060.20,340.127,1651865759999,12258992.22850,3794,221.393,7979323.89910,0 +1651865760000,36060.30,36069.10,36022.60,36030.50,142.176,1651865819999,5124740.26160,2025,64.973,2342206.31550,0 +1651865820000,36030.60,36069.00,36027.50,36050.90,157.877,1651865879999,5691295.75840,1579,97.014,3497341.02120,0 +1651865880000,36051.00,36134.10,36050.90,36100.00,511.402,1651865939999,18461010.78595,5174,378.581,13666520.70035,0 +1651865940000,36099.50,36116.00,35972.40,36043.90,1337.842,1651865999999,48219686.44380,8355,518.463,18688072.94620,0 +1651866000000,36043.90,36087.90,36034.80,36065.50,267.848,1651866059999,9659163.72720,3047,143.373,5170369.40120,0 +1651866060000,36064.50,36074.20,36038.00,36068.70,176.228,1651866119999,6353492.71610,1884,68.584,2472665.74670,0 +1651866120000,36070.00,36073.10,36050.00,36056.70,81.568,1651866179999,2941495.77210,1090,39.168,1412462.79220,0 +1651866180000,36056.80,36061.30,36029.00,36060.20,187.601,1651866239999,6761092.34830,1987,61.628,2220919.13940,0 +1651866240000,36060.20,36079.10,36034.70,36040.70,199.474,1651866299999,7192390.42540,1785,114.922,4144065.72570,0 +1651866300000,36040.80,36060.00,36016.00,36047.50,149.959,1651866359999,5403885.50910,1822,67.634,2437499.47370,0 +1651866360000,36047.50,36116.00,36047.40,36094.00,291.321,1651866419999,10513871.40300,2781,191.726,6919647.66270,0 +1651866420000,36096.00,36099.20,36070.00,36099.20,95.662,1651866479999,3452277.56300,1341,46.974,1695262.29920,0 +1651866480000,36099.20,36118.80,36093.30,36100.00,166.240,1651866539999,6002245.06400,1625,77.531,2799356.92000,0 +1651866540000,36100.10,36112.00,36072.30,36088.40,136.483,1651866599999,4926411.07140,1548,63.440,2289896.89140,0 +1651866600000,36088.30,36135.10,36088.30,36110.00,284.050,1651866659999,10258598.51160,2545,204.467,7384399.50180,0 +1651866660000,36110.10,36110.10,36070.30,36075.00,160.855,1651866719999,5805116.43300,1604,83.660,3018947.16430,0 +1651866720000,36075.00,36087.00,36059.10,36072.10,108.725,1651866779999,3921899.42370,1214,53.430,1927328.92750,0 +1651866780000,36072.20,36083.70,36063.00,36071.40,64.427,1651866839999,2324039.60180,947,20.896,753813.79720,0 +1651866840000,36071.40,36092.00,36063.00,36068.00,199.205,1651866899999,7186145.25360,1803,87.044,3139992.05490,0 +1651866900000,36068.00,36072.10,36018.30,36042.20,227.899,1651866959999,8213832.64650,2241,87.005,3135811.86730,0 +1651866960000,36042.10,36049.00,35919.10,35942.60,1531.381,1651867019999,55075069.35847,6990,689.529,24790691.96760,0 +1651867020000,35942.60,35969.90,35909.50,35969.90,266.522,1651867079999,9578963.79800,2525,135.162,4858833.82110,0 +1651867080000,35969.90,35993.10,35963.00,35974.40,123.956,1651867139999,4459490.48170,1539,49.062,1765133.51540,0 +1651867140000,35974.50,36006.10,35957.80,35980.30,297.137,1651867199999,10691195.42610,2636,170.583,6137756.36410,0 +1651867200000,35981.50,35989.00,35922.50,35922.50,348.671,1651867259999,12533117.54880,2737,97.791,3515081.29290,0 +1651867260000,35922.20,35955.80,35920.00,35947.60,122.656,1651867319999,4408160.37870,1448,76.942,2765314.43690,0 +1651867320000,35947.70,35950.10,35921.00,35937.00,113.331,1651867379999,4072715.38420,1200,69.828,2509454.68980,0 +1651867380000,35937.00,35940.30,35907.60,35919.90,115.385,1651867439999,4144243.12100,1409,43.431,1559895.89740,0 +1651867440000,35920.00,35920.00,35886.00,35889.10,266.430,1651867499999,9565048.28143,2056,71.573,2569537.70510,0 +1651867500000,35889.10,35924.90,35888.20,35909.40,118.052,1651867559999,4238899.95430,1316,91.792,3295868.86780,0 +1651867560000,35909.30,35913.70,35900.50,35911.30,68.023,1651867619999,2442315.35370,1016,35.342,1268901.66060,0 +1651867620000,35911.30,35937.20,35898.60,35936.70,128.044,1651867679999,4598577.36070,1260,70.072,2517065.87170,0 +1651867680000,35936.80,35952.30,35926.10,35928.00,197.123,1651867739999,7084963.56200,1715,127.503,4582885.31410,0 +1651867740000,35928.00,35939.80,35922.20,35933.30,75.755,1651867799999,2721890.35480,771,26.773,961972.39990,0 +1651867800000,35933.40,35934.60,35924.80,35928.20,73.488,1651867859999,2640421.93300,746,36.326,1305195.46810,0 +1651867860000,35928.20,35943.10,35928.00,35941.20,44.565,1651867919999,1601324.33390,553,30.480,1095228.13280,0 +1651867920000,35941.30,35954.10,35941.20,35945.40,88.678,1651867979999,3187668.47020,972,58.109,2088815.36270,0 +1651867980000,35945.40,35945.40,35922.50,35922.60,64.533,1651868039999,2318641.98780,667,13.168,473109.81840,0 +1651868040000,35922.60,35922.70,35877.60,35890.00,195.326,1651868099999,7010358.76210,1985,51.091,1833597.59820,0 +1651868100000,35889.90,35908.60,35886.00,35891.80,260.825,1651868159999,9361470.49210,1317,230.122,8259319.57370,0 +1651868160000,35891.80,35891.90,35861.10,35862.70,131.805,1651868219999,4728114.71413,1592,36.394,1305485.46020,0 +1651868220000,35862.60,35879.80,35852.00,35873.30,157.943,1651868279999,5664686.02965,1721,80.848,2899983.49680,0 +1651868280000,35873.30,35876.70,35854.30,35862.00,80.523,1651868339999,2887764.87550,1080,29.087,1043144.20460,0 +1651868340000,35862.10,35871.70,35851.00,35853.00,73.509,1651868399999,2636021.36060,1018,34.887,1251070.44370,0 +1651868400000,35853.10,35860.90,35848.20,35848.20,110.748,1651868459999,3970792.24332,1414,66.002,2366508.00290,0 +1651868460000,35848.20,35879.80,35845.70,35864.50,131.133,1651868519999,4702844.27260,1429,93.347,3347855.96330,0 +1651868520000,35864.60,35892.10,35864.60,35873.60,154.850,1651868579999,5556189.05750,1197,117.952,4232282.60440,0 +1651868580000,35873.60,35887.40,35867.00,35882.90,81.664,1651868639999,2929847.99970,838,51.201,1836934.87090,0 +1651868640000,35883.00,35887.50,35875.70,35875.70,39.394,1651868699999,1413472.82050,565,17.797,638569.94580,0 +1651868700000,35875.80,35882.40,35875.40,35881.80,53.182,1651868759999,1908124.15810,694,38.901,1395716.69080,0 +1651868760000,35881.90,35890.10,35881.80,35890.00,45.325,1651868819999,1626581.84900,698,30.568,1096988.42800,0 +1651868820000,35890.10,35907.70,35887.50,35887.60,104.693,1651868879999,3758252.39460,1174,62.048,2227345.80160,0 +1651868880000,35887.60,35888.80,35882.60,35882.70,20.922,1651868939999,750784.55830,393,7.953,285397.28900,0 +1651868940000,35882.70,35882.70,35881.90,35882.00,30.664,1651868999999,1100295.83650,360,21.466,770249.81900,0 +1651869000000,35882.00,35882.00,35860.00,35877.10,130.116,1651869059999,4667367.90530,1018,52.550,1885088.89450,0 +1651869060000,35877.10,35878.80,35865.20,35865.30,27.622,1651869119999,990833.28690,572,13.052,468202.07430,0 +1651869120000,35865.20,35875.30,35843.80,35874.60,137.068,1651869179999,4914714.68910,1316,51.910,1861594.38200,0 +1651869180000,35874.70,35889.40,35871.40,35889.40,90.806,1651869239999,3258292.90970,967,51.886,1861754.49380,0 +1651869240000,35889.40,35918.30,35885.30,35894.10,176.535,1651869299999,6338087.54220,1674,135.372,4860273.19100,0 +1651869300000,35894.00,35894.10,35877.50,35882.80,58.189,1651869359999,2088124.71760,728,18.109,649813.51110,0 +1651869360000,35882.70,35882.80,35858.50,35869.60,56.526,1651869419999,2027432.32240,691,16.081,576807.98560,0 +1651869420000,35869.70,35908.50,35869.60,35908.50,37.010,1651869479999,1328169.37570,818,24.078,864114.15960,0 +1651869480000,35908.40,35928.60,35904.40,35909.90,95.677,1651869539999,3436665.51400,1089,54.501,1957659.51300,0 +1651869540000,35910.00,35910.00,35904.00,35907.20,42.459,1651869599999,1524635.34270,580,25.448,913795.60380,0 +1651869600000,35907.10,35922.10,35901.00,35921.70,47.722,1651869659999,1713804.75040,691,24.643,885037.53660,0 +1651869660000,35921.70,35950.60,35920.80,35943.40,236.213,1651869719999,8489105.47410,1738,190.018,6828836.20900,0 +1651869720000,35943.40,35951.50,35930.80,35939.50,93.240,1651869779999,3351202.09160,944,60.411,2171349.81670,0 +1651869780000,35939.50,35954.80,35938.30,35954.70,67.797,1651869839999,2437101.21130,872,47.323,1701145.24550,0 +1651869840000,35954.80,35958.00,35945.70,35945.70,43.571,1651869899999,1566517.32710,692,21.095,758455.73430,0 +1651869900000,35945.70,35972.40,35937.50,35972.30,96.505,1651869959999,3469357.64991,1109,40.946,1472271.69471,0 +1651869960000,35972.40,36018.00,35972.30,36006.00,311.711,1651870019999,11221627.72598,2859,216.914,7808762.73848,0 +1651870020000,36005.90,36018.00,35992.90,36009.90,79.988,1651870079999,2879943.64880,1173,29.084,1047202.08530,0 +1651870080000,36010.00,36019.70,36009.90,36018.10,80.516,1651870139999,2899950.80410,1074,42.501,1530745.85860,0 +1651870140000,36018.10,36029.70,36009.20,36015.60,94.925,1651870199999,3418952.18310,1144,37.702,1357970.37580,0 +1651870200000,36015.60,36027.30,36002.70,36026.80,104.376,1651870259999,3758677.28210,1429,46.590,1677869.91290,0 +1651870260000,36026.70,36050.70,36020.20,36050.30,203.037,1651870319999,7317531.39541,1952,153.340,5526485.68591,0 +1651870320000,36050.40,36068.80,36050.30,36056.00,111.752,1651870379999,4029467.75379,1619,53.729,1937329.45119,0 +1651870380000,36056.10,36076.60,36043.40,36056.50,119.187,1651870439999,4297949.01540,1642,44.446,1602898.34500,0 +1651870440000,36056.50,36092.70,36056.50,36092.60,145.714,1651870499999,5256234.17761,1609,88.831,3204478.47291,0 +1651870500000,36092.60,36098.70,36045.70,36075.90,225.661,1651870559999,8140254.93898,2327,72.395,2611853.08558,0 +1651870560000,36075.90,36086.40,36068.10,36074.00,71.291,1651870619999,2572059.27290,1185,17.454,629727.34100,0 +1651870620000,36074.00,36087.10,36058.70,36068.40,92.057,1651870679999,3320682.71290,1145,29.471,1063018.20740,0 +1651870680000,36068.40,36069.10,36040.20,36041.90,87.697,1651870739999,3162022.08040,1166,28.451,1025799.13150,0 +1651870740000,36041.90,36060.00,36040.40,36059.70,98.363,1651870799999,3546055.93030,926,57.215,2062650.60740,0 +1651870800000,36059.70,36079.80,36059.70,36079.70,53.517,1651870859999,1930479.88760,734,22.051,795402.97020,0 +1651870860000,36079.70,36084.20,36051.70,36051.70,70.851,1651870919999,2555756.09310,1132,25.517,920431.69940,0 +1651870920000,36051.00,36060.00,36039.80,36039.90,105.271,1651870979999,3794845.17070,1289,33.914,1222531.95090,0 +1651870980000,36039.80,36050.00,36029.80,36049.90,103.073,1651871039999,3714713.99240,1049,40.158,1447374.95230,0 +1651871040000,36049.90,36067.20,36049.90,36061.10,62.725,1651871099999,2261876.97870,818,24.953,899775.36750,0 +1651871100000,36061.10,36063.90,35987.00,35998.00,521.501,1651871159999,18779858.68940,3193,110.762,3987898.43750,0 +1651871160000,35998.00,36000.10,35970.20,35997.10,140.949,1651871219999,5072019.34880,1489,50.097,1802776.25600,0 +1651871220000,35997.10,36013.50,35997.00,36005.20,68.048,1651871279999,2450072.89730,800,35.949,1294323.18660,0 +1651871280000,36005.20,36011.00,36005.10,36005.20,34.310,1651871339999,1235438.32030,519,17.830,642016.48280,0 +1651871340000,36005.20,36019.70,36000.90,36012.80,86.939,1651871399999,3130615.61440,1027,52.599,1894041.62180,0 +1651871400000,36012.80,36012.80,36003.60,36012.70,24.227,1651871459999,872409.38680,457,9.219,331969.78450,0 +1651871460000,36012.80,36012.90,36003.60,36003.70,24.583,1651871519999,885255.13650,457,4.295,154668.92920,0 +1651871520000,36003.60,36008.80,36001.00,36007.50,26.976,1651871579999,971290.48560,563,11.061,398257.95240,0 +1651871580000,36007.40,36007.50,35979.00,35988.00,107.897,1651871639999,3883353.54160,1050,13.451,484090.41350,0 +1651871640000,35987.90,35999.40,35981.80,35993.80,103.815,1651871699999,3736443.56270,1098,72.889,2623422.13880,0 +1651871700000,35993.90,35993.90,35976.10,35980.10,36.352,1651871759999,1308029.17960,826,12.448,447915.58730,0 +1651871760000,35980.00,35980.10,35959.20,35968.50,157.560,1651871819999,5667247.24220,1390,39.768,1430362.01590,0 +1651871820000,35968.50,35979.60,35968.40,35977.00,32.058,1651871879999,1153312.37900,613,25.622,921770.83700,0 +1651871880000,35977.00,35986.30,35975.30,35986.30,27.409,1651871939999,986177.37160,498,22.556,811566.85280,0 +1651871940000,35986.20,35993.20,35986.20,35990.20,35.922,1651871999999,1292832.73110,662,19.595,705221.35150,0 +1651872000000,35990.10,35990.10,35984.90,35989.90,23.631,1651872059999,850426.35140,456,9.394,338065.08860,0 +1651872060000,35990.00,35992.50,35984.90,35989.90,26.530,1651872119999,954812.99870,546,11.349,408455.00840,0 +1651872120000,35990.00,36012.80,35989.90,36009.30,85.713,1651872179999,3085993.11590,1121,54.966,1979009.98210,0 +1651872180000,36009.30,36024.90,36005.80,36024.90,57.930,1651872239999,2086551.79410,1011,43.839,1579056.77210,0 +1651872240000,36024.90,36034.40,36020.00,36025.10,50.858,1651872299999,1832363.10160,855,37.354,1345844.31040,0 +1651872300000,36025.00,36025.10,35999.90,36010.40,55.404,1651872359999,1994976.52950,752,21.908,788764.24100,0 +1651872360000,36010.40,36017.50,36010.40,36013.70,27.693,1651872419999,997319.99130,516,15.411,555010.03900,0 +1651872420000,36013.70,36016.00,36010.30,36011.20,26.406,1651872479999,950971.50150,532,10.876,391679.06130,0 +1651872480000,36011.10,36019.40,36005.00,36016.60,74.479,1651872539999,2681940.48850,900,15.789,568622.68440,0 +1651872540000,36016.70,36049.00,36016.60,36045.00,118.914,1651872599999,4285187.66710,1132,103.057,3713774.34530,0 +1651872600000,36044.90,36051.90,36037.10,36051.90,90.427,1651872659999,3259539.17390,1094,63.931,2304553.26460,0 +1651872660000,36051.90,36060.40,36030.70,36033.80,129.265,1651872719999,4659747.46360,1315,44.297,1597020.70760,0 +1651872720000,36033.80,36036.40,36024.60,36028.30,51.064,1651872779999,1839916.27420,762,18.213,656260.85150,0 +1651872780000,36028.20,36028.20,36020.90,36021.00,28.291,1651872839999,1019151.11890,543,6.639,239167.31890,0 +1651872840000,36020.90,36021.00,36005.00,36005.10,90.858,1651872899999,3271651.88090,670,14.820,533740.88440,0 +1651872900000,36005.10,36005.10,35956.20,35960.40,221.102,1651872959999,7955636.49131,2015,40.753,1466165.03030,0 +1651872960000,35960.40,35960.40,35946.20,35958.30,104.604,1651873019999,3760825.92080,1571,54.565,1961837.35460,0 +1651873020000,35958.20,35964.60,35951.30,35957.00,43.827,1651873079999,1575999.59860,853,32.462,1167333.76750,0 +1651873080000,35957.10,35957.10,35951.50,35953.10,18.269,1651873139999,656876.84880,500,8.261,297034.48320,0 +1651873140000,35953.00,35964.60,35953.00,35957.50,58.625,1651873199999,2108055.87280,951,32.107,1154525.24490,0 +1651873200000,35957.60,35960.00,35942.10,35950.30,89.649,1651873259999,3222732.91270,1039,18.181,653580.69460,0 +1651873260000,35949.10,35949.10,35915.50,35932.90,148.458,1651873319999,5334033.33396,1813,41.216,1480878.53290,0 +1651873320000,35932.90,35933.90,35913.00,35919.40,93.210,1651873379999,3348095.36590,1080,28.840,1035943.36060,0 +1651873380000,35919.40,35964.60,35916.00,35951.10,112.772,1651873439999,4053320.79460,1437,83.335,2995205.59540,0 +1651873440000,35951.10,35959.50,35906.60,35909.10,103.723,1651873499999,3726857.13960,1419,25.970,933157.76310,0 +1651873500000,35909.10,35949.60,35908.90,35939.60,98.331,1651873559999,3533511.97220,1190,79.684,2863401.80400,0 +1651873560000,35938.60,35938.60,35879.80,35894.60,187.020,1651873619999,6714053.59713,2122,43.690,1568366.96760,0 +1651873620000,35894.60,35911.80,35879.70,35900.00,68.128,1651873679999,2445487.96690,1209,34.135,1225418.05300,0 +1651873680000,35900.00,35916.40,35900.00,35902.60,68.741,1651873739999,2468363.51360,890,51.151,1836760.02910,0 +1651873740000,35904.40,35915.60,35904.40,35910.10,37.427,1651873799999,1344035.81670,716,21.666,778048.71570,0 +1651873800000,35910.10,35910.10,35900.00,35900.10,33.161,1651873859999,1190585.24880,470,14.393,516755.93650,0 +1651873860000,35900.00,35900.10,35900.00,35900.10,22.967,1651873919999,824516.35970,390,10.597,380433.35970,0 +1651873920000,35900.10,35949.60,35880.10,35949.60,104.336,1651873979999,3746357.66220,1465,69.944,2511858.27050,0 +1651873980000,35949.60,35956.00,35911.40,35911.40,87.822,1651874039999,3156424.16720,1207,43.550,1565475.88190,0 +1651874040000,35911.30,35932.80,35904.90,35932.80,54.527,1651874099999,1958481.36410,778,34.849,1251709.95240,0 +1651874100000,35932.80,35950.50,35915.70,35920.70,70.223,1651874159999,2523412.34900,840,36.260,1302870.89350,0 +1651874160000,35920.80,35934.70,35916.80,35934.70,46.798,1651874219999,1681347.78970,657,27.767,997609.63580,0 +1651874220000,35934.70,35949.00,35934.60,35949.00,42.195,1651874279999,1516441.03950,667,28.951,1040476.20800,0 +1651874280000,35949.00,35949.00,35943.30,35943.30,46.468,1651874339999,1670435.51030,365,17.720,636994.05130,0 +1651874340000,35943.40,35943.40,35934.00,35935.30,31.486,1651874399999,1131643.39730,399,14.944,537105.82900,0 +1651874400000,35935.40,35935.40,35930.00,35932.60,34.861,1651874459999,1252653.16920,536,13.234,475534.36420,0 +1651874460000,35932.60,35974.20,35929.10,35974.20,78.059,1651874519999,2807057.91140,918,59.950,2155952.18500,0 +1651874520000,35974.10,35974.10,35952.00,35952.60,55.555,1651874579999,1997975.41040,660,20.106,723125.09000,0 +1651874580000,35952.50,35963.80,35946.50,35953.10,51.673,1651874639999,1857905.49860,690,24.697,888014.09630,0 +1651874640000,35953.10,35965.00,35953.00,35955.10,35.694,1651874699999,1283579.07960,565,21.752,782214.14920,0 +1651874700000,35955.20,35981.10,35955.10,35981.10,47.009,1651874759999,1691060.16480,632,33.836,1217176.87260,0 +1651874760000,35981.10,35981.10,35956.00,35957.60,72.174,1651874819999,2596221.75640,712,20.319,730938.19920,0 +1651874820000,35957.60,35972.60,35957.50,35972.50,21.483,1651874879999,772680.64650,358,14.506,521728.71680,0 +1651874880000,35972.50,35972.50,35960.30,35971.10,19.600,1651874939999,704933.08310,460,8.731,314018.34780,0 +1651874940000,35971.20,35979.90,35963.60,35964.50,39.293,1651874999999,1413600.60810,558,19.768,711168.47660,0 +1651875000000,35964.60,35967.20,35882.10,35899.50,307.803,1651875059999,11055269.79910,2316,33.344,1197743.08070,0 +1651875060000,35899.50,35912.60,35877.00,35877.10,106.515,1651875119999,3823003.43410,1351,42.286,1517786.35960,0 +1651875120000,35877.10,35895.10,35870.60,35884.20,90.086,1651875179999,3232190.23430,1419,47.771,1714021.37810,0 +1651875180000,35884.10,35892.20,35859.30,35867.90,144.658,1651875239999,5189848.30860,1472,35.731,1282016.46010,0 +1651875240000,35867.90,35873.20,35851.00,35860.80,96.265,1651875299999,3452345.55656,1469,38.796,1391405.55220,0 +1651875300000,35860.70,35883.40,35860.00,35877.60,73.274,1651875359999,2628621.62710,860,59.504,2134633.10270,0 +1651875360000,35877.70,35942.80,35877.60,35918.60,143.112,1651875419999,5139606.26820,1674,99.039,3556493.61650,0 +1651875420000,35918.50,35918.60,35890.20,35890.20,36.433,1651875479999,1308122.51730,612,15.491,556193.62880,0 +1651875480000,35890.20,35897.30,35876.00,35886.50,49.357,1651875539999,1771242.33990,776,29.711,1066196.46590,0 +1651875540000,35886.50,35895.90,35886.50,35886.60,27.190,1651875599999,975905.83920,406,10.782,386979.22820,0 +1651875600000,35886.50,35916.40,35886.40,35916.30,36.046,1651875659999,1294094.86320,730,24.226,869734.50140,0 +1651875660000,35916.30,35939.80,35912.70,35922.50,49.363,1651875719999,1773317.34670,864,15.785,567072.82100,0 +1651875720000,35922.60,35930.00,35908.90,35929.90,42.187,1651875779999,1515316.12790,614,26.127,938476.04770,0 +1651875780000,35930.00,35949.90,35929.90,35937.60,70.686,1651875839999,2540575.11870,704,51.978,1868194.12580,0 +1651875840000,35938.60,35947.80,35925.10,35942.30,46.766,1651875899999,1680667.03110,938,15.955,573355.00340,0 +1651875900000,35942.30,35943.40,35920.80,35927.10,59.453,1651875959999,2136257.04700,698,14.743,529712.63630,0 +1651875960000,35927.10,35929.30,35911.00,35914.70,36.260,1651876019999,1302529.52060,514,20.499,736326.64270,0 +1651876020000,35914.60,35925.80,35893.30,35900.50,68.690,1651876079999,2466442.48800,735,26.853,964167.53630,0 +1651876080000,35900.60,35900.60,35871.30,35889.50,59.873,1651876139999,2148492.51270,781,25.504,915229.30910,0 +1651876140000,35889.50,35900.00,35880.00,35898.10,36.647,1651876199999,1315187.79530,549,16.004,574304.16750,0 +1651876200000,35898.00,35901.90,35884.10,35887.00,33.339,1651876259999,1196580.29730,565,16.442,590110.08210,0 +1651876260000,35885.40,35900.00,35885.40,35900.00,75.926,1651876319999,2725233.81110,608,63.058,2263363.24960,0 +1651876320000,35899.90,35946.30,35899.90,35935.00,212.869,1651876379999,7644330.85400,1466,184.814,6636267.20210,0 +1651876380000,35934.90,35937.00,35920.60,35924.10,58.888,1651876439999,2115926.42930,565,10.545,378925.58920,0 +1651876440000,35924.00,35943.60,35924.00,35938.30,52.149,1651876499999,1874154.20860,619,37.647,1352971.27620,0 +1651876500000,35938.40,35942.10,35921.30,35934.00,143.945,1651876559999,5172827.83200,869,62.190,2234753.56050,0 +1651876560000,35934.00,35934.10,35884.60,35917.20,85.442,1651876619999,3068163.92870,1190,34.295,1231414.08360,0 +1651876620000,35917.30,35920.70,35880.00,35880.10,28.565,1651876679999,1025407.54600,569,6.962,249941.45510,0 +1651876680000,35880.10,35917.30,35858.60,35907.00,184.827,1651876739999,6631794.91630,2045,88.189,3164384.50200,0 +1651876740000,35907.00,35949.10,35907.00,35935.60,102.577,1651876799999,3686256.38440,1012,42.555,1529196.04130,0 +1651876800000,35935.70,35940.00,35930.70,35932.70,29.000,1651876859999,1042164.59720,541,5.505,197835.19430,0 +1651876860000,35932.70,35951.50,35930.70,35948.80,91.139,1651876919999,3275929.76900,997,57.876,2080356.81090,0 +1651876920000,35948.90,35959.90,35938.30,35959.90,63.066,1651876979999,2267172.30651,608,47.010,1690045.83171,0 +1651876980000,35959.90,35961.60,35946.70,35948.30,128.385,1651877039999,4615827.45210,916,26.784,962959.03080,0 +1651877040000,35948.20,35965.00,35948.20,35959.90,37.183,1651877099999,1337059.31360,476,20.601,740753.31000,0 +1651877100000,35959.90,35973.00,35956.90,35973.00,71.584,1651877159999,2574539.35390,624,56.610,2035969.69160,0 +1651877160000,35973.00,35973.00,35944.30,35948.60,293.296,1651877219999,10546462.95760,1481,148.827,5351417.79040,0 +1651877220000,35948.70,35957.50,35945.00,35948.70,64.394,1651877279999,2315168.45620,589,16.133,580025.55220,0 +1651877280000,35948.70,35979.00,35948.60,35978.90,53.126,1651877339999,1910676.43390,689,36.712,1320386.48190,0 +1651877340000,35979.00,36050.00,35973.10,36046.60,867.121,1651877399999,31234497.43239,3953,587.335,21153461.57369,0 +1651877400000,36045.90,36046.60,36007.50,36015.70,173.422,1651877459999,6247671.95090,1996,82.872,2985507.45560,0 +1651877460000,36015.80,36031.80,36013.20,36028.70,112.871,1651877519999,4065783.71870,1015,72.312,2604837.12930,0 +1651877520000,36028.10,36033.90,36012.50,36019.90,42.502,1651877579999,1530969.14190,794,13.078,471104.21050,0 +1651877580000,36020.00,36029.00,35983.00,35990.60,308.336,1651877639999,11100853.47610,1710,56.692,2040974.91500,0 +1651877640000,35990.60,36025.60,35984.90,36020.50,60.059,1651877699999,2162659.16780,707,32.843,1182537.10160,0 +1651877700000,36020.50,36020.50,36006.00,36014.90,58.352,1651877759999,2101387.61950,654,28.911,1041130.35790,0 +1651877760000,36015.00,36022.50,36014.90,36015.00,34.210,1651877819999,1232213.40990,479,21.898,788739.29470,0 +1651877820000,36015.00,36020.00,36006.90,36020.00,88.336,1651877879999,3181421.25650,669,64.833,2335041.13560,0 +1651877880000,36019.90,36020.00,35986.60,36006.70,192.941,1651877939999,6946308.79270,1384,26.406,950785.60370,0 +1651877940000,36006.70,36009.90,35961.30,35974.50,216.528,1651877999999,7790682.53510,1442,31.285,1125770.65040,0 +1651878000000,35974.40,35986.30,35971.50,35986.20,51.686,1651878059999,1859551.51550,779,31.708,1140798.30610,0 +1651878060000,35986.20,36008.20,35986.20,36005.60,62.489,1651878119999,2249586.62750,882,32.332,1163972.59640,0 +1651878120000,36005.60,36010.00,35998.20,35998.30,51.923,1651878179999,1869574.30530,592,27.438,987976.68240,0 +1651878180000,35998.20,35999.80,35993.20,35999.80,30.042,1651878239999,1081421.05080,361,19.912,716768.97070,0 +1651878240000,35999.70,36017.50,35999.70,36008.40,130.896,1651878299999,4713553.54600,667,111.793,4025626.60650,0 +1651878300000,36008.40,36012.00,36003.90,36011.60,25.443,1651878359999,916166.01890,461,9.095,327494.90540,0 +1651878360000,36011.60,36036.90,36011.50,36029.10,77.496,1651878419999,2791954.59010,786,53.812,1938605.43780,0 +1651878420000,36029.10,36029.10,35988.00,35988.00,63.918,1651878479999,2301050.18580,965,12.994,467788.70110,0 +1651878480000,35988.00,36001.60,35982.50,35992.70,47.502,1651878539999,1709673.87230,613,25.012,900170.57010,0 +1651878540000,35992.70,36032.60,35992.70,36029.90,144.595,1651878599999,5208372.65470,956,127.073,4577348.13240,0 +1651878600000,36029.80,36029.80,36003.80,36005.50,67.923,1651878659999,2446363.93380,730,16.425,591558.21860,0 +1651878660000,36005.60,36020.60,36005.50,36020.50,30.520,1651878719999,1099190.53040,431,16.470,593170.91960,0 +1651878720000,36020.60,36037.80,36020.50,36035.00,70.161,1651878779999,2528007.96070,852,52.668,1897772.11330,0 +1651878780000,36035.20,36044.20,36014.30,36024.60,97.952,1651878839999,3529490.78340,1117,54.079,1948635.69450,0 +1651878840000,36024.60,36072.60,36024.50,36041.60,738.761,1651878899999,26631110.96429,2762,362.384,13062557.99049,0 +1651878900000,36041.60,36092.80,36034.00,36092.80,216.960,1651878959999,7822603.47171,1890,101.143,3647156.94841,0 +1651878960000,36089.90,36099.00,36044.40,36047.50,85.213,1651879019999,3073868.68490,1483,40.723,1469033.91170,0 +1651879020000,36047.50,36064.50,36047.50,36062.70,78.711,1651879079999,2838202.25100,840,43.755,1577703.49390,0 +1651879080000,36062.70,36065.20,36051.70,36057.00,31.234,1651879139999,1126217.56010,508,9.712,350185.02240,0 +1651879140000,36057.00,36059.60,36051.30,36058.20,36.424,1651879199999,1313365.22390,471,15.579,561751.67990,0 +1651879200000,36058.20,36073.80,36049.00,36073.30,79.773,1651879259999,2876475.88120,810,22.455,809737.24870,0 +1651879260000,36073.20,36088.90,36062.00,36064.80,66.598,1651879319999,2402578.99000,814,36.401,1313221.98850,0 +1651879320000,36064.90,36080.00,36064.80,36071.40,47.448,1651879379999,1711621.56120,683,15.122,545489.80700,0 +1651879380000,36071.30,36079.20,36061.50,36079.20,75.507,1651879439999,2723300.94560,805,29.967,1080844.95490,0 +1651879440000,36079.20,36079.20,36061.50,36072.00,34.538,1651879499999,1245797.98360,625,16.432,592691.48800,0 +1651879500000,36072.00,36078.00,36062.20,36065.60,136.986,1651879559999,4941159.43720,788,22.349,806113.09110,0 +1651879560000,36065.60,36096.40,36065.60,36079.60,186.435,1651879619999,6727365.76100,1141,141.146,5093262.01290,0 +1651879620000,36079.60,36080.00,36068.60,36068.70,24.674,1651879679999,890110.92260,490,9.707,350178.92300,0 +1651879680000,36068.60,36068.70,36065.50,36068.40,30.218,1651879739999,1089884.05390,413,7.089,255675.83070,0 +1651879740000,36068.30,36072.40,36067.80,36068.70,21.274,1651879799999,767344.42830,384,8.867,319831.97120,0 +1651879800000,36068.70,36069.90,36054.30,36057.30,134.721,1651879859999,4858654.24160,801,10.532,379801.26980,0 +1651879860000,36057.30,36074.40,36057.30,36068.90,43.854,1651879919999,1581758.19160,746,21.050,759227.12010,0 +1651879920000,36069.00,36069.00,36043.90,36044.00,182.845,1651879979999,6591579.12610,1098,39.550,1425659.41390,0 +1651879980000,36043.90,36044.00,36031.00,36038.10,103.143,1651880039999,3716904.83180,1282,51.551,1857683.37140,0 +1651880040000,36038.70,36040.20,36032.70,36032.70,130.875,1651880099999,4716082.09670,750,43.974,1584656.68010,0 +1651880100000,36032.70,36039.70,36012.60,36039.70,156.672,1651880159999,5644301.74770,1171,50.658,1824992.21120,0 +1651880160000,36039.70,36045.30,36024.80,36039.90,48.925,1651880219999,1763191.20710,689,23.532,848052.66940,0 +1651880220000,36040.00,36047.50,36039.90,36041.80,45.978,1651880279999,1657250.00050,492,22.666,816959.85470,0 +1651880280000,36041.80,36042.90,36041.00,36042.90,24.023,1651880339999,865829.85350,316,13.722,494565.63370,0 +1651880340000,36042.90,36048.00,36042.80,36047.90,19.601,1651880399999,706541.68440,328,13.062,470837.25400,0 +1651880400000,36048.00,36048.20,36028.10,36037.90,52.340,1651880459999,1886237.30550,687,10.001,360400.44770,0 +1651880460000,36037.80,36045.70,36037.80,36043.30,37.458,1651880519999,1350033.25030,405,14.573,525230.56550,0 +1651880520000,36043.40,36049.40,36041.50,36049.10,30.642,1651880579999,1104509.65810,585,17.432,628353.22170,0 +1651880580000,36049.00,36069.90,36048.50,36067.30,62.526,1651880639999,2254658.84740,671,51.352,1851822.25060,0 +1651880640000,36067.40,36067.40,36050.50,36059.60,59.918,1651880699999,2160600.85320,717,27.937,1007437.32790,0 +1651880700000,36059.70,36066.70,36012.00,36020.30,97.637,1651880759999,3517418.72250,1258,31.209,1124271.46540,0 +1651880760000,36020.30,36020.30,36000.00,36010.40,98.352,1651880819999,3541530.25550,1122,31.161,1122089.66820,0 +1651880820000,36010.30,36015.30,36002.60,36012.70,39.599,1651880879999,1425970.42100,602,15.757,567417.54950,0 +1651880880000,36012.70,36018.40,36012.60,36016.50,20.176,1651880939999,726648.43480,378,12.009,432510.99440,0 +1651880940000,36016.50,36030.00,36013.10,36030.00,57.968,1651880999999,2088094.86360,675,24.319,876005.39220,0 +1651881000000,36030.00,36068.20,36030.00,36059.50,73.738,1651881059999,2658937.20210,855,38.396,1384455.58570,0 +1651881060000,36059.60,36061.90,36042.80,36042.80,30.964,1651881119999,1116367.58010,538,9.407,339169.88670,0 +1651881120000,36042.80,36042.80,36018.70,36018.70,24.380,1651881179999,878499.50560,461,10.575,381077.12550,0 +1651881180000,36018.50,36036.10,36005.40,36021.60,44.015,1651881239999,1585485.41360,831,28.178,1015059.34270,0 +1651881240000,36021.50,36027.90,36014.50,36027.90,37.758,1651881299999,1360131.71340,516,16.367,589556.07570,0 +1651881300000,36027.80,36031.90,36000.00,36000.20,78.189,1651881359999,2815960.76460,1096,39.854,1435393.94750,0 +1651881360000,36000.20,36000.20,35989.60,35993.90,111.951,1651881419999,4029720.45080,904,21.683,780481.00910,0 +1651881420000,35993.90,36012.40,35993.80,35996.80,73.877,1651881479999,2659704.79180,741,33.468,1204999.50350,0 +1651881480000,35996.80,36012.00,35993.00,36004.80,286.586,1651881539999,10318452.44230,838,271.151,9762761.43980,0 +1651881540000,36004.80,36009.70,35991.00,35995.30,35.970,1651881599999,1294815.33930,625,16.443,591885.46580,0 diff --git a/pkg/indicator/v2/three_crows.go b/pkg/indicator/v2/three_crows.go new file mode 100644 index 0000000000..f3c69d4afc --- /dev/null +++ b/pkg/indicator/v2/three_crows.go @@ -0,0 +1,63 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type ThreeCrowsStream struct { + *types.Float64Series + + window int +} + +// https://www.candlescanner.com/candlestick-patterns/two-crows/ +// The Two Crows is a three-line bearish reversal candlestick pattern. +// The pattern requires confirmation, that is, the following candles should break +// a trendline or the nearest support area which may be formed by the first candle’s line. +// If the pattern is not confirmed it may act only as a temporary pause within an uptrend. +// Although the pattern name suggest that two lines form it, in fact, it contains three lines +func ThreeCrows(source KLineSubscription) *ThreeCrowsStream { + s := &ThreeCrowsStream{ + Float64Series: types.NewFloat64Series(), + window: 3, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + + var ( + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + isDownTrend = three.Low.Float64() > two.Low.Float64() && + two.Low.Float64() > one.Low.Float64() + isAllBearish = three.Open.Float64() > three.Close.Float64() && + two.Open.Float64() > two.Close.Float64() && + one.Open.Float64() > one.Close.Float64() + opensWithinPreviousBody = three.Open.Float64() > two.Open.Float64() && + two.Open.Float64() > three.Close.Float64() && + two.Open.Float64() > one.Open.Float64() && + one.Open.Float64() > two.Close.Float64() + ) + + if isDownTrend && isAllBearish && opensWithinPreviousBody { + output = Bear + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *ThreeCrowsStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/three_crows_test.go b/pkg/indicator/v2/three_crows_test.go new file mode 100644 index 0000000000..a28ba82aeb --- /dev/null +++ b/pkg/indicator/v2/three_crows_test.go @@ -0,0 +1,27 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestThreeCrows(t *testing.T) { + ts := []types.KLine{ + {Open: n(21.65), Low: n(21.25), High: n(21.82), Close: n(21.32)}, + {Open: n(21.48), Low: n(20.97), High: n(21.57), Close: n(21.10)}, + {Open: n(21.25), Low: n(20.60), High: n(21.35), Close: n(20.70)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := ThreeCrows(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestThreeCrows Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } +} diff --git a/pkg/indicator/v2/three_line_strike.go b/pkg/indicator/v2/three_line_strike.go new file mode 100644 index 0000000000..89e7925e58 --- /dev/null +++ b/pkg/indicator/v2/three_line_strike.go @@ -0,0 +1,76 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type ThreeLineStrikeStream struct { + *types.Float64Series + + window int +} + +func ThreeLineStrike(source KLineSubscription) *ThreeLineStrikeStream { + s := &ThreeLineStrikeStream{ + Float64Series: types.NewFloat64Series(), + window: 4, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + + var ( + four = source.Last(3) + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + ) + // BEAR + if three.Close.Float64() < four.Close.Float64() { + if two.Close.Float64() < three.Close.Float64() { + if four.Close.Float64() < three.Open.Float64() && + three.Open.Float64() < four.Open.Float64() { + if three.Close.Float64() < two.Open.Float64() && + two.Open.Float64() < three.Open.Float64() { + if one.Open.Float64() < two.Close.Float64() { + if one.Close.Float64() > four.Open.Float64() { + output = Bear + } + } + } + } + } + } + // BULL + if three.Close.Float64() > four.Close.Float64() { + if two.Close.Float64() > three.Close.Float64() { + if four.Close.Float64() > three.Open.Float64() && + three.Open.Float64() > four.Open.Float64() { + if three.Close.Float64() > two.Open.Float64() && + two.Open.Float64() > three.Open.Float64() { + if one.Open.Float64() > two.Close.Float64() { + if one.Close.Float64() < four.Open.Float64() { + output = Bull + } + } + } + } + } + } + + s.PushAndEmit(output) + }) + + return s +} + +func (s *ThreeLineStrikeStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/three_line_strike_test.go b/pkg/indicator/v2/three_line_strike_test.go new file mode 100644 index 0000000000..f4ba487ba4 --- /dev/null +++ b/pkg/indicator/v2/three_line_strike_test.go @@ -0,0 +1,46 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestThreeLineStrike(t *testing.T) { + ts := []types.KLine{ + {Open: n(98), Low: n(77), High: n(100), Close: n(80)}, + {Open: n(90), Low: n(68), High: n(95), Close: n(73)}, + {Open: n(82), Low: n(65), High: n(86), Close: n(67)}, + {Open: n(62), Low: n(59), High: n(103), Close: n(101)}, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := ThreeLineStrike(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBear := -1.0 + + if ind.Last(0) != expectedBear { + t.Errorf("TestThreeLineStrike Bear unexpected result: got %v want %v", ind.Last(0), expectedBear) + } + + ts = []types.KLine{ + {Open: n(70), Low: n(60), High: n(100), Close: n(90)}, + {Open: n(80), Low: n(75), High: n(110), Close: n(105)}, + {Open: n(95), Low: n(93), High: n(120), Close: n(115)}, + {Open: n(125), Low: n(50), High: n(130), Close: n(55)}, + } + ind = ThreeLineStrike(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestThreeLineStrike Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } + +} diff --git a/pkg/indicator/v2/three_white_soldiers.go b/pkg/indicator/v2/three_white_soldiers.go new file mode 100644 index 0000000000..2321992990 --- /dev/null +++ b/pkg/indicator/v2/three_white_soldiers.go @@ -0,0 +1,56 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type ThreeWhiteSoldiersStream struct { + *types.Float64Series + + window int +} + +func ThreeWhiteSoldiers(source KLineSubscription) *ThreeWhiteSoldiersStream { + s := &ThreeWhiteSoldiersStream{ + Float64Series: types.NewFloat64Series(), + window: 3, + } + + source.AddSubscriber(func(kLine types.KLine) { + var ( + i = source.Length() + output = Neutral + ) + if i < s.window { + s.PushAndEmit(output) + return + } + var ( + three = source.Last(2) + two = source.Last(1) + one = source.Last(0) + isUpTrend = two.High.Float64() > three.High.Float64() && + one.High.Float64() > two.High.Float64() + isAllBullish = three.Open.Float64() < three.Close.Float64() && + two.Open.Float64() < two.Close.Float64() && + one.Open.Float64() < one.Close.Float64() + doesOpenWithinPreviousBody = three.Close.Float64() > two.Open.Float64() && + two.Open.Float64() < three.High.Float64() && + two.High.Float64() > one.Open.Float64() && + one.Open.Float64() < two.Close.Float64() + ) + + if isUpTrend && isAllBullish && doesOpenWithinPreviousBody { + output = Bull + } + + s.PushAndEmit(output) + + }) + + return s +} + +func (s *ThreeWhiteSoldiersStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfPattern) +} diff --git a/pkg/indicator/v2/three_white_soldiers_test.go b/pkg/indicator/v2/three_white_soldiers_test.go new file mode 100644 index 0000000000..b40a4dbae8 --- /dev/null +++ b/pkg/indicator/v2/three_white_soldiers_test.go @@ -0,0 +1,28 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestThreeWhiteSoldiers(t *testing.T) { + ts := []types.KLine{ + {Open: n(21.12), Low: n(20.85), High: n(21.83), Close: n(21.65)}, + {Open: n(21.48), Low: n(21.36), High: n(22.40), Close: n(22.20)}, + {Open: n(21.80), Low: n(21.66), High: n(22.80), Close: n(22.65)}, + } + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := ThreeWhiteSoldiers(kLines) + + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + expectedBull := 1.0 + + if ind.Last(0) != expectedBull { + t.Errorf("TestThreeWhiteSoldiers Bull unexpected result: got %v want %v", ind.Last(0), expectedBull) + } +} diff --git a/pkg/indicator/v2/trend_line.go b/pkg/indicator/v2/trend_line.go new file mode 100644 index 0000000000..88c040f0da --- /dev/null +++ b/pkg/indicator/v2/trend_line.go @@ -0,0 +1,68 @@ +package indicatorv2 + +import ( + "math" + + "github.com/c9s/bbgo/pkg/types" +) + +type TrendLineStream struct { + *types.Float64Series +} + +// NewTrendlineIndicator returns an indicator whose output is the slope of the trend +// line given by the values in the window. +func TrendLine(source KLineSubscription, window int) *TrendLineStream { + var ( + closing = ClosePrices(source) + s = &TrendLineStream{ + Float64Series: types.NewFloat64Series(), + } + ) + source.AddSubscriber(func(v types.KLine) { + var index = source.Length() + if index < window { + s.PushAndEmit(0.0) + } + var ( + values = closing.Slice.Tail(window) + ab = sumXy(values)*float64(window) - sumX(values)*sumY(values) + cd = sumX2(values)*float64(window) - math.Pow(sumX(values), 2) + trend = ab / cd + ) + s.PushAndEmit(trend) + }) + return s +} + +func (s *TrendLineStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} + +func sumX(s []float64) (sum float64) { + for i := range s { + sum += float64(i) + } + return +} + +func sumY(s []float64) (sum float64) { + for _, d := range s { + sum += d + } + return +} + +func sumXy(s []float64) (sum float64) { + for i, d := range s { + sum += d * float64(i) + } + return +} + +func sumX2(s []float64) (sum float64) { + for i := range s { + sum += math.Pow(float64(i), 2) + } + return +} diff --git a/pkg/indicator/v2/trend_line_test.go b/pkg/indicator/v2/trend_line_test.go new file mode 100644 index 0000000000..97adb93291 --- /dev/null +++ b/pkg/indicator/v2/trend_line_test.go @@ -0,0 +1,52 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestTrendIndicator(t *testing.T) { + t.Run("returns the correct slope of the trend", func(t *testing.T) { + tests := []struct { + closing []float64 + expectedResult float64 + }{ + { + closing: []float64{0, 1, 2, 3}, + expectedResult: 1, + }, + { + closing: []float64{0, 2, 4, 6}, + expectedResult: 2, + }, + { + closing: []float64{5, 4, 3, 2}, + expectedResult: -1, + }, + } + + for _, tt := range tests { + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := TrendLine(kLines, 3) + buildKLines := func(closing []float64) (kLines []types.KLine) { + for i := range closing { + kLines = append(kLines, types.KLine{Close: n(closing[i])}) + } + return kLines + } + ts := buildKLines(tt.closing) + for _, d := range ts { + stream.EmitKLineClosed(d) + } + + spew.Dump(ind) + assert.InDelta(t, tt.expectedResult, ind.Last(0), 0.001, "Expected Trend.Last(0) to be %v, but got %v", tt.expectedResult, ind.Last(0)) + + } + }) +} diff --git a/pkg/indicator/v2/trima.go b/pkg/indicator/v2/trima.go new file mode 100644 index 0000000000..f0c5cb433e --- /dev/null +++ b/pkg/indicator/v2/trima.go @@ -0,0 +1,45 @@ +package indicatorv2 + +import "github.com/c9s/bbgo/pkg/types" + +type TrimaStream struct { + // embedded struct + *types.Float64Series + + trima *SMAStream +} + +// Trima function calculates the Triangular Moving Average (TRIMA). +// +// If period is even: +// +// TRIMA = SMA(period / 2, SMA((period / 2) + 1, values)) +// +// If period is odd: +// +// TRIMA = SMA((period + 1) / 2, SMA((period + 1) / 2, values)) +// +// Returns trima. +func Trima(source types.Float64Source, window int) *TrimaStream { + var n1, n2 int + + if window%2 == 0 { + n1 = window / 2 + n2 = n1 + 1 + } else { + n1 = (window + 1) / 2 + n2 = n1 + } + + var s = &TrimaStream{ + Float64Series: types.NewFloat64Series(), + trima: SMA(SMA(source, n2), n1), + } + + s.Bind(source, s) + return s +} + +func (s *TrimaStream) Calculate(_ float64) float64 { + return s.trima.Last(0) +} diff --git a/pkg/indicator/v2/ulcer_index.go b/pkg/indicator/v2/ulcer_index.go new file mode 100644 index 0000000000..97e3089738 --- /dev/null +++ b/pkg/indicator/v2/ulcer_index.go @@ -0,0 +1,46 @@ +package indicatorv2 + +import ( + "math" + + "github.com/c9s/bbgo/pkg/types" +) + +type UlcerIndexStream struct { + *types.Float64Series + sma *SMAStream +} + +// The Ulcer Index (UI) measures downside risk. The index increases in value +// as the price moves farther away from a recent high and falls as the price +// rises to new highs. +// +// High Closings = Max(period, Closings) +// Percentage Drawdown = 100 * ((Closings - High Closings) / High Closings) +// Squared Average = Sma(period, Percent Drawdown * Percent Drawdown) +// Ulcer Index = Sqrt(Squared Average) + +// https://www.investopedia.com/terms/a/UlcerIndex.asp +func UlcerIndex(source types.Float64Source, window int) *UlcerIndexStream { + s := &UlcerIndexStream{ + Float64Series: types.NewFloat64Series(), + sma: SMA(SquaredAverage(source, window), window), + } + + s.Bind(source, s) + + return s +} + +// The default ulcer index with the default period of 14. +func UlcerIndexDefault(source types.Float64Source) *UlcerIndexStream { + return UlcerIndex(source, 14) +} + +func (s *UlcerIndexStream) Calculate(_ float64) float64 { + return math.Sqrt(s.sma.Last(0)) +} + +func (s *UlcerIndexStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/ulcer_index_test.go b/pkg/indicator/v2/ulcer_index_test.go new file mode 100644 index 0000000000..fcaff116b5 --- /dev/null +++ b/pkg/indicator/v2/ulcer_index_test.go @@ -0,0 +1,23 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestUlcerIndex(t *testing.T) { + closing := []float64{9, 11, 7, 10, 8, 7, 7, 8, 10, 9, 5, 4, 6, 7} + expected := []float64{0, 0, 20.99, 18.74, 20.73, 24.05, 26.17, 26.31, + 24.99, 24.39, 28.49, 32.88, 34.02, 34.19} + + source := types.NewFloat64Series() + ind := UlcerIndexDefault(source) + + for _, d := range closing { + source.PushAndEmit(d) + } + assert.InDeltaSlice(t, expected, ind.Slice, 0.01) +} diff --git a/pkg/indicator/v2/util.go b/pkg/indicator/v2/util.go new file mode 100644 index 0000000000..293326fe42 --- /dev/null +++ b/pkg/indicator/v2/util.go @@ -0,0 +1,15 @@ +package indicatorv2 + +func Max(x, y int) int { + if x > y { + return x + } + return y +} + +func Min(x, y int) int { + if x < y { + return x + } + return y +} diff --git a/pkg/indicator/v2/vortex.go b/pkg/indicator/v2/vortex.go new file mode 100644 index 0000000000..41a514502d --- /dev/null +++ b/pkg/indicator/v2/vortex.go @@ -0,0 +1,90 @@ +package indicatorv2 + +import ( + "math" + + "github.com/c9s/bbgo/pkg/datatype/floats" + + "github.com/c9s/bbgo/pkg/types" +) + +// Vortex Indicator. It provides two oscillators that capture positive and +// negative trend movement. A bullish signal triggers when the positive +// trend indicator crosses above the negative trend indicator or a key +// level. A bearish signal triggers when the negative trend indicator +// crosses above the positive trend indicator or a key level. +// +// +VM = Abs(Current High - Prior Low) +// -VM = Abd(Current Low - Prior High) +// +// +VM14 = 14-Period Sum of +VM +// -VM14 = 14-Period Sum of -VM +// +// TR = Max((High[i]-Low[i]), Abs(High[i]-Closing[i-1]), Abs(Low[i]-Closing[i-1])) +// TR14 = 14-Period Sum of TR +// +// +VI14 = +VM14 / TR14 +// -VI14 = -VM14 / TR14 +// +// Based on https://school.stockcharts.com/doku.php?id=technical_indicators:vortex_indicator +type VortexStream struct { + plusVm, minusVm, tr floats.Slice + PlusVi, MinusVi *types.Float64Series + plusVmSum, minusVmSum, trSum float64 + window int +} + +func Vortex(source KLineSubscription) *VortexStream { + var ( + low = LowPrices(source) + high = HighPrices(source) + closing = ClosePrices(source) + window = 14 + s = &VortexStream{ + PlusVi: types.NewFloat64Series(), + MinusVi: types.NewFloat64Series(), + plusVm: make([]float64, window), + minusVm: make([]float64, window), + tr: make([]float64, window), + window: window, + } + ) + + source.AddSubscriber(func(v types.KLine) { + var ( + i = source.Length() + j = i % s.window + ) + if i == 1 { + s.PlusVi.PushAndEmit(0) + s.MinusVi.PushAndEmit(0) + return + } + s.plusVmSum -= s.plusVm[j] + s.plusVm[j] = math.Abs(high.Last(0) - low.Last(1)) + s.plusVmSum += s.plusVm[j] + + s.minusVmSum -= s.minusVm[j] + s.minusVm[j] = math.Abs(low.Last(0) - high.Last(1)) + s.minusVmSum += s.minusVm[j] + + var ( + highLow = high.Last(0) - low.Last(0) + highPrevClosing = math.Abs(high.Last(0) - closing.Last(1)) + lowPrevClosing = math.Abs(low.Last(0) - closing.Last(1)) + ) + + s.trSum -= s.tr[j] + s.tr[j] = math.Max(highLow, math.Max(highPrevClosing, lowPrevClosing)) + s.trSum += s.tr[j] + + s.PlusVi.PushAndEmit(s.plusVmSum / s.trSum) + s.MinusVi.PushAndEmit(s.minusVmSum / s.trSum) + }) + return s +} + +func (s *VortexStream) Truncate() { + s.PlusVi.Slice = s.PlusVi.Slice.Truncate(MaxNumOfMA) + s.MinusVi.Slice = s.MinusVi.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/vortex_test.go b/pkg/indicator/v2/vortex_test.go new file mode 100644 index 0000000000..a27668d903 --- /dev/null +++ b/pkg/indicator/v2/vortex_test.go @@ -0,0 +1,37 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestVortex(t *testing.T) { + high := []float64{1404.14, 1405.95, 1405.98, 1405.87, 1410.03} + low := []float64{1396.13, 1398.80, 1395.62, 1397.32, 1400.60} + closing := []float64{1402.22, 1402.80, 1405.87, 1404.11, 1403.93} + expectedPlusVi := []float64{0.00000, 1.37343, 0.97087, 1.04566, 1.12595} + expectedMinusVi := []float64{0.00000, 0.74685, 0.89492, 0.93361, 0.83404} + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := Vortex(kLines) + var ts []types.KLine + for i := range closing { + kline := types.KLine{Low: n(low[i]), High: n(high[i]), Close: n(closing[i])} + ts = append(ts, kline) + } + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + spew.Dump(ind) + for i, v := range expectedPlusVi { + assert.InDelta(t, v, ind.PlusVi.Slice[i], 0.01, "Expected Vortex.slice[%d] to be %v, but got %v", i, v, ind.PlusVi.Slice[i]) + } + for i, v := range expectedMinusVi { + assert.InDelta(t, v, ind.MinusVi.Slice[i], 0.01, "Expected Vortex.slice[%d] to be %v, but got %v", i, v, ind.MinusVi.Slice[i]) + } +} diff --git a/pkg/indicator/v2/vwap.go b/pkg/indicator/v2/vwap.go new file mode 100644 index 0000000000..7cc8fea59c --- /dev/null +++ b/pkg/indicator/v2/vwap.go @@ -0,0 +1,41 @@ +package indicatorv2 + +import ( + "gonum.org/v1/gonum/floats" + + "github.com/c9s/bbgo/pkg/types" +) + +type VWAPStream struct { + *types.Float64Series + window int +} + +// The Volume Weighted Average Price (VWAP) provides the average price +// the asset has traded weighted by volume. +// +// VWAP = Sum(Closing * Volume) / Sum(Volume) +func VWAP(source KLineSubscription, window int) *VWAPStream { + var ( + pv = CloseMulVolume(source) + volume = Volumes(source) + s = &VWAPStream{ + Float64Series: types.NewFloat64Series(), + window: window, + } + ) + source.AddSubscriber(func(v types.KLine) { + // var vwap = pv.Sum(window) / volume.Sum(s.window) // todo behaviour not the same?! + var vwap = floats.Sum(pv.Slice.Tail(s.window)) / floats.Sum(volume.Slice.Tail(s.window)) + s.PushAndEmit(vwap) + }) + return s +} + +func VwapDefault(source KLineSubscription) *VWAPStream { + return VWAP(source, 14) +} + +func (s *VWAPStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/vwap_test.go b/pkg/indicator/v2/vwap_test.go new file mode 100644 index 0000000000..663c93160c --- /dev/null +++ b/pkg/indicator/v2/vwap_test.go @@ -0,0 +1,32 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestVolumeWeightedAveragePrice(t *testing.T) { + ts := []types.KLine{ + {Volume: n(100), Close: n(9)}, + {Volume: n(110), Close: n(11)}, + {Volume: n(80), Close: n(7)}, + {Volume: n(120), Close: n(10)}, + {Volume: n(90), Close: n(8)}, + } + expected := []float64{9, 10.05, 9.32, 8.8, 9.14} + + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := VWAP(kLines, 2) + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + spew.Dump(ind) + for i, v := range expected { + assert.InDelta(t, v, ind.Slice[i], 0.01, "Expected VWAP.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } +} diff --git a/pkg/indicator/v2/vwma.go b/pkg/indicator/v2/vwma.go new file mode 100644 index 0000000000..c5d6d18653 --- /dev/null +++ b/pkg/indicator/v2/vwma.go @@ -0,0 +1,40 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +type VwmaStream struct { + *types.Float64Series + sma1 *SMAStream + sma2 *SMAStream + window int +} + +// The Vwma function calculates the Volume Weighted Moving Average (VWMA) +// averaging the price data with an emphasis on volume, meaning areas +// with higher volume will have a greater weight. +// +// VWMA = Sum(Price * Volume) / Sum(Volume) for a given Period. +func Vwma(source KLineSubscription, window int) *VwmaStream { + s := &VwmaStream{ + Float64Series: types.NewFloat64Series(), + sma1: SMA(CloseMulVolume(source), window), + sma2: SMA(Volumes(source), window), + window: window, + } + source.AddSubscriber(func(v types.KLine) { + var vwma = s.sma1.Last(0) / s.sma2.Last(0) + s.PushAndEmit(vwma) + }) + return s +} + +// The DefaultVwma function calculates VWMA with a period of 20. +func VwmaDefault(source KLineSubscription) *VwmaStream { + return Vwma(source, 20) +} + +func (s *VwmaStream) Calculate(_ float64) float64 { + return s.Slice.Last(0) +} diff --git a/pkg/indicator/v2/vwma_test.go b/pkg/indicator/v2/vwma_test.go new file mode 100644 index 0000000000..588a54847d --- /dev/null +++ b/pkg/indicator/v2/vwma_test.go @@ -0,0 +1,55 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/types" +) + +func TestVwma(t *testing.T) { + + tests := []struct { + name string + closing []float64 + volume []float64 + window int + want []float64 + }{ + { + name: "Valid sample", + closing: []float64{20, 21, 21, 19, 16}, + volume: []float64{100, 50, 40, 50, 100}, + window: 3, + want: []float64{20, 20.33, 20.47, 20.29, 17.84}, + }, + { + name: "Default window", + closing: []float64{20, 21, 21, 19, 16}, + volume: []float64{100, 50, 40, 50, 100}, + window: 20, + want: []float64{20, 20.33, 20.47, 20.17, 18.94}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := Vwma(kLines, tt.window) + var ts []types.KLine + for i := range tt.closing { + kline := types.KLine{Volume: n(tt.volume[i]), Close: n(tt.closing[i])} + ts = append(ts, kline) + } + for _, candle := range ts { + stream.EmitKLineClosed(candle) + } + spew.Dump(ind) + for i, v := range tt.want { + assert.InDelta(t, v, ind.Slice[i], 0.01, "Expected TEMA.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } + }) + } +} diff --git a/pkg/indicator/v2/williams_r.go b/pkg/indicator/v2/williams_r.go new file mode 100644 index 0000000000..082c804d3b --- /dev/null +++ b/pkg/indicator/v2/williams_r.go @@ -0,0 +1,54 @@ +package indicatorv2 + +import ( + "github.com/c9s/bbgo/pkg/types" +) + +// Williams R. Determine overbought and oversold. + +// Developed by Larry Williams, Williams %R is a momentum indicator that is +// the inverse of the Fast Stochastic Oscillator. Also referred to as %R, +// Williams %R reflects the level of the close relative to the highest high +// for the look-back period. In contrast, the Stochastic Oscillator reflects +// the level of the close relative to the lowest low. %R corrects for the +// inversion by multiplying the raw value by -100. As a result, the Fast +// Stochastic Oscillator and Williams %R produce the exact same lines, but +// with different scaling. Williams %R oscillates from 0 to -100; readings +// from 0 to -20 are considered overbought, while readings from -80 to -100 +// are considered oversold. Unsurprisingly, signals derived from the Stochastic +// Oscillator are also applicable to Williams %R. +// +// https://school.stockcharts.com/doku.php?id=technical_indicators:williams_r +// https://www.investopedia.com/terms/w/williamsr.asp +// +// WR = (Highest High - Closing) / (Highest High - Lowest Low) * -100. +// +// Buy when -80 and below. Sell when -20 and above. +type WilliamsRStream struct { + // embedded structs + *types.Float64Series + min *MinValueStream + max *MaxValueStream +} + +func WilliamsR(source KLineSubscription, window int) *WilliamsRStream { + s := &WilliamsRStream{ + Float64Series: types.NewFloat64Series(), + min: MinValue(LowPrices(source), window), + max: MaxValue(HighPrices(source), window), + } + source.AddSubscriber(func(v types.KLine) { + + highestHigh := s.max.Last(0) + lowestLow := s.min.Last(0) + var w = (highestHigh - v.Close.Float64()) / (highestHigh - lowestLow) * -100 + + s.PushAndEmit(w) + }) + + return s +} + +func (s *WilliamsRStream) Truncate() { + s.Slice = s.Slice.Truncate(MaxNumOfMA) +} diff --git a/pkg/indicator/v2/williams_r_test.go b/pkg/indicator/v2/williams_r_test.go new file mode 100644 index 0000000000..4ca92af82b --- /dev/null +++ b/pkg/indicator/v2/williams_r_test.go @@ -0,0 +1,60 @@ +package indicatorv2 + +import ( + "encoding/json" + "slices" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/c9s/bbgo/pkg/fixedpoint" + + "github.com/c9s/bbgo/pkg/types" +) + +func Test_WilliamsR(t *testing.T) { + high := []byte(`[127.0090,127.6159,126.5911,127.3472,128.1730,128.4317,127.3671,126.4220,126.8995,126.8498,125.6460,125.7156,127.1582,127.7154,127.6855,128.2228,128.2725,128.0934,128.2725,127.7353,128.7700,129.2873,130.0633,129.1182,129.2873,128.4715,128.0934,128.6506,129.1381,128.6406]`) + low := []byte(`[125.3574,126.1633,124.9296,126.0937,126.8199,126.4817,126.0340,124.8301,126.3921,125.7156,124.5615,124.5715,125.0689,126.8597,126.6309,126.8001,126.7105,126.8001,126.1335,125.9245,126.9891,127.8148,128.4715,128.0641,127.6059,127.5960,126.9990,126.8995,127.4865,127.3970]`) + close := []byte(`[125.3574,126.1633,124.9296,126.0937,126.8199,126.4817,126.0340,124.8301,126.3921,125.7156,124.5615,124.5715,125.0689,127.2876,127.1781,128.0138,127.1085,127.7253,127.0587,127.3273,128.7103,127.8745,128.5809,128.6008,127.9342,128.1133,127.5960,127.5960,128.6904,128.2725]`) + buildKLines := func(high, low, close []fixedpoint.Value) (kLines []types.KLine) { + for i := range high { + kLines = append(kLines, types.KLine{High: high[i], Low: low[i], Close: close[i]}) + } + return kLines + } + var h, l, c []fixedpoint.Value + _ = json.Unmarshal(high, &h) + _ = json.Unmarshal(low, &l) + _ = json.Unmarshal(close, &c) + + expected := []float64{ + -29.561779752984485, + -32.391090899695165, + -10.797891581830443, + -34.189447573768696, + -18.252286703529535, + -35.476202780218095, + -25.470223659391287, + -1.4185576808844131, + -29.89546743408507, + -26.943909266058082, + -26.582209458722687, + -38.76870971266242, + -39.04372897645341, + -59.61389774813938, + -59.61389774813938, + -33.171450662027304, + -43.26858026481079, + } + stream := &types.StandardStream{} + kLines := KLines(stream, "", "") + ind := WilliamsR(kLines, 14) + k := buildKLines(h, l, c) + for _, candle := range k { + stream.EmitKLineClosed(candle) + } + slices.Reverse(expected) + for i, v := range expected { + assert.InDelta(t, v, ind.Last(i), 0.000001, "Expected williamsR.slice[%d] to be %v, but got %v", i, v, ind.Slice[i]) + } +} diff --git a/pkg/indicator/v2/window.go b/pkg/indicator/v2/window.go new file mode 100644 index 0000000000..2e5b4892bb --- /dev/null +++ b/pkg/indicator/v2/window.go @@ -0,0 +1,24 @@ +package indicatorv2 + +// Window returns a copied slice of series starting at n index ago. +func Window[T any](series []T, n int) []T { + + ln := len(series) + if ln <= n { + window := make([]T, ln) + copy(window, series) + return window + } + + i := (ln - n) - 1 + window := make([]T, n+1) + copy(window, series[i:]) + + return window +} + +// WindowAppend appends a value to the end of the series and slices it to the window starting at n index ago. +// Semantics of n argument are the same as Window function. +func WindowAppend[T any](series []T, n int, v T) []T { + return Window(append(series, v), n) +} diff --git a/pkg/indicator/v2/window_test.go b/pkg/indicator/v2/window_test.go new file mode 100644 index 0000000000..ac12f00bc5 --- /dev/null +++ b/pkg/indicator/v2/window_test.go @@ -0,0 +1,85 @@ +package indicatorv2 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestWindow(t *testing.T) { + tests := []struct { + name string + giveSeries []float64 + giveN int + want []float64 + }{ + { + name: "Latest value only", + giveSeries: []float64{0, 1, 2, 3, 4, 5}, + giveN: 0, + want: []float64{5}, + }, + { + name: "All values", + giveSeries: []float64{0, 1, 2, 3, 4, 5}, + giveN: 6, + want: []float64{0, 1, 2, 3, 4, 5}, + }, + { + name: "Index out of range - returns all values", + giveSeries: []float64{0, 1, 2, 3, 4, 5}, + giveN: 7, + want: []float64{0, 1, 2, 3, 4, 5}, + }, + { + name: "Sub window", + giveSeries: []float64{0, 1, 2, 3, 4, 5}, + giveN: 3, + want: []float64{2, 3, 4, 5}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + act := Window(tt.giveSeries, tt.giveN) + assert.Equal(t, tt.want, act) + }) + } +} + +func TestWindowAppend(t *testing.T) { + tests := []struct { + name string + giveSeries []float64 + giveN int + giveV float64 + want []float64 + }{ + { + name: "Append and no slice", + giveSeries: []float64{0, 1, 2, 3, 4, 5}, + giveN: 7, + giveV: 99, + want: []float64{0, 1, 2, 3, 4, 5, 99}, + }, + { + name: "Append and slice", + giveSeries: []float64{0, 1, 2, 3, 4, 5}, + giveN: 2, + giveV: 99, + want: []float64{4, 5, 99}, + }, + { + name: "Append and slice to new value only", + giveSeries: []float64{0, 1, 2, 3, 4, 5}, + giveN: 0, + giveV: 99, + want: []float64{99}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + act := WindowAppend(tt.giveSeries, tt.giveN, tt.giveV) + assert.Equal(t, tt.want, act) + }) + } +} diff --git a/pkg/report/profit_report.go b/pkg/report/profit_report.go index 065a425011..bb20866047 100644 --- a/pkg/report/profit_report.go +++ b/pkg/report/profit_report.go @@ -2,12 +2,13 @@ package report import ( "fmt" + "strconv" + "github.com/c9s/bbgo/pkg/data/tsv" "github.com/c9s/bbgo/pkg/datatype/floats" "github.com/c9s/bbgo/pkg/fixedpoint" indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/types" - "strconv" ) // AccumulatedProfitReport For accumulated profit report output diff --git a/pkg/strategy/bollmaker/strategy.go b/pkg/strategy/bollmaker/strategy.go index d8f32b8c25..6dcbf5ab14 100644 --- a/pkg/strategy/bollmaker/strategy.go +++ b/pkg/strategy/bollmaker/strategy.go @@ -6,15 +6,14 @@ import ( "math" "sync" - indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" - "github.com/c9s/bbgo/pkg/util" - "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/fixedpoint" + indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/types" + "github.com/c9s/bbgo/pkg/util" ) // TODO: diff --git a/pkg/strategy/bollmaker/trend.go b/pkg/strategy/bollmaker/trend.go index 0ff0cc9d39..a3faac0b10 100644 --- a/pkg/strategy/bollmaker/trend.go +++ b/pkg/strategy/bollmaker/trend.go @@ -1,8 +1,6 @@ package bollmaker -import ( - indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" -) +import indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" type PriceTrend string diff --git a/pkg/strategy/scmaker/strategy.go b/pkg/strategy/scmaker/strategy.go index ecccefc8b0..2dbbab2ea2 100644 --- a/pkg/strategy/scmaker/strategy.go +++ b/pkg/strategy/scmaker/strategy.go @@ -11,7 +11,7 @@ import ( "github.com/c9s/bbgo/pkg/bbgo" "github.com/c9s/bbgo/pkg/fixedpoint" - . "github.com/c9s/bbgo/pkg/indicator/v2" + indicatorv2 "github.com/c9s/bbgo/pkg/indicator/v2" "github.com/c9s/bbgo/pkg/risk/riskcontrol" "github.com/c9s/bbgo/pkg/strategy/common" "github.com/c9s/bbgo/pkg/types" @@ -74,8 +74,8 @@ type Strategy struct { liquidityScale bbgo.Scale // indicators - ewma *EWMAStream - boll *BOLLStream + ewma *indicatorv2.EWMAStream + boll *indicatorv2.BOLLStream intensity *IntensityStream positionRiskControl *riskcontrol.PositionRiskControl @@ -174,7 +174,7 @@ func (s *Strategy) Run(ctx context.Context, orderExecutor bbgo.OrderExecutor, se return nil } -func (s *Strategy) preloadKLines(inc *KLineStream, session *bbgo.ExchangeSession, symbol string, interval types.Interval) { +func (s *Strategy) preloadKLines(inc *indicatorv2.KLineStream, session *bbgo.ExchangeSession, symbol string, interval types.Interval) { if store, ok := session.MarketDataStore(symbol); ok { if kLinesData, ok := store.KLinesOfInterval(interval); ok { for _, k := range *kLinesData { @@ -185,23 +185,23 @@ func (s *Strategy) preloadKLines(inc *KLineStream, session *bbgo.ExchangeSession } func (s *Strategy) initializeMidPriceEMA(session *bbgo.ExchangeSession) { - kLines := KLines(session.MarketDataStream, s.Symbol, s.MidPriceEMA.Interval) - s.ewma = EWMA2(ClosePrices(kLines), s.MidPriceEMA.Window) + kLines := indicatorv2.KLines(session.MarketDataStream, s.Symbol, s.MidPriceEMA.Interval) + s.ewma = indicatorv2.EWMA2(indicatorv2.ClosePrices(kLines), s.MidPriceEMA.Window) s.preloadKLines(kLines, session, s.Symbol, s.MidPriceEMA.Interval) } func (s *Strategy) initializeIntensityIndicator(session *bbgo.ExchangeSession) { - kLines := KLines(session.MarketDataStream, s.Symbol, s.StrengthInterval) + kLines := indicatorv2.KLines(session.MarketDataStream, s.Symbol, s.StrengthInterval) s.intensity = Intensity(kLines, 10) s.preloadKLines(kLines, session, s.Symbol, s.StrengthInterval) } func (s *Strategy) initializePriceRangeBollinger(session *bbgo.ExchangeSession) { - kLines := KLines(session.MarketDataStream, s.Symbol, s.PriceRangeBollinger.Interval) - closePrices := ClosePrices(kLines) - s.boll = BOLL(closePrices, s.PriceRangeBollinger.Window, s.PriceRangeBollinger.K) + kLines := indicatorv2.KLines(session.MarketDataStream, s.Symbol, s.PriceRangeBollinger.Interval) + closePrices := indicatorv2.ClosePrices(kLines) + s.boll = indicatorv2.BOLL(closePrices, s.PriceRangeBollinger.Window, s.PriceRangeBollinger.K) s.preloadKLines(kLines, session, s.Symbol, s.PriceRangeBollinger.Interval) } diff --git a/pkg/types/bst/bst.go b/pkg/types/bst/bst.go new file mode 100644 index 0000000000..fbfc6fe8be --- /dev/null +++ b/pkg/types/bst/bst.go @@ -0,0 +1,94 @@ +package bst + +// BST node. +type Node struct { + value interface{} + left *Node + right *Node +} + +// BST type. +type Tree struct { + root *Node +} + +// New binary search tree. +func New() *Tree { + return &Tree{} +} + +// Inserts the given value. +func (t *Tree) Insert(value interface{}) { + newNode := &Node{ + value: value, + } + + if t.root == nil { + t.root = newNode + return + } + + curNode := t.root + + for { + if Compare(newNode.value, curNode.value) <= 0 { + if curNode.left == nil { + curNode.left = newNode + return + } else { + curNode = curNode.left + } + } else { + if curNode.right == nil { + curNode.right = newNode + return + } else { + curNode = curNode.right + } + } + } +} + +// Removes the given value. +func (t *Tree) Remove(value interface{}) bool { + var parent *Node + node := t.root + + for node != nil { + switch Compare(value, node.value) { + case 0: + t.removeNode(parent, node) + return true + + case -1: + parent = node + node = node.left + + case 1: + parent = node + node = node.right + } + } + + return false +} + +// Min value. +func (t *Tree) Min() interface{} { + node, _ := minNode(t.root) + if node == nil { + return nil + } + + return node.value +} + +// Max value. +func (t *Tree) Max() interface{} { + node, _ := maxNode(t.root) + if node == nil { + return nil + } + + return node.value +} diff --git a/pkg/types/bst/bst_test.go b/pkg/types/bst/bst_test.go new file mode 100644 index 0000000000..6b5855fe6d --- /dev/null +++ b/pkg/types/bst/bst_test.go @@ -0,0 +1,57 @@ +package bst + +import ( + "testing" +) + +func TestInsertAndRemove(t *testing.T) { + values := []float64{2, 1, 3, 4, 0, 6, 6, 10, -1, 9} + + tree := New() + + for _, value := range values { + tree.Insert(value) + } + + for _, value := range values { + if !tree.Remove(value) { + t.Fatalf("unable to remove %f", value) + } + } +} + +func TestMinAndMax(t *testing.T) { + values := []float64{2, 1, 3, 4, 0, 6, 6, 10, -1, 9} + mins := []float64{2, 1, 1, 1, 0, 0, 0, 0, -1, -1} + maxs := []float64{2, 2, 3, 4, 4, 6, 6, 10, 10, 10} + + tree := New() + + for i := 0; i < len(values); i++ { + tree.Insert(values[i]) + + min := tree.Min() + if min != mins[i] { + t.Fatalf("at %d actual %f expected %f", i, min, mins[i]) + } + + max := tree.Max() + if max != maxs[i] { + t.Fatalf("at %d actual %f expected %f", i, max, maxs[i]) + } + } + + for i := len(values) - 1; i > 0; i-- { + tree.Remove(values[i]) + + min := tree.Min() + if min != mins[i-1] { + t.Fatalf("at %d actual %f expected %f", i, min, mins[i-1]) + } + + max := tree.Max() + if max != maxs[i-1] { + t.Fatalf("at %d actual %f expected %f", i, max, maxs[i-1]) + } + } +} diff --git a/pkg/types/bst/compare.go b/pkg/types/bst/compare.go new file mode 100644 index 0000000000..5f3053cd2f --- /dev/null +++ b/pkg/types/bst/compare.go @@ -0,0 +1,115 @@ +package bst + +// Comparable interface. +type Comparable interface { + // Compare with other value. Returns -1 if less than, 0 if + // equals, and 1 if greather than the other value. + Compare(other Comparable) int +} + +// Compares first and second values. The given values must be +// numeric or must implement Comparable interface. +// +// Returns -1 if less than, 0 if equals, 1 if greather than. +func Compare(first, second interface{}) int { + if _, ok := first.(Comparable); ok { + return first.(Comparable).Compare(second.(Comparable)) + } + + switch first.(type) { + + case float64: + return compareFloat64(first.(float64), second.(float64)) + + case int64: + return compareInt64(first.(int64), second.(int64)) + } + + panic("not comparable") +} + +func compareFloat64(first, second float64) int { + if first < second { + return -1 + } + + if first > second { + return 1 + } + + return 0 +} + +func compareInt64(first, second int64) int { + if first < second { + return -1 + } + + if first > second { + return 1 + } + + return 0 +} + +// Remove node. +func (t *Tree) removeNode(parent, node *Node) { + if node.left != nil && node.right != nil { + min, minParent := minNode(node.right) + if minParent == nil { + minParent = node + } + + t.removeNode(minParent, min) + node.value = min.value + } else { + var child *Node + if node.left != nil { + child = node.left + } else { + child = node.right + } + + if node == t.root { + t.root = child + } else if parent.left == node { + parent.left = child + } else { + parent.right = child + } + } +} + +// Min node. Returns min node and its parent. +func minNode(root *Node) (*Node, *Node) { + if root == nil { + return nil, nil + } + + var parent *Node + node := root + + for node.left != nil { + parent = node + node = node.left + } + + return node, parent +} + +// Max node. Returns max node and its parent. +func maxNode(root *Node) (*Node, *Node) { + if root == nil { + return nil, nil + } + + var parent *Node + node := root + + for node.right != nil { + parent = node + node = node.right + } + + return node, parent +} diff --git a/pkg/types/interval.go b/pkg/types/interval.go index 689da68e71..d4ff8e4fbc 100644 --- a/pkg/types/interval.go +++ b/pkg/types/interval.go @@ -64,6 +64,61 @@ func (i Interval) Duration() time.Duration { return time.Duration(i.Milliseconds()) * time.Millisecond } +// Convert determines the candle open time from a given timestamp +// eg interval 1 hour and tick at timestamp 00:58 will return timestamp shifted to 00:00 +func (i Interval) Convert(ts time.Time) time.Time { + var start = time.Date(ts.Year(), ts.Month(), ts.Day(), ts.Hour(), ts.Second(), 0, 0, ts.Location()) + switch i { + case Interval1s: // default start as defined above + case Interval1m: + return time.Date(ts.Year(), ts.Month(), ts.Day(), ts.Hour(), ts.Minute(), 0, 0, ts.Location()) + case Interval3m: + return shiftMinute(ts, 3) + case Interval5m: + return shiftMinute(ts, 5) + case Interval15m: + return shiftMinute(ts, 15) + case Interval30m: + return shiftMinute(ts, 30) + case Interval1h: + return time.Date(ts.Year(), ts.Month(), ts.Day(), ts.Hour(), 0, 0, 0, ts.Location()) + case Interval2h: + return shiftHour(ts, 2) + case Interval4h: + return shiftHour(ts, 4) + case Interval6h: + return shiftHour(ts, 6) + case Interval12h: + return shiftHour(ts, 12) + case Interval1d: + return time.Date(ts.Year(), ts.Month(), ts.Day(), 0, 0, 0, 0, ts.Location()) + case Interval3d: + return shiftDay(ts, 3) + case Interval1w: + return shiftDay(ts, 7) + case Interval2w: + return shiftDay(ts, 14) + case Interval1mo: + return time.Date(ts.Year(), ts.Month(), 0, 0, 0, 0, 0, ts.Location()) + } + return start +} + +func shiftDay(ts time.Time, shift int) time.Time { + day := ts.Day() - (ts.Day() % shift) + return time.Date(ts.Year(), ts.Month(), day, 0, 0, 0, 0, ts.Location()) +} + +func shiftHour(ts time.Time, shift int) time.Time { + hour := ts.Hour() - (ts.Hour() % shift) + return time.Date(ts.Year(), ts.Month(), ts.Day(), hour, 0, 0, 0, ts.Location()) +} + +func shiftMinute(ts time.Time, shift int) time.Time { + minute := ts.Minute() - (ts.Minute() % shift) + return time.Date(ts.Year(), ts.Month(), ts.Day(), ts.Hour(), minute, 0, 0, ts.Location()) +} + func (i *Interval) UnmarshalJSON(b []byte) (err error) { var a string err = json.Unmarshal(b, &a) diff --git a/pkg/types/kline.go b/pkg/types/kline.go index 6bf863c30b..7d0c67d267 100644 --- a/pkg/types/kline.go +++ b/pkg/types/kline.go @@ -673,6 +673,22 @@ func KLineHLC3Mapper(k KLine) float64 { return k.High.Add(k.Low).Add(k.Close).Div(Three).Float64() } +func KLineHLC3xVMapper(k KLine) float64 { + return k.High.Add(k.Low).Add(k.Close).Div(Three).Mul(k.Volume).Float64() +} + +func KLineHL2Mapper(k KLine) float64 { + return k.High.Add(k.Low).Div(Two).Float64() +} + +func KLineCxVMapper(k KLine) float64 { + return k.Close.Mul(k.Volume).Float64() +} + +func KLineCOMapper(k KLine) float64 { + return k.Close.Sub(k.Open).Float64() +} + func MapKLinePrice(kLines []KLine, f KLineValueMapper) (prices []float64) { for _, k := range kLines { prices = append(prices, f(k))