diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..9a9b53c --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,13 @@ +name: golangci-lint +on: + push: +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + version: v1.52.2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..55c2b08 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,21 @@ +name: test +on: + push: +jobs: + test: + strategy: + matrix: + platform: [ ubuntu-latest ] + runs-on: ${{ matrix.platform }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Install go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + - name: Test + run: go test -gcflags='-N -l' -race -coverprofile=coverage.txt ./... + - name: Upload coverage to Codecov + run: bash <(curl -s https://codecov.io/bash) + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f496f37 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +.PHONY: help lint test doc +.DEFAULT_GOAL := help + +help: + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +pre-push: lint test ## Run golang lint and test + +lint: ## Run golang lint using docker + go mod download + docker run --rm \ + -v ${GOPATH}/pkg/mod:/go/pkg/mod \ + -v ${PWD}:/app \ + -w /app \ + golangci/golangci-lint:v1.52.2 \ + golangci-lint run -v --modules-download-mode=readonly + +test: ## Run tests + go test ./... diff --git a/README.md b/README.md index c9c69b5..3b35f70 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ fmt.Println(atrIndicator.Calculate(1)) // 22.84552 ### Trend -The indicator returns a trend direction. It bases on fast (with shorter period) and slow EMA. The third parameter of NewTrend allows setting max difference between fast and slow EMA when Calculate returns the flat. +The indicator returns a trend direction. It bases on fast (with shorter period) and slow EMA. The third parameter (flatMaxDiff) of NewTrend allows setting max difference between fast and slow EMA when Calculate returns the flat. Option TrendWithFlatMaxDiffInPercent allows to pass flatMaxDiff in percent ```go fastEMAIndicator, err := indicator.NewExponentialMovingAverage(series, 14) @@ -88,7 +88,7 @@ if err != nil { log.Fatalln(err) } -trendIndicator := indicator.NewTrend(fastEMAIndicator, slowEMAIndicator, 0.6) +trendIndicator := indicator.NewTrend(fastEMAIndicator, slowEMAIndicator, 0.6, TrendWithFlatMaxDiffInPercent(false)) trend := trendIndicator.Calculate(1) switch trend { case indicator.UpTrend: diff --git a/indicator/trend.go b/indicator/trend.go index 8ff31c6..9783c49 100644 --- a/indicator/trend.go +++ b/indicator/trend.go @@ -8,33 +8,54 @@ const ( DownTrend float64 = -1 ) +type TrendOption func(*Trend) + +// TrendWithFlatMaxDiffInPercent allows to pass flatMaxDiff in percent. +// The default value is false +func TrendWithFlatMaxDiffInPercent(val bool) func(*Trend) { + return func(t *Trend) { + t.flatMaxDiffInPercent = val + } +} + // Trend returns a trend direction. // It bases on fast (with shorter period) and slow EMA. // flatMaxDiff allows setting max difference between // fast and slow EMA when Calculate returns the flat. type Trend struct { - fastEMAIndicator Indicator - slowEMAIndicator Indicator - flatMaxDiff float64 + fastEMAIndicator Indicator + slowEMAIndicator Indicator + flatMaxDiff float64 + flatMaxDiffInPercent bool } func NewTrend( fastEMAIndicator Indicator, slowEMAIndicator Indicator, flatMaxDiff float64, + opts ...TrendOption, ) *Trend { - return &Trend{ + trend := &Trend{ fastEMAIndicator: fastEMAIndicator, slowEMAIndicator: slowEMAIndicator, flatMaxDiff: flatMaxDiff, } + for _, opt := range opts { + opt(trend) + } + return trend } func (t *Trend) Calculate(index int) float64 { fastVal := t.fastEMAIndicator.Calculate(index) slowVal := t.slowEMAIndicator.Calculate(index) - if math.Abs(fastVal-slowVal)-t.flatMaxDiff <= 1e-6 { + flatMaxDiff := t.flatMaxDiff + if t.flatMaxDiffInPercent { + flatMaxDiff = slowVal * t.flatMaxDiff / 100 + } + + if math.Abs(fastVal-slowVal)-flatMaxDiff <= 1e-6 { return FlatTrend } if fastVal > slowVal { diff --git a/indicator/trend_test.go b/indicator/trend_test.go index bc26681..63102eb 100644 --- a/indicator/trend_test.go +++ b/indicator/trend_test.go @@ -8,10 +8,11 @@ import ( func TestTrend_Calculate(t *testing.T) { tests := []struct { - name string - fastVal float64 - slowVal float64 - want float64 + name string + fastVal float64 + slowVal float64 + flatMaxDiffInPercent bool + want float64 }{ { name: "up trend", @@ -37,6 +38,20 @@ func TestTrend_Calculate(t *testing.T) { slowVal: 1.0, want: FlatTrend, }, + { + name: "flat trend, diff in percent", + fastVal: 100.5, + slowVal: 100, + flatMaxDiffInPercent: true, + want: FlatTrend, + }, + { + name: "up trend, diff in percent", + fastVal: 100.7, + slowVal: 100, + flatMaxDiffInPercent: true, + want: UpTrend, + }, } for _, tt := range tests { @@ -44,7 +59,12 @@ func TestTrend_Calculate(t *testing.T) { slowEMAIndicator := &MockIndicator{} fastEMAIndicator := &MockIndicator{} - ind := NewTrend(fastEMAIndicator, slowEMAIndicator, 0.6) + ind := NewTrend( + fastEMAIndicator, + slowEMAIndicator, + 0.6, + TrendWithFlatMaxDiffInPercent(tt.flatMaxDiffInPercent), + ) fastEMAIndicator.On("Calculate", 1).Return(tt.fastVal) slowEMAIndicator.On("Calculate", 1).Return(tt.slowVal)