From 517f53cfc2b17083cc10bd68e203e60db9563d71 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Sun, 24 May 2026 20:22:59 -0700 Subject: [PATCH] feat(helper): implement IsRising utility Adds IsRising[T Number](c <-chan T, period int) <-chan T to the helper package. Returns 1 when the current value strictly exceeds the value n periods ago, 0 otherwise. 100% test coverage. Closes #369 Signed-off-by: Sai Asish Y --- helper/is_rising.go | 27 +++++++++++++++ helper/is_rising_test.go | 71 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 helper/is_rising.go create mode 100644 helper/is_rising_test.go diff --git a/helper/is_rising.go b/helper/is_rising.go new file mode 100644 index 0000000..0c85e6d --- /dev/null +++ b/helper/is_rising.go @@ -0,0 +1,27 @@ +// Copyright (c) 2021-2026 Onur Cinar. +// The source code is provided under GNU AGPLv3 License. +// https://github.com/cinar/indicator + +package helper + +// IsRising returns 1 if the current value is strictly greater than the value +// n periods ago, and 0 otherwise. +// +// Example: +// +// input := []int{1, 2, 5, 5, 8, 2, 1, 1, 3, 4} +// output := helper.IsRising(helper.SliceToChan(input), 2) +// fmt.Println(helper.ChanToSlice(output)) // [1, 1, 1, 0, 0, 0, 1, 1] +func IsRising[T Number](c <-chan T, period int) <-chan T { + cs := Duplicate(c, 2) + cs[0] = Buffered(cs[0], period) + cs[1] = Skip(cs[1], period) + + return Operate(cs[1], cs[0], func(current, previous T) T { + if current > previous { + return 1 + } + + return 0 + }) +} diff --git a/helper/is_rising_test.go b/helper/is_rising_test.go new file mode 100644 index 0000000..9694dfc --- /dev/null +++ b/helper/is_rising_test.go @@ -0,0 +1,71 @@ +// Copyright (c) 2021-2026 Onur Cinar. +// The source code is provided under GNU AGPLv3 License. +// https://github.com/cinar/indicator + +package helper_test + +import ( + "testing" + + "github.com/cinar/indicator/v2/helper" +) + +func TestIsRising(t *testing.T) { + input := helper.SliceToChan([]int{1, 2, 5, 5, 8, 2, 1, 1, 3, 4}) + expected := helper.SliceToChan([]int{1, 1, 1, 0, 0, 0, 1, 1}) + + actual := helper.IsRising(input, 2) + + err := helper.CheckEquals(actual, expected) + if err != nil { + t.Fatal(err) + } +} + +func TestIsRisingDefaultPeriod(t *testing.T) { + input := helper.SliceToChan([]int{1, 3, 2, 5, 4}) + expected := helper.SliceToChan([]int{1, 0, 1, 0}) + + actual := helper.IsRising(input, 1) + + err := helper.CheckEquals(actual, expected) + if err != nil { + t.Fatal(err) + } +} + +func TestIsRisingAllRising(t *testing.T) { + input := helper.SliceToChan([]int{1, 2, 3, 4, 5}) + expected := helper.SliceToChan([]int{1, 1, 1, 1}) + + actual := helper.IsRising(input, 1) + + err := helper.CheckEquals(actual, expected) + if err != nil { + t.Fatal(err) + } +} + +func TestIsRisingNoneRising(t *testing.T) { + input := helper.SliceToChan([]int{5, 4, 3, 2, 1}) + expected := helper.SliceToChan([]int{0, 0, 0, 0}) + + actual := helper.IsRising(input, 1) + + err := helper.CheckEquals(actual, expected) + if err != nil { + t.Fatal(err) + } +} + +func TestIsRisingEqual(t *testing.T) { + input := helper.SliceToChan([]int{3, 3, 3, 3}) + expected := helper.SliceToChan([]int{0, 0, 0}) + + actual := helper.IsRising(input, 1) + + err := helper.CheckEquals(actual, expected) + if err != nil { + t.Fatal(err) + } +}