diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..11864d0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,31 +8,17 @@ assignees: '' --- **Describe the bug** + A clear and concise description of what the bug is. -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error +**List References** -**Expected behavior** -A clear and concise description of what you expected to happen. +List references to your resources. -**Screenshots** -If applicable, add screenshots to help explain your problem. +**To Reproduce** -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] +Provide an example demonstrating the bug. -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] +**Expected behavior** -**Additional context** -Add any other context about the problem here. +Provide a test data showing the expected behavior. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7..f0291e0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -8,13 +8,17 @@ assignees: '' --- **Is your feature request related to a problem? Please describe.** + A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** + A clear and concise description of what you want to happen. **Describe alternatives you've considered** + A clear and concise description of any alternative solutions or features you've considered. **Additional context** + Add any other context or screenshots about the feature request here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index aad738c..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "gomod" - directory: "/" - schedule: - interval: "daily" - - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0401b47..52aec37 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,9 +1,18 @@ # Describe Request -Please describe your request. +Please describe your request. Fixed # (issue). -Fixed # (issue) +- [ ] Code coverage is maintained or increased. +- [ ] All parameters are configurable. +- [ ] Test data file provided. +- [ ] Links to any references. # Change Type What is the type of this change. + +- [ ] New indicator is added. +- [ ] New strategy is added. +- [ ] Other improvments. +- [ ] Code maintenance. +- [ ] Bug fix. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2360c3..a732d1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: Build on: push: - branches: [ "main" ] + branches: [ "main", "v2" ] pull_request: - branches: [ "main" ] + branches: [ "main", "v2" ] jobs: @@ -26,6 +26,9 @@ jobs: with: args: ./... + - name: Run Revive + uses: docker://morphy/revive-action:v2 + - name: Go fix run: go fix ./... @@ -33,7 +36,7 @@ jobs: run: go build -v ./... - name: Go test - run: go test -v -coverprofile=coverage.out ./... + run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 diff --git a/.replit b/.replit deleted file mode 100644 index 922556e..0000000 --- a/.replit +++ /dev/null @@ -1,2 +0,0 @@ -language = "go" -run = "go test" \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..242498e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Contributing to Indicator + +Welcome to the Indicator library! We appreciate your interest in contributing. Before you start, please take a moment to read our [Contributor Covenant Code of Conduct](./CODE_OF_CONDUCT.md). + +## Who Can Contribute? + +Anyone is welcome to contribute to the Indicator library. No prior experience is required, but having some knowledge of coding is helpful. + +## How to Contribute? + +### Finding a Bug? + +1. Check if the bug has already been reported by searching [Project Issues](https://github.com/cinar/indicator/issues). +2. If no open issue addresses the problem, create a new one. +3. Use the relevant bug report templates, providing a clear title, detailed description, and, if possible, a code sample or executable test case demonstrating the issue. + +### Writing a Fix? + +1. Open a new GitHub pull request with your patch. +2. The project maintainers will review pull requests, merging them if they meet coding standards and are approved. + +## Coding Standards + +The project adheres to Go coding standards. + +### Code Quality + +- The Indicator library maintains 100% code coverage for reliability. +- Test cases are located in _test.go files, covering all possible scenarios for user input validation. +- Clearly explain each function and provide references. + +### Pull Requests + +- Ensure code coverage remains at 100% by adding sufficient test cases. +- Test large data sets using CSV files. + +Thank you for contributing to make the Indicator library better. Your efforts help ensure the reliability and correctness of user data handling. We look forward to your contributions! diff --git a/README.md b/README.md index 962c217..2bb4a62 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,22 @@ [![GoDoc](https://godoc.org/github.com/cinar/indicator?status.svg)](https://godoc.org/github.com/cinar/indicator) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) -[![Build Status](https://github.com/cinar/indicator/actions/workflows/ci.yml/badge.svg)](https://github.com/cinar/indicator/actions/workflows/ci.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/cinar/indicator)](https://goreportcard.com/report/github.com/cinar/indicator) +![Go CI](https://github.com/cinar/indicator/actions/workflows/ci.yml/badge.svg) [![codecov](https://codecov.io/gh/cinar/indicator/branch/master/graph/badge.svg?token=MB7L69UAWM)](https://codecov.io/gh/cinar/indicator) # Indicator Go -Indicator is a Golang module providing various stock technical analysis indicators, strategies, and a backtest framework for trading. +Indicator is a Golang module providing a comprehensive set of technical analysis indicators, strategies, and a backtest framework. + +You can find the [v1 version](https://github.com/cinar/indicator) of the library in the `v1` branch. + +The [v2 version](https://github.com/cinar/indicator/tree/v2) is a complete rewrite of the library with the following goals: + +- Achieving and maintaining 100% code coverage, along with test files for each indicator and strategy. +- Operating on data streams (Go channels) for both inputs and outputs. If you prefer using slices, helper functions like [helper.SliceToChan](helper/README.md#func-slicetochan) and [helper.ChanToSlice](helper/README.md#func-chantoslice) are available. Alternatively, you can still use the `v1 version`. +- Supporting all numeric formats using Golang generics. + +Not everything has been fully ported from `v1 version` to `v2 version`. Any indicator or strategy without a link to documentation is not currently implemented in the `v2 version`. Your contributions are highly welcomed. Feel free to contribute to the project and help us expand the library. *I also have a TypeScript version of this module now at [Indicator TS](https://github.com/cinar/indicatorts).* @@ -16,124 +26,124 @@ The following list of indicators are currently supported by this package: ### Trend Indicators -- [Absolute Price Oscillator (APO)](trend_indicators.md#absolute-price-oscillator-apo) -- [Aroon Indicator](trend_indicators.md#aroon-indicator) -- [Balance of Power (BOP)](trend_indicators.md#balance-of-power-bop) -- [Chande Forecast Oscillator (CFO)](trend_indicators.md#chande-forecast-oscillator-cfo) -- [Community Channel Index (CMI)](trend_indicators.md#community-channel-index-cmi) -- [Double Exponential Moving Average (DEMA)](trend_indicators.md#double-exponential-moving-average-dema) -- [Exponential Moving Average (EMA)](trend_indicators.md#exponential-moving-average-ema) -- [Mass Index (MI)](trend_indicators.md#mass-index-mi) -- [Moving Average Convergence Divergence (MACD)](trend_indicators.md#moving-average-convergence-divergence-macd) -- [Moving Max](trend_indicators.md#moving-max) -- [Moving Min](trend_indicators.md#moving-min) -- [Moving Sum](trend_indicators.md#moving-sum) -- [Parabolic SAR](trend_indicators.md#parabolic-sar) -- [Qstick](trend_indicators.md#qstick) -- [Random Index (KDJ)](trend_indicators.md#random-index-kdj) -- [Rolling Moving Average (RMA)](trend_indicators.md#rolling-moving-average-rma) -- [Simple Moving Average (SMA)](trend_indicators.md#simple-moving-average-sma) -- [Since Change](trend_indicators.md#since-change) -- [Triple Exponential Moving Average (TEMA)](trend_indicators.md#triple-exponential-moving-average-tema) -- [Triangular Moving Average (TRIMA)](trend_indicators.md#triangular-moving-average-trima) -- [Triple Exponential Average (TRIX)](trend_indicators.md#triple-exponential-average-trix) -- [Typical Price](trend_indicators.md#typical-price) -- [Volume Weighted Moving Average (VWMA)](trend_indicators.md#volume-weighted-moving-average-vwma) -- [Vortex Indicator](trend_indicators.md#vortex-indicator) +- [Absolute Price Oscillator (APO)](trend/README.md#type-apo) +- [Aroon Indicator](trend/README.md#type-aroon) +- [Balance of Power (BOP)](trend/README.md#type-bop) +- Chande Forecast Oscillator (CFO) +- Community Channel Index (CMI) +- Double Exponential Moving Average (DEMA) +- [Exponential Moving Average (EMA)](trend/README.md#type-ema) +- Mass Index (MI) +- Moving Average Convergence Divergence (MACD) +- [Moving Max](trend/README.md#func-movingmax) +- [Moving Min](trend/README.md#func-movingmin) +- [Moving Sum](trend/README.md#func-movingsum) +- Parabolic SAR +- Qstick +- Random Index (KDJ) +- Rolling Moving Average (RMA) +- [Simple Moving Average (SMA)](trend/README.md#type-sma) +- [Since Change](helper/README.md#func-since) +- Triple Exponential Moving Average (TEMA) +- Triangular Moving Average (TRIMA) +- Triple Exponential Average (TRIX) +- Typical Price +- Volume Weighted Moving Average (VWMA) +- Vortex Indicator ### Momentum Indicators -- [Awesome Oscillator](momentum_indicators.md#awesome-oscillator) -- [Chaikin Oscillator](momentum_indicators.md#chaikin-oscillator) -- [Ichimoku Cloud](momentum_indicators.md#ichimoku-cloud) -- [Percentage Price Oscillator (PPO)](momentum_indicators.md#percentage-price-oscillator-ppo) -- [Percentage Volume Oscillator (PVO)](momentum_indicators.md#percentage-volume-oscillator-pvo) -- [Relative Strength Index (RSI)](momentum_indicators.md#relative-strength-index-rsi) -- [RSI 2](momentum_indicators.md#rsi-2) -- [RSI Period](momentum_indicators.md#rsi-period) -- [Stochastic Oscillator](momentum_indicators.md#stochastic-oscillator) -- [Williams R](momentum_indicators.md#williams-r) +- Awesome Oscillator +- Chaikin Oscillator +- Ichimoku Cloud +- Percentage Price Oscillator (PPO) +- Percentage Volume Oscillator (PVO) +- Relative Strength Index (RSI) +- RSI 2 +- RSI Period +- Stochastic Oscillator +- Williams R ### Volatility Indicators -- [Acceleration Bands](volatility_indicators.md#acceleration-bands) -- [Actual True Range (ATR)](volatility_indicators.md#actual-true-range-atr) -- [Bollinger Band Width](volatility_indicators.md#bollinger-band-width) -- [Bollinger Bands](volatility_indicators.md#bollinger-bands) -- [Chandelier Exit](volatility_indicators.md#chandelier-exit) -- [Donchian Channel (DC)](volatility_indicators.md#donchian-channel-dc) -- [Keltner Channel (KC)](volatility_indicators.md#keltner-channel-kc) -- [Moving Standard Deviation (Std)](volatility_indicators.md#moving-standard-deviation-std) -- [Projection Oscillator (PO)](volatility_indicators.md#projection-oscillator-po) -- [Ulcer Index (UI)](volatility_indicators.md#ulcer-index-ui) +- Acceleration Bands +- Actual True Range (ATR) +- Bollinger Band Width +- Bollinger Bands +- Chandelier Exit +- Donchian Channel (DC) +- Keltner Channel (KC) +- Moving Standard Deviation (Std) +- Projection Oscillator (PO) +- Ulcer Index (UI) ### Volume Indicators -- [Accumulation/Distribution (A/D)](volume_indicators.md#accumulationdistribution-ad) -- [Chaikin Money Flow (CMF)](volume_indicators.md#chaikin-money-flow-cmf) -- [Ease of Movement (EMV)](volume_indicators.md#ease-of-movement-emv) -- [Force Index (FI)](volume_indicators.md#force-index-fi) -- [Money Flow Index (MFI)](volume_indicators.md#money-flow-index-mfi) -- [Negative Volume Index (NVI)](volume_indicators.md#negative-volume-index-nvi) -- [On-Balance Volume (OBV)](volume_indicators.md#on-balance-volume-obv) -- [Volume Price Trend (VPT)](volume_indicators.md#volume-price-trend-vpt) -- [Volume Weighted Average Price (VWAP)](volume_indicators.md#volume-weighted-average-price-vwap) +- Accumulation/Distribution (A/D) +- Chaikin Money Flow (CMF) +- Ease of Movement (EMV) +- Force Index (FI) +- Money Flow Index (MFI) +- Negative Volume Index (NVI) +- On-Balance Volume (OBV) +- Volume Price Trend (VPT) +- Volume Weighted Average Price (VWAP) ## Strategies Provided Strategies relies on the following: -- [Asset](strategy.md#asset) -- [Action](strategy.md#action) -- [Strategy Function](strategy.md#strategy-function) -- [Buy and Hold Strategy](strategy.md#buy-and-hold-strategy) +- Action +- [Snapshot](asset/README.md#type-snapshot) +- Strategy Function +- Buy and Hold Strategy The following list of strategies are currently supported by this package: ### Trend Strategies -- [Chande Forecast Oscillator Strategy](trend_strategies.md#chande-forecast-oscillator-strategy) -- [KDJ Strategy](trend_strategies.md#kdj-strategy) -- [MACD Strategy](trend_strategies.md#macd-strategy) -- [Trend Strategy](trend_strategies.md#trend-strategy) -- [Volume Weighted Moving Average (VWMA) Strategy](trend_strategies.md#volume-weighted-moving-average-vwma-strategy) +- Chande Forecast Oscillator Strategy +- KDJ Strategy +- MACD Strategy +- Trend Strategy +- Volume Weighted Moving Average (VWMA) Strategy ### Momentum Strategies -- [Awesome Oscillator Strategy](momentum_strategies.md#awesome-oscillator-strategy) -- [RSI Strategy](momentum_strategies.md#rsi-strategy) -- [RSI 2 Strategy](momentum_strategies.md#rsi-2-strategy) -- [Williams R Strategy](momentum_strategies.md#williams-r-strategy) +- Awesome Oscillator Strategy +- RSI Strategy +- RSI 2 Strategy +- Williams R Strategy ### Volatility Strategies -- [Bollinger Bands Strategy](volatility_strategies.md#bollinger-bands-strategy) -- [Projection Oscillator Strategy](volatility_strategies.md#projection-oscillator-strategy) +- Bollinger Bands Strategy +- Projection Oscillator Strategy ### Volume Strategies -- [Chaikin Money Flow Strategy](volume_strategies.md#chaikin-money-flow-strategy) -- [Ease of Movement Strategy](volume_strategies.md#ease-of-movement-strategy) -- [Force Index Strategy](volume_strategies.md#force-index-strategy) -- [Money Flow Index Strategy](volume_strategies.md#money-flow-index-strategy) -- [Negative Volume Index Strategy](volume_strategies.md#negative-volume-index-strategy) -- [Volume Weighted Average Price Strategy](volume_strategies.md#volume-weighted-average-price-strategy) +- Chaikin Money Flow Strategy +- Ease of Movement Strategy +- Force Index Strategy +- Money Flow Index Strategy +- Negative Volume Index Strategy +- Volume Weighted Average Price Strategy ### Compound Strategies -- [All Strategies](compound_strategies.md#all-strategies) -- [Run Strategies](compound_strategies.md#run-strategies) -- [Separate Strategies](compound_strategies.md#separate-strategies) -- [MACD and RSI Strategy](compound_strategies.md#macd-and-rsi-strategy) +- All Strategies +- Run Strategies +- Separate Strategies +- MACD and RSI Strategy ## Backtest Backtesting is the method for seeing how well a strategy would have done. The following backtesting functions are provided for evaluating strategies. -- [Apply Actions](backtest.md#apply-actions) -- [Count Transactions](backtest.md#count-transactions) -- [Normalize Actions](backtest.md#normalize-actions) -- [Normalize Gains](backtest.md#normalize-gains) +- Apply Actions +- Count Transactions +- Normalize Actions +- Normalize Gains ## Usage @@ -151,12 +161,15 @@ import ( ) ``` +## Contributing to the Project + +Anyone can contribute to Checkers library. Please make sure to read our [Contributor Covenant Code of Conduct](./CODE_OF_CONDUCT.md) guide first. Follow the [How to Contribute to Checker](./CONTRIBUTING.md) to contribute. + ## Disclaimer The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. ## License -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. + Copyright (c) 2021-2023 Onur Cinar. All Rights Reserved. + The source code is provided under MIT License. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..c8796b6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 2.x.x | :white_check_mark: | +| 1.x.x | :white_check_mark: | +| < 1.0 | :x: | + +## Reporting a Vulnerability + +If you found a vulnerability with this library, please file a new issue. \ No newline at end of file diff --git a/v2/asset/README.md b/asset/README.md similarity index 91% rename from v2/asset/README.md rename to asset/README.md index bf73825..c68c129 100644 --- a/v2/asset/README.md +++ b/asset/README.md @@ -3,7 +3,7 @@ # asset ```go -import "github.com/cinar/indicator/v2/asset" +import "github.com/cinar/indicator/asset" ``` Package asset contains the asset related functions. @@ -28,7 +28,7 @@ The information provided on this project is strictly for informational purposes -## type [Snapshot]() +## type [Snapshot]() Snapshot captures a single observation of an asset's price at a specific moment. diff --git a/v2/asset/asset.go b/asset/asset.go similarity index 100% rename from v2/asset/asset.go rename to asset/asset.go diff --git a/v2/asset/snapshot.go b/asset/snapshot.go similarity index 100% rename from v2/asset/snapshot.go rename to asset/snapshot.go diff --git a/backtest.go b/backtest.go deleted file mode 100644 index 7bfe80c..0000000 --- a/backtest.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -// The NormalizeActions takes a list of independenc actions, -// such as SELL, SELL, BUY, SELL, HOLD, SELL, and produces -// a normalized list where the actions are following the -// proper BUY, HOLD, SELL, HOLD order. -func NormalizeActions(actions []Action) []Action { - normalized := make([]Action, len(actions)) - - last := SELL - - for i, action := range actions { - if (action != HOLD) && (action != last) { - normalized[i] = action - last = action - } else { - normalized[i] = HOLD - } - } - - return normalized -} - -// The CountTransactions takes a list of normalized actions, -// and counts the BUY and SELL actions. -func CountTransactions(actions []Action) int { - count := 0 - - for _, action := range actions { - if (action == BUY) || (action == SELL) { - count++ - } - } - - return count -} - -// The ApplyActions takes the given list of prices, applies the -// given list of normalized actions, and returns the gains. -func ApplyActions(prices []float64, actions []Action) []float64 { - gains := make([]float64, len(actions)) - - initialBalance := 1.0 - balance := initialBalance - shares := 0.0 - - for i := 0; i < len(actions); i++ { - if actions[i] == BUY { - if balance > 0 { - shares = balance / prices[i] - balance = 0 - } - } else if actions[i] == SELL { - if shares > 0 { - balance = shares * prices[i] - shares = 0 - } - } - - gains[i] = ((shares * prices[i]) + balance - initialBalance) / initialBalance - } - - return gains -} - -// The NormalizeGains takes the given list of prices, calculates the -// price gains, subtracts it from the given list of gains. -func NormalizeGains(prices, gains []float64) []float64 { - priceGains := Sum(len(prices), percentDiff(prices, 1)) - normalized := subtract(gains, priceGains) - - return normalized -} diff --git a/backtest.md b/backtest.md deleted file mode 100644 index 5fff59e..0000000 --- a/backtest.md +++ /dev/null @@ -1,50 +0,0 @@ -### Backtest - -Backtesting is the method for seeing how well a strategy would have done. The following backtesting functions are provided for evaluating strategies. - -- [Apply Actions](#apply-actions) -- [Count Transactions](#count-transactions) -- [Normalize Actions](#normalize-actions) -- [Normalize Gains](#normalize-gains) - -#### Apply Actions - -The [ApplyActions](https://pkg.go.dev/github.com/cinar/indicator#ApplyActions) takes the given list of prices, applies the given list of normalized actions, and returns the gains. - -```golang -gains := indicator.ApplyActions(prices, actions) -``` - -#### Count Transactions - -The [CountTransactions](https://pkg.go.dev/github.com/cinar/indicator#CountTransactions) takes a list of normalized actions, and counts the _BUY_ and _SELL_ actions. - -```golang -count := indicator.CountTransactions(actions) -``` - -#### Normalize Actions - -The [NormalizeActions](https://pkg.go.dev/github.com/cinar/indicator#NormalizeActions) takes a list of independenc actions, such as _SELL_, _SELL_, _BUY_, _SELL_, _HOLD_, _SELL_, and produces a normalized list where the actions are following the proper _BUY_, _HOLD_, _SELL_, _HOLD_ order. - -```golang -normalized := indicator.NormalizeActions(actions) -``` - -#### Normalize Gains - -The [NormalizeGains](https://pkg.go.dev/github.com/cinar/indicator#NormalizeGains) takes the given list of prices, calculates the price gains, subtracts it from the given list of gains. - -```golang -normalizedGains := indicator.NormalizeGains(prices, gains) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. diff --git a/backtest_test.go b/backtest_test.go deleted file mode 100644 index 115b21d..0000000 --- a/backtest_test.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator_test - -import ( - "math" - "testing" - - "github.com/cinar/indicator" -) - -func TestNormalizeActions(t *testing.T) { - actions := []indicator.Action{ - indicator.HOLD, - indicator.SELL, - indicator.SELL, - indicator.BUY, - indicator.BUY, - indicator.SELL, - indicator.BUY, - indicator.BUY, - indicator.HOLD, - indicator.SELL, - } - - expected := []indicator.Action{ - indicator.HOLD, - indicator.HOLD, - indicator.HOLD, - indicator.BUY, - indicator.HOLD, - indicator.SELL, - indicator.BUY, - indicator.HOLD, - indicator.HOLD, - indicator.SELL, - } - - normalized := indicator.NormalizeActions(actions) - - if len(normalized) != len(expected) { - t.Fatal("not the same size") - } - - for i, actual := range normalized { - if actual != expected[i] { - t.Fatalf("at %d actual %v expected %v", i, actual, expected[i]) - } - } -} - -func TestCountTransactions(t *testing.T) { - actions := []indicator.Action{ - indicator.HOLD, - indicator.HOLD, - indicator.HOLD, - indicator.BUY, - indicator.HOLD, - indicator.SELL, - indicator.BUY, - indicator.HOLD, - indicator.HOLD, - indicator.SELL, - } - - expected := 4 - - actual := indicator.CountTransactions(actions) - - if actual != expected { - t.Fatalf("actual %d expected %d", actual, expected) - } -} - -func TestApplyActions(t *testing.T) { - prices := []float64{ - 1.00, - 2.00, - 3.00, - 4.00, - 4.00, - 5.00, - 7.00, - 5.00, - 8.00, - 9.00, - } - - actions := []indicator.Action{ - indicator.HOLD, - indicator.HOLD, - indicator.HOLD, - indicator.BUY, - indicator.HOLD, - indicator.SELL, - indicator.BUY, - indicator.HOLD, - indicator.HOLD, - indicator.SELL, - } - - expected := []float64{ - 0.00, - 0.00, - 0.00, - 0.00, - 0.00, - 0.25, - 0.25, - -0.11, - 0.43, - 0.61, - } - - gains := indicator.ApplyActions(prices, actions) - - if len(gains) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(gains); i++ { - actual := math.Round(gains[i]*100) / 100 - - if actual != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual, expected[i]) - } - } -} - -func TestNormalizeGains(t *testing.T) { - prices := []float64{2, 4, 6, 12, 18} - gains := []float64{0, 1, 1.5, 2.5, 3} - expected := []float64{0, 0, 0, 0, 0} - - actual := indicator.NormalizeGains(prices, gains) - if len(actual) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(expected); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..97ec172 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,6 @@ +coverage: + status: + project: + default: + target: 100% + threshold: 0% \ No newline at end of file diff --git a/compound_strategies.go b/compound_strategies.go deleted file mode 100644 index ca98682..0000000 --- a/compound_strategies.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -// The AllStrategies function takes one or more StrategyFunction and -// provides a StrategyFunction that will return a BUY or SELL action -// if all strategies are returning the same action, otherwise it -// will return a HOLD action. -func AllStrategies(strategies ...StrategyFunction) StrategyFunction { - return func(asset *Asset) []Action { - actions := RunStrategies(asset, strategies...) - - for i := 1; i < len(actions); i++ { - for j := 0; j < len(actions[0]); j++ { - if actions[0][j] != actions[i][j] { - actions[0][j] = HOLD - } - } - } - - return actions[0] - } -} - -// The SeparateStrategies function takes a buy strategy and a sell strategy. -// -// It returns a BUY action if the buy strategy returns a BUY action and -// the the sell strategy returns a HOLD action. -// -// It returns a SELL action if the sell strategy returns a SELL action -// and the buy strategy returns a HOLD action. -// -// It returns HOLD otherwise. -func SeparateStategies(buyStrategy, sellStrategy StrategyFunction) StrategyFunction { - return func(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - buyActions := buyStrategy(asset) - sellActions := sellStrategy(asset) - - for i := 0; i < len(actions); i++ { - if buyActions[i] == BUY && sellActions[i] == HOLD { - actions[i] = BUY - } else if sellActions[i] == SELL && buyActions[i] == HOLD { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions - } -} - -// The RunStrategies takes one or more StrategyFunction and returns -// the acitions for each. -func RunStrategies(asset *Asset, strategies ...StrategyFunction) [][]Action { - actions := make([][]Action, len(strategies)) - - for i := 0; i < len(strategies); i++ { - actions[i] = strategies[i](asset) - } - - return actions -} - -// MACD and RSI strategy. -func MacdAndRsiStrategy(asset *Asset) []Action { - strategy := AllStrategies(MacdStrategy, DefaultRsiStrategy) - return strategy(asset) -} diff --git a/compound_strategies.md b/compound_strategies.md deleted file mode 100644 index e5c8ea4..0000000 --- a/compound_strategies.md +++ /dev/null @@ -1,58 +0,0 @@ -### Compound Strategies - -Compound strategies combine multiple strategies together to generate signals. - -- [All Strategies](#all-strategies) -- [Run Strategies](#run-strategies) -- [Separate Strategies](#separate-strategies) -- [MACD and RSI Strategy](#macd-and-rsi-strategy) - -#### All Strategies - -The [AllStrategies](https://pkg.go.dev/github.com/cinar/indicator#AllStrategies) function takes one or more [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction) and provides a [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction) that will return a _BUY_ or _SELL_ action if all strategies are returning the same action, otherwise it will return a _HOLD_ action. - -```golang -strategy := indicator.AllStrategies(indicator.MacdStrategy, indicator.DefaultRsiStrategy) -actions := strategy(asset) -``` - -#### Run Strategies - -The [RunStrategies](https://pkg.go.dev/github.com/cinar/indicator#RunStrategies) function takes one or more [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction) and returns the actions for each. - -```golang -actions := indicator.RunStrategies(asset, indicator.MacdStrategy, indicator.DefaultRsiStrategy) -``` - -#### Separate Strategies - -The [SeparateStrategies](https://pkg.go.dev/github.com/cinar/indicator#SeparateStrategies) function takes a buy strategy and a sell strategy. - -It returns a _BUY_ action if the buy strategy returns a _BUY_ action and the the sell strategy returns a _HOLD_ action. - -It returns a _SELL_ action if the sell strategy returns a _SELL_ action and the buy strategy returns a _HOLD_ action. - -It returns _HOLD_ otherwise. - -```golang -strategy := indicator.SeparateStrategies(indicator.MacdStrategy, indicator.DefaultRsiStrategy) -actions := strategy(asset) -``` - -#### MACD and RSI Strategy - -The [MacdAndRsiStrategy](https://pkg.go.dev/github.com/cinar/indicator#MacdAndRsiStrategy) function is a compound strategy that combines the [MacdStrategy](https://pkg.go.dev/github.com/cinar/indicator#MacdStrategy) and the [DefaultRsiStrategy](https://pkg.go.dev/github.com/cinar/indicator#DefaultRsiStrategy). It will return a _BUY_ or _SELL_ action if both strategies are turning the same action, otherwise, it will return a _HOLD_ action. - -```golang -actions := indicator.MacdAndRsiStrategy(asset) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. diff --git a/container/bst/bst.go b/container/bst/bst.go deleted file mode 100644 index d6cd7cf..0000000 --- a/container/bst/bst.go +++ /dev/null @@ -1,160 +0,0 @@ -package bst - -import ( - "github.com/cinar/indicator/container" -) - -// 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 container.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 container.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 -} - -// 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/container/bst/bst_test.go b/container/bst/bst_test.go deleted file mode 100644 index 6b5855f..0000000 --- a/container/bst/bst_test.go +++ /dev/null @@ -1,57 +0,0 @@ -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/container/comparable.go b/container/comparable.go deleted file mode 100644 index a38decb..0000000 --- a/container/comparable.go +++ /dev/null @@ -1,52 +0,0 @@ -package container - -// 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 -} diff --git a/container/queue/queue.go b/container/queue/queue.go deleted file mode 100644 index 99f9201..0000000 --- a/container/queue/queue.go +++ /dev/null @@ -1,40 +0,0 @@ -package queue - -import ( - "container/list" -) - -// Queue type. -type Queue struct { - list *list.List -} - -// New queue. -func New() *Queue { - return &Queue{ - list: list.New(), - } -} - -// Enqueue the given value. -func (q *Queue) Enqueue(v interface{}) { - q.list.PushBack(v) -} - -// Dequeue from the queue. -func (q *Queue) Dequeue() interface{} { - front := q.list.Front() - if front == nil { - panic("queue empty") - } - - value := front.Value - q.list.Remove(front) - - return value -} - -// Queue empty. -func (q *Queue) Empty() bool { - return (q.list.Len() == 0) -} diff --git a/container/queue/queue_test.go b/container/queue/queue_test.go deleted file mode 100644 index 24e81e7..0000000 --- a/container/queue/queue_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package queue - -import ( - "testing" -) - -func TestQueue(t *testing.T) { - values := []float64{1, 2, 3, 4} - - queue := New() - - for _, value := range values { - queue.Enqueue(value) - } - - for i, value := range values { - if queue.Empty() { - t.Fatal("queue empty") - } - - actual := queue.Dequeue() - - if actual != value { - t.Fatalf("at %d actual %f expected %f", i, actual, value) - } - } -} diff --git a/go.mod b/go.mod index 22cb308..6b57e8c 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/cinar/indicator -go 1.14 +go 1.20 diff --git a/go.work b/go.work deleted file mode 100644 index 437edda..0000000 --- a/go.work +++ /dev/null @@ -1,6 +0,0 @@ -go 1.20 - -use ( - . - ./v2 -) diff --git a/helper.go b/helper.go deleted file mode 100644 index e74250f..0000000 --- a/helper.go +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import ( - "math" - "testing" -) - -// Check values same size. -func checkSameSize(values ...[]float64) { - if len(values) < 2 { - return - } - - n := len(values[0]) - - for i := 1; i < len(values); i++ { - if len(values[i]) != n { - panic("not all same size") - } - } -} - -// Multiply values by multipler. -func multiplyBy(values []float64, multiplier float64) []float64 { - result := make([]float64, len(values)) - - for i, value := range values { - result[i] = value * multiplier - } - - return result -} - -// Multiply values1 and values2. -func multiply(values1, values2 []float64) []float64 { - checkSameSize(values1, values2) - - result := make([]float64, len(values1)) - - for i := 0; i < len(result); i++ { - result[i] = values1[i] * values2[i] - } - - return result -} - -// Divide values by divider. -func divideBy(values []float64, divider float64) []float64 { - return multiplyBy(values, float64(1)/divider) -} - -// Divide values1 by values2. -func divide(values1, values2 []float64) []float64 { - checkSameSize(values1, values2) - - result := make([]float64, len(values1)) - - for i := 0; i < len(result); i++ { - result[i] = values1[i] / values2[i] - } - - return result -} - -// Add values1 and values2. -func add(values1, values2 []float64) []float64 { - checkSameSize(values1, values2) - - result := make([]float64, len(values1)) - for i := 0; i < len(result); i++ { - result[i] = values1[i] + values2[i] - } - - return result -} - -// Add addition to values. -func addBy(values []float64, addition float64) []float64 { - result := make([]float64, len(values)) - - for i := 0; i < len(result); i++ { - result[i] = values[i] + addition - } - - return result -} - -// subtract values2 from values1. -func subtract(values1, values2 []float64) []float64 { - subtract := multiplyBy(values2, float64(-1)) - return add(values1, subtract) -} - -// Difference between current and before values. -func diff(values []float64, before int) []float64 { - return subtract(values, shiftRight(before, values)) -} - -// Percent difference between current and before values. -func percentDiff(values []float64, before int) []float64 { - result := make([]float64, len(values)) - - for i := before; i < len(values); i++ { - result[i] = (values[i] - values[i-before]) / values[i-before] - } - - return result -} - -// Shift right for period and fills with value. -func shiftRightAndFillBy(period int, fill float64, values []float64) []float64 { - result := make([]float64, len(values)) - - for i := 0; i < len(result); i++ { - if i < period { - result[i] = fill - } else { - result[i] = values[i-period] - } - } - - return result -} - -// Shift right for period. -func shiftRight(period int, values []float64) []float64 { - return shiftRightAndFillBy(period, 0, values) -} - -// Round value to digits. -func roundDigits(value float64, digits int) float64 { - n := math.Pow(10, float64(digits)) - - return math.Round(value*n) / n -} - -// Round values to digits. -func roundDigitsAll(values []float64, digits int) []float64 { - result := make([]float64, len(values)) - - for i := 0; i < len(result); i++ { - result[i] = roundDigits(values[i], digits) - } - - return result -} - -// Generate numbers. -func generateNumbers(begin, end, step float64) []float64 { - n := int(math.Round((end - begin) / step)) - - numbers := make([]float64, n) - - for i := 0; i < n; i++ { - numbers[i] = begin + (step * float64(i)) - } - - return numbers -} - -// Convets the []int64 to []float64. -func asFloat64(values []int64) []float64 { - result := make([]float64, len(values)) - - for i := 0; i < len(values); i++ { - result[i] = float64(values[i]) - } - - return result -} - -// Calculate power of base with exponent. -func pow(base []float64, exponent float64) []float64 { - result := make([]float64, len(base)) - - for i := 0; i < len(result); i++ { - result[i] = math.Pow(base[i], exponent) - } - - return result -} - -// Extact sign. -func extractSign(values []float64) []float64 { - result := make([]float64, len(values)) - - for i := 0; i < len(result); i++ { - if values[i] >= 0 { - result[i] = 1 - } else { - result[i] = -1 - } - } - - return result -} - -// Keep positives. -func keepPositives(values []float64) []float64 { - result := make([]float64, len(values)) - - for i := 0; i < len(values); i++ { - if values[i] > 0 { - result[i] = values[i] - } else { - result[i] = 0 - } - } - - return result -} - -// Keep negatives. -func keepNegatives(values []float64) []float64 { - result := make([]float64, len(values)) - - for i := 0; i < len(values); i++ { - if values[i] < 0 { - result[i] = values[i] - } else { - result[i] = 0 - } - } - - return result -} - -// Test equals. -func testEquals(t *testing.T, actual, expected []float64) { - if len(actual) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(expected); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -// Test equals int array. -func testEqualsInt(t *testing.T, actual, expected []int) { - if len(actual) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(expected); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %d expected %d", i, actual[i], expected[i]) - } - } -} - -// Test equals action array. -func testEqualsAction(t *testing.T, actual, expected []Action) { - if len(actual) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(expected); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %d expected %d", i, actual[i], expected[i]) - } - } -} - -// Sqrt of given values. -func sqrt(values []float64) []float64 { - result := make([]float64, len(values)) - - for i := 0; i < len(values); i++ { - result[i] = math.Sqrt(values[i]) - } - - return result -} - -// Abs of given values. -func abs(values []float64) []float64 { - result := make([]float64, len(values)) - - for i := 0; i < len(values); i++ { - result[i] = math.Abs(values[i]) - } - - return result -} - -// fillNaNWith fills the NaN values with the given fill value. -func fillNaNWith(values []float64, fill float64) []float64 { - result := make([]float64, len(values)) - - for i, value := range values { - if math.IsNaN(value) { - value = fill - } - - result[i] = value - } - - return result -} diff --git a/v2/helper/README.md b/helper/README.md similarity index 85% rename from v2/helper/README.md rename to helper/README.md index 0ae9ed0..c0b4b90 100644 --- a/v2/helper/README.md +++ b/helper/README.md @@ -3,7 +3,7 @@ # helper ```go -import "github.com/cinar/indicator/v2/helper" +import "github.com/cinar/indicator/helper" ``` Package helper contains the helper functions. @@ -99,7 +99,7 @@ const ( ``` -## func [Abs]() +## func [Abs]() ```go func Abs[T Number](c <-chan T) <-chan T @@ -115,7 +115,7 @@ fmt.Println(helper.ChanToSlice(abs)) // [10, 20, 4, 5] ``` -## func [Add]() +## func [Add]() ```go func Add[T Number](ac, bc <-chan T) <-chan T @@ -135,7 +135,7 @@ fmt.Println(actual) // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] ``` -## func [Apply]() +## func [Apply]() ```go func Apply[T Number](c <-chan T, f func(T) T) <-chan T @@ -152,7 +152,7 @@ timesTwo := helper.Apply(c, func(n int) int { ``` -## func [ChanToSlice]() +## func [ChanToSlice]() ```go func ChanToSlice[T any](c <-chan T) []T @@ -174,7 +174,7 @@ fmt.Println(helper.ChanToSlice(c)) // [1, 2, 3, 4] ``` -## func [Change]() +## func [Change]() ```go func Change[T Number](c <-chan T, before int) <-chan T @@ -191,7 +191,7 @@ fmt.Println(helper.ChanToSlice(output)) // [4, 3, 3, -3, -7, -1, 2, 3] ``` -## func [ChangePercent]() +## func [ChangePercent]() ```go func ChangePercent[T Number](c <-chan T, before int) <-chan T @@ -208,7 +208,7 @@ fmt.Println(helper.ChanToSlice(actual)) // [400, 150, 60, -60, -87.5, -50, 200, ``` -## func [CheckEquals]() +## func [CheckEquals]() ```go func CheckEquals[T Number](inputs ...<-chan T) error @@ -217,7 +217,7 @@ func CheckEquals[T Number](inputs ...<-chan T) error CheckEquals determines whether the two channels are equal. -## func [DecrementBy]() +## func [DecrementBy]() ```go func DecrementBy[T Number](c <-chan T, d T) <-chan T @@ -234,7 +234,7 @@ fmt.Println(helper.ChanToSlice(substractOne)) // [0, 1, 2, 3] ``` -## func [Divide]() +## func [Divide]() ```go func Divide[T Number](ac, bc <-chan T) <-chan T @@ -254,7 +254,7 @@ fmt.Println(helper.ChanToSlice(division)) // [1, 4, 2, 4, 2] ``` -## func [DivideBy]() +## func [DivideBy]() ```go func DivideBy[T Number](c <-chan T, d T) <-chan T @@ -270,7 +270,7 @@ fmt.Println(helper.ChanToSlice(half)) // [1, 2, 3, 4] ``` -## func [Drain]() +## func [Drain]() ```go func Drain[T any](c <-chan T) @@ -279,7 +279,7 @@ func Drain[T any](c <-chan T) Drain drains the given channel. -## func [Duplicate]() +## func [Duplicate]() ```go func Duplicate[T any](input <-chan T, count int) []<-chan T @@ -298,7 +298,7 @@ fmt.Println(helper.ChanToSlice(outputs[1])) // [-10, 20, -4, -5] ``` -## func [Field]() +## func [Field]() ```go func Field[T, S any](c <-chan *S, name string) (<-chan T, error) @@ -307,7 +307,7 @@ func Field[T, S any](c <-chan *S, name string) (<-chan T, error) Field extracts a specific field from a channel of struct pointers and delivers it through a new channel. -## func [Filter]() +## func [Filter]() ```go func Filter[T Number](c <-chan T, p func(T) bool) <-chan T @@ -324,7 +324,7 @@ even := helper.Filter(c, func(n int) bool { ``` -## func [Head]() +## func [Head]() ```go func Head[T Number](c <-chan T, count int) <-chan T @@ -341,7 +341,7 @@ fmt.Println(helper.ChanToSlice(actual)) // [2, 4] ``` -## func [IncrementBy]() +## func [IncrementBy]() ```go func IncrementBy[T Number](c <-chan T, i T) <-chan T @@ -358,7 +358,7 @@ fmt.Println(helper.ChanToSlice(actual)) // [2, 3, 4, 5] ``` -## func [KeepNegatives]() +## func [KeepNegatives]() ```go func KeepNegatives[T Number](c <-chan T) <-chan T @@ -375,7 +375,7 @@ fmt.Println(helper.ChanToSlice(negatives)) // [-10, 0, 0, -5] ``` -## func [KeepPositives]() +## func [KeepPositives]() ```go func KeepPositives[T Number](c <-chan T) <-chan T @@ -392,7 +392,7 @@ fmt.Println(helper.ChanToSlice(positives)) // [0, 20, 4, 0] ``` -## func [Map]() +## func [Map]() ```go func Map[F, T any](c <-chan F, f func(F) T) <-chan T @@ -409,7 +409,7 @@ timesTwo := helper.Map(c, func(n int) int { ``` -## func [Multiply]() +## func [Multiply]() ```go func Multiply[T Number](ac, bc <-chan T) <-chan T @@ -429,7 +429,7 @@ fmt.Println(helper.ChanToSlice(multiplication)) // [2, 4, 6, 8, 10] ``` -## func [MultiplyBy]() +## func [MultiplyBy]() ```go func MultiplyBy[T Number](c <-chan T, m T) <-chan T @@ -446,7 +446,7 @@ fmt.Println(helper.ChanToSlice(twoTimes)) // [2, 4, 6, 8] ``` -## func [Operate]() +## func [Operate]() ```go func Operate[T Number](ac, bc <-chan T, o func(T, T) T) <-chan T @@ -463,7 +463,7 @@ add := helper.Operate(ac, bc, func(a, b int) int { ``` -## func [Pipe]() +## func [Pipe]() ```go func Pipe[T Number](f <-chan T, t chan<- T) @@ -481,7 +481,7 @@ fmt.println(helper.ChanToSlice(output)) // [2, 4, 6, 8] ``` -## func [Pow]() +## func [Pow]() ```go func Pow[T Number](c <-chan T, y T) <-chan T @@ -498,7 +498,7 @@ fmt.Println(helper.ChanToSlice(squared)) // [4, 9, 25, 100] ``` -## func [ReadFromCsvFile]() +## func [ReadFromCsvFile]() ```go func ReadFromCsvFile[T any](fileName string, hasHeader bool) (<-chan *T, error) @@ -507,7 +507,7 @@ func ReadFromCsvFile[T any](fileName string, hasHeader bool) (<-chan *T, error) ReadFromCsvFile creates a CSV instance, parses CSV data from the provided filename, maps the data to corresponding struct fields, and delivers it through the channel. -## func [RoundDigit]() +## func [RoundDigit]() ```go func RoundDigit[T Number](n T, d int) T @@ -523,7 +523,7 @@ fmt.Println(n) // 10.12 ``` -## func [RoundDigits]() +## func [RoundDigits]() ```go func RoundDigits[T Number](c <-chan T, d int) <-chan T @@ -540,7 +540,7 @@ fmt.Println(helper.ChanToSlice(rounded)) // [10.12, 5.68, 6.78, 8.91] ``` -## func [Seq]() +## func [Seq]() ```go func Seq[T Number](from, to, increment T) <-chan T @@ -561,7 +561,7 @@ fmt.Println(<- s) // 4 ``` -## func [Shift]() +## func [Shift]() ```go func Shift[T Number](c <-chan T, count int, fill T) <-chan T @@ -578,7 +578,7 @@ fmt.Println(helper.ChanToSlice(output)) // [0, 0, 0, 0, 2, 4, 6, 8] ``` -## func [Sign]() +## func [Sign]() ```go func Sign[T Number](c <-chan T) <-chan T @@ -595,7 +595,7 @@ fmt.Println(helper.ChanToSlice(sign)) // [-1, 1, -1, 0] ``` -## func [Since]() +## func [Since]() ```go func Since[T Number](c <-chan T) <-chan T @@ -604,7 +604,7 @@ func Since[T Number](c <-chan T) <-chan T Since counts the number of periods since the last change of value in a channel of numbers. -## func [Skip]() +## func [Skip]() ```go func Skip[T Number](c <-chan T, count int) <-chan T @@ -621,7 +621,7 @@ fmt.Println(helper.ChanToSlice(actual)) // [6, 8] ``` -## func [SliceToChan]() +## func [SliceToChan]() ```go func SliceToChan[T any](slice []T) <-chan T @@ -641,7 +641,7 @@ fmt.Println(<- c) // 8 ``` -## func [Sqrt]() +## func [Sqrt]() ```go func Sqrt[T Number](c <-chan T) <-chan T @@ -658,7 +658,7 @@ fmt.Println(helper.ChanToSlice(sqrt)) // [3, 9, 4, 10] ``` -## func [Subtract]() +## func [Subtract]() ```go func Subtract[T Number](ac, bc <-chan T) <-chan T @@ -676,7 +676,7 @@ fmt.Println(helper.ChanToSlice(actual)) // [1, 2, 3, 4, 5] ``` -## func [Waitable]() +## func [Waitable]() ```go func Waitable[T any](wg *sync.WaitGroup, c <-chan T) <-chan T @@ -685,7 +685,7 @@ func Waitable[T any](wg *sync.WaitGroup, c <-chan T) <-chan T Waitable increments the wait group before reading from the channel and signals completion when the channel is closed. -## type [Bst]() +## type [Bst]() Bst represents the binary search tree. @@ -696,7 +696,7 @@ type Bst[T Number] struct { ``` -### func [NewBst]() +### func [NewBst]() ```go func NewBst[T Number]() *Bst[T] @@ -705,7 +705,7 @@ func NewBst[T Number]() *Bst[T] NewBst creates a new binary search tree. -### func \(\*Bst\[T\]\) [Contains]() +### func \(\*Bst\[T\]\) [Contains]() ```go func (b *Bst[T]) Contains(value T) bool @@ -714,7 +714,7 @@ func (b *Bst[T]) Contains(value T) bool Contains checks whether the given value exists in the binary search tree. -### func \(\*Bst\[T\]\) [Insert]() +### func \(\*Bst\[T\]\) [Insert]() ```go func (b *Bst[T]) Insert(value T) @@ -723,7 +723,7 @@ func (b *Bst[T]) Insert(value T) Insert adds a new value to the binary search tree. -### func \(\*Bst\[T\]\) [Max]() +### func \(\*Bst\[T\]\) [Max]() ```go func (b *Bst[T]) Max() T @@ -732,7 +732,7 @@ func (b *Bst[T]) Max() T Max function returns the maximum value in the binary search tree. -### func \(\*Bst\[T\]\) [Min]() +### func \(\*Bst\[T\]\) [Min]() ```go func (b *Bst[T]) Min() T @@ -741,7 +741,7 @@ func (b *Bst[T]) Min() T Min function returns the minimum value in the binary search tree. -### func \(\*Bst\[T\]\) [Remove]() +### func \(\*Bst\[T\]\) [Remove]() ```go func (b *Bst[T]) Remove(value T) bool @@ -750,7 +750,7 @@ func (b *Bst[T]) Remove(value T) bool Remove removes the specified value from the binary search tree and rebalances the tree. -## type [BstNode]() +## type [BstNode]() BstNode represents the binary search tree node. @@ -761,7 +761,7 @@ type BstNode[T Number] struct { ``` -## type [Csv]() +## type [Csv]() Csv represents the configuration for CSV reader and writer. @@ -772,7 +772,7 @@ type Csv[T any] struct { ``` -### func [NewCsv]() +### func [NewCsv]() ```go func NewCsv[T any](hasHeader bool) (*Csv[T], error) @@ -781,7 +781,7 @@ func NewCsv[T any](hasHeader bool) (*Csv[T], error) NewCsv function initializes a new CSV instance. The parameter hasHeader indicates whether the CSV contains a header row. -### func \(\*Csv\[T\]\) [ReadFromFile]() +### func \(\*Csv\[T\]\) [ReadFromFile]() ```go func (c *Csv[T]) ReadFromFile(fileName string) (<-chan *T, error) @@ -790,7 +790,7 @@ func (c *Csv[T]) ReadFromFile(fileName string) (<-chan *T, error) ReadFromFile parses the CSV data from the provided file name, maps the data to corresponding struct fields, and delivers the resulting snapshots through the channel. -### func \(\*Csv\[T\]\) [ReadFromReader]() +### func \(\*Csv\[T\]\) [ReadFromReader]() ```go func (c *Csv[T]) ReadFromReader(reader io.Reader) <-chan *T @@ -799,7 +799,7 @@ func (c *Csv[T]) ReadFromReader(reader io.Reader) <-chan *T ReadFromReader parses the CSV data from the provided reader, maps the data to corresponding struct fields, and delivers the resulting it through the channel. -## type [Float]() +## type [Float]() Float refers to any float type. @@ -810,7 +810,7 @@ type Float interface { ``` -## type [Integer]() +## type [Integer]() Integer refers to any integer type. @@ -821,7 +821,7 @@ type Integer interface { ``` -## type [Number]() +## type [Number]() Number refers to any numeric type. @@ -832,7 +832,7 @@ type Number interface { ``` -## type [Ring]() +## type [Ring]() Ring represents a ring structure that can be instantiated using the NewRing function. @@ -854,7 +854,7 @@ type Ring[T any] struct { ``` -### func [NewRing]() +### func [NewRing]() ```go func NewRing[T any](size int) *Ring[T] @@ -863,7 +863,7 @@ func NewRing[T any](size int) *Ring[T] NewRing creates a new ring instance with the given size. -### func \(\*Ring\[T\]\) [Insert]() +### func \(\*Ring\[T\]\) [Insert]() ```go func (r *Ring[T]) Insert(t T) T diff --git a/v2/helper/abs.go b/helper/abs.go similarity index 100% rename from v2/helper/abs.go rename to helper/abs.go diff --git a/v2/helper/abs_test.go b/helper/abs_test.go similarity index 92% rename from v2/helper/abs_test.go rename to helper/abs_test.go index da2f038..756961c 100644 --- a/v2/helper/abs_test.go +++ b/helper/abs_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestAbs(t *testing.T) { diff --git a/v2/helper/add.go b/helper/add.go similarity index 100% rename from v2/helper/add.go rename to helper/add.go diff --git a/v2/helper/add_test.go b/helper/add_test.go similarity index 94% rename from v2/helper/add_test.go rename to helper/add_test.go index 733b1a6..ac88e0b 100644 --- a/v2/helper/add_test.go +++ b/helper/add_test.go @@ -9,7 +9,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestAdd(t *testing.T) { diff --git a/v2/helper/apply.go b/helper/apply.go similarity index 100% rename from v2/helper/apply.go rename to helper/apply.go diff --git a/v2/helper/apply_test.go b/helper/apply_test.go similarity index 93% rename from v2/helper/apply_test.go rename to helper/apply_test.go index 1cc5ba7..9bb1d16 100644 --- a/v2/helper/apply_test.go +++ b/helper/apply_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestApply(t *testing.T) { diff --git a/v2/helper/bst.go b/helper/bst.go similarity index 100% rename from v2/helper/bst.go rename to helper/bst.go diff --git a/v2/helper/bst_test.go b/helper/bst_test.go similarity index 97% rename from v2/helper/bst_test.go rename to helper/bst_test.go index 00e3e88..2dfe37d 100644 --- a/v2/helper/bst_test.go +++ b/helper/bst_test.go @@ -7,7 +7,7 @@ package helper_test import ( "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestInsertAndContains(t *testing.T) { diff --git a/v2/helper/chan_to_slice.go b/helper/chan_to_slice.go similarity index 100% rename from v2/helper/chan_to_slice.go rename to helper/chan_to_slice.go diff --git a/v2/helper/chan_to_slice_test.go b/helper/chan_to_slice_test.go similarity index 92% rename from v2/helper/chan_to_slice_test.go rename to helper/chan_to_slice_test.go index 4ef6eb0..4dc8427 100644 --- a/v2/helper/chan_to_slice_test.go +++ b/helper/chan_to_slice_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestChanToSlice(t *testing.T) { diff --git a/v2/helper/change.go b/helper/change.go similarity index 100% rename from v2/helper/change.go rename to helper/change.go diff --git a/v2/helper/change_percent.go b/helper/change_percent.go similarity index 100% rename from v2/helper/change_percent.go rename to helper/change_percent.go diff --git a/v2/helper/change_percent_test.go b/helper/change_percent_test.go similarity index 93% rename from v2/helper/change_percent_test.go rename to helper/change_percent_test.go index 0a8b447..abb1f6c 100644 --- a/v2/helper/change_percent_test.go +++ b/helper/change_percent_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestChangePercent(t *testing.T) { diff --git a/v2/helper/change_test.go b/helper/change_test.go similarity index 92% rename from v2/helper/change_test.go rename to helper/change_test.go index f34f2e5..908cf8b 100644 --- a/v2/helper/change_test.go +++ b/helper/change_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestChange(t *testing.T) { diff --git a/v2/helper/check.go b/helper/check.go similarity index 100% rename from v2/helper/check.go rename to helper/check.go diff --git a/v2/helper/check_test.go b/helper/check_test.go similarity index 96% rename from v2/helper/check_test.go rename to helper/check_test.go index 341b5e1..f673bf0 100644 --- a/v2/helper/check_test.go +++ b/helper/check_test.go @@ -7,7 +7,7 @@ package helper_test import ( "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestCheckEqualsNotPairs(t *testing.T) { diff --git a/v2/helper/csv.go b/helper/csv.go similarity index 100% rename from v2/helper/csv.go rename to helper/csv.go diff --git a/v2/helper/csv_test.go b/helper/csv_test.go similarity index 98% rename from v2/helper/csv_test.go rename to helper/csv_test.go index 8f887d7..29a5142 100644 --- a/v2/helper/csv_test.go +++ b/helper/csv_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestCsv(t *testing.T) { diff --git a/v2/helper/decrement_by.go b/helper/decrement_by.go similarity index 100% rename from v2/helper/decrement_by.go rename to helper/decrement_by.go diff --git a/v2/helper/decrement_by_test.go b/helper/decrement_by_test.go similarity index 92% rename from v2/helper/decrement_by_test.go rename to helper/decrement_by_test.go index 673d4bf..0d2a950 100644 --- a/v2/helper/decrement_by_test.go +++ b/helper/decrement_by_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestDecrementBy(t *testing.T) { diff --git a/v2/helper/divide.go b/helper/divide.go similarity index 100% rename from v2/helper/divide.go rename to helper/divide.go diff --git a/v2/helper/divide_by.go b/helper/divide_by.go similarity index 100% rename from v2/helper/divide_by.go rename to helper/divide_by.go diff --git a/v2/helper/divide_by_test.go b/helper/divide_by_test.go similarity index 92% rename from v2/helper/divide_by_test.go rename to helper/divide_by_test.go index 22686a4..2b5f90e 100644 --- a/v2/helper/divide_by_test.go +++ b/helper/divide_by_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestDivideBy(t *testing.T) { diff --git a/v2/helper/divide_test.go b/helper/divide_test.go similarity index 92% rename from v2/helper/divide_test.go rename to helper/divide_test.go index f937596..c686199 100644 --- a/v2/helper/divide_test.go +++ b/helper/divide_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestDivide(t *testing.T) { diff --git a/v2/helper/drain.go b/helper/drain.go similarity index 100% rename from v2/helper/drain.go rename to helper/drain.go diff --git a/v2/helper/drain_test.go b/helper/drain_test.go similarity index 87% rename from v2/helper/drain_test.go rename to helper/drain_test.go index 6a871b4..4af7928 100644 --- a/v2/helper/drain_test.go +++ b/helper/drain_test.go @@ -7,7 +7,7 @@ package helper_test import ( "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestDrain(t *testing.T) { diff --git a/v2/helper/duplicate.go b/helper/duplicate.go similarity index 100% rename from v2/helper/duplicate.go rename to helper/duplicate.go diff --git a/v2/helper/duplicate_test.go b/helper/duplicate_test.go similarity index 93% rename from v2/helper/duplicate_test.go rename to helper/duplicate_test.go index 22e2ff5..cc416a4 100644 --- a/v2/helper/duplicate_test.go +++ b/helper/duplicate_test.go @@ -7,7 +7,7 @@ package helper_test import ( "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestDuplicate(t *testing.T) { diff --git a/v2/helper/field.go b/helper/field.go similarity index 100% rename from v2/helper/field.go rename to helper/field.go diff --git a/v2/helper/field_test.go b/helper/field_test.go similarity index 96% rename from v2/helper/field_test.go rename to helper/field_test.go index 108e8f0..ce644bf 100644 --- a/v2/helper/field_test.go +++ b/helper/field_test.go @@ -7,7 +7,7 @@ package helper_test import ( "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestField(t *testing.T) { diff --git a/v2/helper/filter.go b/helper/filter.go similarity index 100% rename from v2/helper/filter.go rename to helper/filter.go diff --git a/v2/helper/filter_test.go b/helper/filter_test.go similarity index 92% rename from v2/helper/filter_test.go rename to helper/filter_test.go index 2ad6c17..c12f166 100644 --- a/v2/helper/filter_test.go +++ b/helper/filter_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestFilter(t *testing.T) { diff --git a/v2/helper/head.go b/helper/head.go similarity index 100% rename from v2/helper/head.go rename to helper/head.go diff --git a/v2/helper/head_test.go b/helper/head_test.go similarity index 94% rename from v2/helper/head_test.go rename to helper/head_test.go index 43a162c..890df9d 100644 --- a/v2/helper/head_test.go +++ b/helper/head_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestHead(t *testing.T) { diff --git a/v2/helper/helper.go b/helper/helper.go similarity index 100% rename from v2/helper/helper.go rename to helper/helper.go diff --git a/v2/helper/increment_by.go b/helper/increment_by.go similarity index 100% rename from v2/helper/increment_by.go rename to helper/increment_by.go diff --git a/v2/helper/increment_by_test.go b/helper/increment_by_test.go similarity index 92% rename from v2/helper/increment_by_test.go rename to helper/increment_by_test.go index 9c3e37f..7d358ae 100644 --- a/v2/helper/increment_by_test.go +++ b/helper/increment_by_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestIncrementBy(t *testing.T) { diff --git a/v2/helper/keep_negatives.go b/helper/keep_negatives.go similarity index 100% rename from v2/helper/keep_negatives.go rename to helper/keep_negatives.go diff --git a/v2/helper/keep_negatives_test.go b/helper/keep_negatives_test.go similarity index 92% rename from v2/helper/keep_negatives_test.go rename to helper/keep_negatives_test.go index 7fdc9b1..f0f39a2 100644 --- a/v2/helper/keep_negatives_test.go +++ b/helper/keep_negatives_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestKeepNegatives(t *testing.T) { diff --git a/v2/helper/keep_positives.go b/helper/keep_positives.go similarity index 100% rename from v2/helper/keep_positives.go rename to helper/keep_positives.go diff --git a/v2/helper/keep_positives_test.go b/helper/keep_positives_test.go similarity index 92% rename from v2/helper/keep_positives_test.go rename to helper/keep_positives_test.go index db5ce03..85f17c1 100644 --- a/v2/helper/keep_positives_test.go +++ b/helper/keep_positives_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestKeepPositives(t *testing.T) { diff --git a/v2/helper/map.go b/helper/map.go similarity index 100% rename from v2/helper/map.go rename to helper/map.go diff --git a/v2/helper/map_test.go b/helper/map_test.go similarity index 93% rename from v2/helper/map_test.go rename to helper/map_test.go index 90ff6ab..591dc67 100644 --- a/v2/helper/map_test.go +++ b/helper/map_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestMap(t *testing.T) { diff --git a/v2/helper/multiply.go b/helper/multiply.go similarity index 100% rename from v2/helper/multiply.go rename to helper/multiply.go diff --git a/v2/helper/multiply_by.go b/helper/multiply_by.go similarity index 100% rename from v2/helper/multiply_by.go rename to helper/multiply_by.go diff --git a/v2/helper/multiply_by_test.go b/helper/multiply_by_test.go similarity index 92% rename from v2/helper/multiply_by_test.go rename to helper/multiply_by_test.go index f976963..23da59a 100644 --- a/v2/helper/multiply_by_test.go +++ b/helper/multiply_by_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestMultiplyBy(t *testing.T) { diff --git a/v2/helper/multiply_test.go b/helper/multiply_test.go similarity index 92% rename from v2/helper/multiply_test.go rename to helper/multiply_test.go index 784fc88..034b0ed 100644 --- a/v2/helper/multiply_test.go +++ b/helper/multiply_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestMultiply(t *testing.T) { diff --git a/v2/helper/operate.go b/helper/operate.go similarity index 100% rename from v2/helper/operate.go rename to helper/operate.go diff --git a/v2/helper/operate_test.go b/helper/operate_test.go similarity index 97% rename from v2/helper/operate_test.go rename to helper/operate_test.go index dd7c970..f5868ca 100644 --- a/v2/helper/operate_test.go +++ b/helper/operate_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestOperate(t *testing.T) { diff --git a/v2/helper/pipe.go b/helper/pipe.go similarity index 100% rename from v2/helper/pipe.go rename to helper/pipe.go diff --git a/v2/helper/pipe_test.go b/helper/pipe_test.go similarity index 92% rename from v2/helper/pipe_test.go rename to helper/pipe_test.go index f3f8b3b..13a37ee 100644 --- a/v2/helper/pipe_test.go +++ b/helper/pipe_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestPipe(t *testing.T) { diff --git a/v2/helper/pow.go b/helper/pow.go similarity index 100% rename from v2/helper/pow.go rename to helper/pow.go diff --git a/v2/helper/pow_test.go b/helper/pow_test.go similarity index 92% rename from v2/helper/pow_test.go rename to helper/pow_test.go index 4b22ed3..ae55fd0 100644 --- a/v2/helper/pow_test.go +++ b/helper/pow_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestPow(t *testing.T) { diff --git a/v2/helper/reflect.go b/helper/reflect.go similarity index 100% rename from v2/helper/reflect.go rename to helper/reflect.go diff --git a/v2/helper/reflect_test.go b/helper/reflect_test.go similarity index 100% rename from v2/helper/reflect_test.go rename to helper/reflect_test.go diff --git a/v2/helper/ring.go b/helper/ring.go similarity index 100% rename from v2/helper/ring.go rename to helper/ring.go diff --git a/v2/helper/ring_test.go b/helper/ring_test.go similarity index 92% rename from v2/helper/ring_test.go rename to helper/ring_test.go index d8a4855..6ef0f23 100644 --- a/v2/helper/ring_test.go +++ b/helper/ring_test.go @@ -7,7 +7,7 @@ package helper_test import ( "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestRing(t *testing.T) { diff --git a/v2/helper/round_digit.go b/helper/round_digit.go similarity index 100% rename from v2/helper/round_digit.go rename to helper/round_digit.go diff --git a/v2/helper/round_digit_test.go b/helper/round_digit_test.go similarity index 90% rename from v2/helper/round_digit_test.go rename to helper/round_digit_test.go index 273e832..3c64e3c 100644 --- a/v2/helper/round_digit_test.go +++ b/helper/round_digit_test.go @@ -7,7 +7,7 @@ package helper_test import ( "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestRoundDigit(t *testing.T) { diff --git a/v2/helper/round_digits.go b/helper/round_digits.go similarity index 100% rename from v2/helper/round_digits.go rename to helper/round_digits.go diff --git a/v2/helper/round_digits_test.go b/helper/round_digits_test.go similarity index 92% rename from v2/helper/round_digits_test.go rename to helper/round_digits_test.go index 6a2023e..aee79be 100644 --- a/v2/helper/round_digits_test.go +++ b/helper/round_digits_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestRoundDigits(t *testing.T) { diff --git a/v2/helper/seq.go b/helper/seq.go similarity index 100% rename from v2/helper/seq.go rename to helper/seq.go diff --git a/v2/helper/seq_test.go b/helper/seq_test.go similarity index 91% rename from v2/helper/seq_test.go rename to helper/seq_test.go index 6276d9c..9f061f2 100644 --- a/v2/helper/seq_test.go +++ b/helper/seq_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestSeq(t *testing.T) { diff --git a/v2/helper/shift.go b/helper/shift.go similarity index 100% rename from v2/helper/shift.go rename to helper/shift.go diff --git a/v2/helper/shift_test.go b/helper/shift_test.go similarity index 92% rename from v2/helper/shift_test.go rename to helper/shift_test.go index c159f6f..5d6d927 100644 --- a/v2/helper/shift_test.go +++ b/helper/shift_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestShift(t *testing.T) { diff --git a/v2/helper/sign.go b/helper/sign.go similarity index 100% rename from v2/helper/sign.go rename to helper/sign.go diff --git a/v2/helper/sign_test.go b/helper/sign_test.go similarity index 92% rename from v2/helper/sign_test.go rename to helper/sign_test.go index 694d54a..787977e 100644 --- a/v2/helper/sign_test.go +++ b/helper/sign_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestSign(t *testing.T) { diff --git a/v2/helper/since.go b/helper/since.go similarity index 100% rename from v2/helper/since.go rename to helper/since.go diff --git a/v2/helper/since_test.go b/helper/since_test.go similarity index 92% rename from v2/helper/since_test.go rename to helper/since_test.go index 37a1837..07cb6c5 100644 --- a/v2/helper/since_test.go +++ b/helper/since_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestSince(t *testing.T) { diff --git a/v2/helper/skip.go b/helper/skip.go similarity index 100% rename from v2/helper/skip.go rename to helper/skip.go diff --git a/v2/helper/skip_test.go b/helper/skip_test.go similarity index 92% rename from v2/helper/skip_test.go rename to helper/skip_test.go index 1f10f53..36c0b98 100644 --- a/v2/helper/skip_test.go +++ b/helper/skip_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestSkip(t *testing.T) { diff --git a/v2/helper/slice_to_chan.go b/helper/slice_to_chan.go similarity index 100% rename from v2/helper/slice_to_chan.go rename to helper/slice_to_chan.go diff --git a/v2/helper/slice_to_chan_test.go b/helper/slice_to_chan_test.go similarity index 91% rename from v2/helper/slice_to_chan_test.go rename to helper/slice_to_chan_test.go index 1971b2e..585f20b 100644 --- a/v2/helper/slice_to_chan_test.go +++ b/helper/slice_to_chan_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestSliceToChan(t *testing.T) { diff --git a/v2/helper/sqrt.go b/helper/sqrt.go similarity index 100% rename from v2/helper/sqrt.go rename to helper/sqrt.go diff --git a/v2/helper/sqrt_test.go b/helper/sqrt_test.go similarity index 92% rename from v2/helper/sqrt_test.go rename to helper/sqrt_test.go index 5dda58d..97454c7 100644 --- a/v2/helper/sqrt_test.go +++ b/helper/sqrt_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestSqrt(t *testing.T) { diff --git a/v2/helper/subtract.go b/helper/subtract.go similarity index 100% rename from v2/helper/subtract.go rename to helper/subtract.go diff --git a/v2/helper/subtract_test.go b/helper/subtract_test.go similarity index 92% rename from v2/helper/subtract_test.go rename to helper/subtract_test.go index c268fed..37a813c 100644 --- a/v2/helper/subtract_test.go +++ b/helper/subtract_test.go @@ -8,7 +8,7 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestSubstract(t *testing.T) { diff --git a/v2/helper/testdata/with_header.csv b/helper/testdata/with_header.csv similarity index 100% rename from v2/helper/testdata/with_header.csv rename to helper/testdata/with_header.csv diff --git a/v2/helper/testdata/without_header.csv b/helper/testdata/without_header.csv similarity index 100% rename from v2/helper/testdata/without_header.csv rename to helper/testdata/without_header.csv diff --git a/v2/helper/waitable.go b/helper/waitable.go similarity index 100% rename from v2/helper/waitable.go rename to helper/waitable.go diff --git a/v2/helper/waitable_test.go b/helper/waitable_test.go similarity index 89% rename from v2/helper/waitable_test.go rename to helper/waitable_test.go index 7748218..6c2e839 100644 --- a/v2/helper/waitable_test.go +++ b/helper/waitable_test.go @@ -8,7 +8,7 @@ import ( "sync" "testing" - "github.com/cinar/indicator/v2/helper" + "github.com/cinar/indicator/helper" ) func TestWaitable(t *testing.T) { diff --git a/helper_test.go b/helper_test.go deleted file mode 100644 index be1f358..0000000 --- a/helper_test.go +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import ( - "testing" -) - -func TestMultiplyBy(t *testing.T) { - values := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - multiplier := float64(2) - - result := multiplyBy(values, multiplier) - if len(result) != len(values) { - t.Fatal("result not same size") - } - - for i := 0; i < len(result); i++ { - expected := values[i] * multiplier - actual := result[i] - - if actual != expected { - t.Fatalf("result %d actual %f expected %f", i, actual, expected) - } - } -} - -func TestMultiply(t *testing.T) { - values1 := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - values2 := []float64{2, 4, 2, 4, 2, 4, 2, 4, 2, 4} - expected := []float64{2, 8, 6, 16, 10, 24, 14, 32, 18, 40} - - actual := multiply(values1, values2) - - if len(actual) != len(expected) { - t.Fatalf("actual %d expected %d", len(actual), len(expected)) - } - - for i := 0; i < len(actual); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestDivideBy(t *testing.T) { - values := []float64{2, 4, 6, 8, 10, 12, 14, 16, 18, 20} - divider := float64(2) - - result := divideBy(values, divider) - if len(result) != len(values) { - t.Fatal("result not same size") - } - - for i := 0; i < len(result); i++ { - expected := values[i] / divider - actual := result[i] - - if actual != expected { - t.Fatalf("result %d actual %f expected %f", i, actual, expected) - } - } -} - -func TestDivide(t *testing.T) { - values1 := []float64{2, 8, 6, 16, 10, 24, 14, 32, 18, 40} - values2 := []float64{2, 4, 2, 4, 2, 4, 2, 4, 2, 4} - expected := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - - actual := divide(values1, values2) - checkSameSize(actual, expected) - - for i := 0; i < len(actual); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestAddWithDifferentSizes(t *testing.T) { - values1 := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - values2 := []float64{1, 2, 3, 4, 5} - - defer func() { - if r := recover(); r == nil { - t.Fatal("did not check size") - } - }() - - add(values1, values2) -} - -func TestAdd(t *testing.T) { - values := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - - result := add(values, values) - checkSameSize(result, values) - - for i := 0; i < len(result); i++ { - expected := values[i] + values[i] - actual := result[i] - - if actual != expected { - t.Fatalf("result %d actual %f expected %f", i, actual, expected) - } - } -} - -func TestAddBy(t *testing.T) { - values := []float64{1, 2, 3, 4} - expected := []float64{2, 3, 4, 5} - - actual := addBy(values, 1) - checkSameSize(actual, expected) - - for i := 0; i < len(actual); i++ { - if actual[i] != expected[i] { - t.Fatalf("result %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestSubtract(t *testing.T) { - values1 := []float64{10, 20, 30, 40, 50, 60, 70, 80, 90, 100} - values2 := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - - result := subtract(values1, values2) - checkSameSize(result, values1) - - for i := 0; i < len(result); i++ { - expected := values1[i] - values2[i] - actual := result[i] - - if actual != expected { - t.Fatalf("result %d actual %f expected %f", i, actual, expected) - } - } -} - -func TestDiff(t *testing.T) { - values := []float64{1, 2, 1, 4, 2, 2, 6, 8, 2, 10} - expected := []float64{1, 1, -1, 3, -2, 0, 4, 2, -6, 8} - before := 1 - - actual := diff(values, before) - checkSameSize(actual, expected) - - for i := 0; i < len(actual); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestPercentDiff(t *testing.T) { - values := []float64{1, 2, 8, 32, 64} - expected := []float64{0, 1, 3, 3, 1} - before := 1 - - actual := percentDiff(values, before) - checkSameSize(actual, expected) - - for i := 0; i < len(actual); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestShiftRightAndFillBy(t *testing.T) { - values := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - expected := []float64{1, 1, 1, 1, 1, 2, 3, 4, 5, 6} - period := 4 - - actual := shiftRightAndFillBy(period, values[0], values) - checkSameSize(actual, expected) - - for i := 0; i < len(actual); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestShiftRight(t *testing.T) { - values := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - expected := []float64{0, 0, 0, 0, 1, 2, 3, 4, 5, 6} - period := 4 - - actual := shiftRight(period, values) - checkSameSize(actual, expected) - - for i := 0; i < len(actual); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestRoundDigits(t *testing.T) { - value := 1.5182345 - expected := 1.5182 - - actual := roundDigits(value, 4) - - if actual != expected { - t.Fatalf("actual %f expected %f", actual, expected) - } -} - -func TestGenerateNunbers(t *testing.T) { - expected := []float64{2, 4, 6, 8} - - actual := generateNumbers(2, 10, 2) - - if len(actual) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(expected); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestAsFloat64(t *testing.T) { - values := []int64{1, 2, 3, 4} - expected := []float64{1, 2, 3, 4} - - actual := asFloat64(values) - - if len(actual) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(expected); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestPow(t *testing.T) { - values := []float64{1, 2, 3, 4} - expected := []float64{1, 4, 9, 16} - exponent := 2.0 - - actual := pow(values, exponent) - - if len(actual) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(expected); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestExtractSign(t *testing.T) { - values := []float64{1, -2, 3, 4, -5, 0, 6, -8, -9, 10} - expected := []float64{1, -1, 1, 1, -1, 1, 1, -1, -1, 1} - - actual := extractSign(values) - - if len(actual) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(expected); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestKeepPositives(t *testing.T) { - values := []float64{1, -2, -3, 4} - expected := []float64{1, 0, 0, 4} - - actual := keepPositives(values) - - if len(actual) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(expected); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} - -func TestKeepNegatives(t *testing.T) { - values := []float64{1, -2, -3, 4} - expected := []float64{0, -2, -3, 0} - - actual := keepNegatives(values) - - if len(actual) != len(expected) { - t.Fatal("not the same size") - } - - for i := 0; i < len(expected); i++ { - if actual[i] != expected[i] { - t.Fatalf("at %d actual %f expected %f", i, actual[i], expected[i]) - } - } -} diff --git a/indicator.go b/indicator.go deleted file mode 100644 index e61a18f..0000000 --- a/indicator.go +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator diff --git a/momentum_indicators.go b/momentum_indicators.go deleted file mode 100644 index b62652b..0000000 --- a/momentum_indicators.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -// Awesome Oscillator. -// -// Median Price = ((Low + High) / 2). -// AO = 5-Period SMA - 34-Period SMA. -// -// Returns ao. -func AwesomeOscillator(low, high []float64) []float64 { - medianPrice := divideBy(add(low, high), float64(2)) - sma5 := Sma(5, medianPrice) - sma34 := Sma(34, medianPrice) - ao := subtract(sma5, sma34) - - return ao -} - -// The ChaikinOscillator function measures the momentum of the -// Accumulation/Distribution (A/D) using the Moving Average -// Convergence Divergence (MACD) formula. It takes the -// difference between fast and slow periods EMA of the A/D. -// Cross above the A/D line indicates bullish. -// -// CO = Ema(fastPeriod, AD) - Ema(slowPeriod, AD) -// -// Returns co, ad. -func ChaikinOscillator(fastPeriod, slowPeriod int, low, high, closing, volume []float64) ([]float64, []float64) { - ad := AccumulationDistribution(high, low, closing, volume) - co := subtract(Ema(fastPeriod, ad), Ema(slowPeriod, ad)) - - return co, ad -} - -// The DefaultChaikinOscillator function calculates Chaikin -// Oscillator with the most frequently used fast and short -// periods, 3 and 10. -// -// Returns co, ad. -func DefaultChaikinOscillator(low, high, closing, volume []float64) ([]float64, []float64) { - return ChaikinOscillator(3, 10, low, high, closing, volume) -} - -// Ichimoku Cloud. Also known as Ichimoku Kinko Hyo, is a versatile indicator that defines support and -// resistence, identifies trend direction, gauges momentum, and provides trading signals. -// -// Tenkan-sen (Conversion Line) = (9-Period High + 9-Period Low) / 2 -// Kijun-sen (Base Line) = (26-Period High + 26-Period Low) / 2 -// Senkou Span A (Leading Span A) = (Conversion Line + Base Line) / 2 -// Senkou Span B (Leading Span B) = (52-Period High + 52-Period Low) / 2 -// Chikou Span (Lagging Span) = Closing plotted 26 days in the past. -// -// Returns conversionLine, baseLine, leadingSpanA, leadingSpanB, laggingSpan -func IchimokuCloud(high, low, closing []float64) ([]float64, []float64, []float64, []float64, []float64) { - checkSameSize(high, low, closing) - - conversionLine := divideBy(add(Max(9, high), Min(9, low)), float64(2)) - baseLine := divideBy(add(Max(26, high), Min(26, low)), float64(2)) - leadingSpanA := divideBy(add(conversionLine, baseLine), float64(2)) - leadingSpanB := divideBy(add(Max(52, high), Min(52, low)), float64(2)) - laggingLine := shiftRight(26, closing) - - return conversionLine, baseLine, leadingSpanA, leadingSpanB, laggingLine -} - -// Percentage Price Oscillator (PPO). It is a momentum oscillator for the price. -// It is used to indicate the ups and downs based on the price. A breakout is -// confirmed when PPO is positive. -// -// PPO = ((EMA(fastPeriod, prices) - EMA(slowPeriod, prices)) / EMA(longPeriod, prices)) * 100 -// Signal = EMA(9, PPO) -// Histogram = PPO - Signal -// -// Returns ppo, signal, histogram -func PercentagePriceOscillator(fastPeriod, slowPeriod, signalPeriod int, price []float64) ([]float64, []float64, []float64) { - fastEma := Ema(fastPeriod, price) - slowEma := Ema(slowPeriod, price) - ppo := multiplyBy(divide(subtract(fastEma, slowEma), slowEma), 100) - signal := Ema(signalPeriod, ppo) - histogram := subtract(ppo, signal) - - return ppo, signal, histogram -} - -// Default Percentage Price Oscillator calculates it with the default periods of 12, 26, 9. -// -// Returns ppo, signal, histogram -func DefaultPercentagePriceOscillator(price []float64) ([]float64, []float64, []float64) { - return PercentagePriceOscillator(12, 26, 9, price) -} - -// Percentage Volume Oscillator (PVO). It is a momentum oscillator for the volume. -// It is used to indicate the ups and downs based on the volume. A breakout is -// confirmed when PVO is positive. -// -// PVO = ((EMA(fastPeriod, volumes) - EMA(slowPeriod, volumes)) / EMA(longPeriod, volumes)) * 100 -// Signal = EMA(9, PVO) -// Histogram = PVO - Signal -// -// Returns pvo, signal, histogram -func PercentageVolumeOscillator(fastPeriod, slowPeriod, signalPeriod int, volume []float64) ([]float64, []float64, []float64) { - fastEma := Ema(fastPeriod, volume) - slowEma := Ema(slowPeriod, volume) - pvo := multiplyBy(divide(subtract(fastEma, slowEma), slowEma), 100) - signal := Ema(signalPeriod, pvo) - histogram := subtract(pvo, signal) - - return pvo, signal, histogram -} - -// Default Percentage Volume Oscillator calculates it with the default periods of 12, 26, 9. -// -// Returns pvo, signal, histogram -func DefaultPercentageVolumeOscillator(volume []float64) ([]float64, []float64, []float64) { - return PercentageVolumeOscillator(12, 26, 9, volume) -} - -// Relative Strength Index (RSI). It is a momentum indicator that measures the magnitude -// of recent price changes to evaluate overbought and oversold conditions. -// -// RS = Average Gain / Average Loss -// RSI = 100 - (100 / (1 + RS)) -// -// Returns rs, rsi -func Rsi(closing []float64) ([]float64, []float64) { - return RsiPeriod(14, closing) -} - -// RSI with 2 period, a mean-reversion trading strategy -// developed by Larry Connors. -// -// REturns rs, rsi -func Rsi2(closing []float64) ([]float64, []float64) { - return RsiPeriod(2, closing) -} - -// RsiPeriod allows to calculate the RSI indicator with a non-standard period. -func RsiPeriod(period int, closing []float64) ([]float64, []float64) { - gains := make([]float64, len(closing)) - losses := make([]float64, len(closing)) - - for i := 1; i < len(closing); i++ { - difference := closing[i] - closing[i-1] - - if difference > 0 { - gains[i] = difference - losses[i] = 0 - } else { - losses[i] = -difference - gains[i] = 0 - } - } - - meanGains := Rma(period, gains) - meanLosses := Rma(period, losses) - - rsi := make([]float64, len(closing)) - rs := make([]float64, len(closing)) - - for i := 0; i < len(rsi); i++ { - rs[i] = meanGains[i] / meanLosses[i] - rsi[i] = 100 - (100 / (1 + rs[i])) - } - - return rs, rsi -} - -// Stochastic Oscillator. It is a momentum indicator that shows the location of the closing -// relative to high-low range over a set number of periods. -// -// K = (Closing - Lowest Low) / (Highest High - Lowest Low) * 100 -// D = 3-Period SMA of K -// -// Returns k, d -func StochasticOscillator(high, low, closing []float64) ([]float64, []float64) { - checkSameSize(high, low, closing) - - highestHigh14 := Max(14, high) - lowestLow14 := Min(14, low) - - k := multiplyBy(divide(subtract(closing, lowestLow14), subtract(highestHigh14, lowestLow14)), float64(100)) - d := Sma(3, fillNaNWith(k, 0)) - - return k, d -} - -// Williams R. Determine overbought and oversold. -// -// WR = (Highest High - Closing) / (Highest High - Lowest Low) * -100. -// -// Buy when -80 and below. Sell when -20 and above. -// -// Returns wr. -func WilliamsR(low, high, closing []float64) []float64 { - period := 14 - - highestHigh := Max(period, high) - lowestLow := Min(period, low) - - result := make([]float64, len(closing)) - - for i := 0; i < len(closing); i++ { - result[i] = (highestHigh[i] - closing[i]) / (highestHigh[i] - lowestLow[i]) * float64(-100) - } - - return result -} diff --git a/momentum_indicators.md b/momentum_indicators.md deleted file mode 100644 index 23c862a..0000000 --- a/momentum_indicators.md +++ /dev/null @@ -1,171 +0,0 @@ -### Momentum Indicators - -Momentum indicators measure the speed of movement. - -- [Awesome Oscillator](#awesome-oscillator) -- [Chaikin Oscillator](#chaikin-oscillator) -- [Ichimoku Cloud](#ichimoku-cloud) -- [Percentage Price Oscillator (PPO)](#percentage-price-oscillator-ppo) -- [Percentage Volume Oscillator (PVO)](#percentage-volume-oscillator-pvo) -- [Relative Strength Index (RSI)](#relative-strength-index-rsi) -- [RSI 2](#rsi-2) -- [RSI Period](#rsi-period) -- [Stochastic Oscillator](#stochastic-oscillator) -- [Williams R](#williams-r) - -#### Awesome Oscillator - -The [AwesomeOscillator](https://pkg.go.dev/github.com/cinar/indicator#AwesomeOscillator) function calculates the awesome oscillator based on low and high daily prices for a given stock. It is an indicator used to measure market momentum. - -``` -Median Price = ((Low + High) / 2) -AO = 5-Period SMA - 34-Period SMA. -``` - -```Golang -result := indicator.AwesomeOscillator(low, high) -``` - -#### Chaikin Oscillator - -The [ChaikinOscillator](https://pkg.go.dev/github.com/cinar/indicator#ChaikinOscillator) function measures the momentum of the [Accumulation/Distribution (A/D)](volume_indicators.md#accumulationdistribution-ad) using the [Moving Average Convergence Divergence (MACD)](trend_indicators.md#moving-average-convergence-divergence-macd) formula. It takes the difference between fast and slow periods EMA of the A/D. Cross above the A/D line indicates bullish. - -``` -CO = Ema(fastPeriod, AD) - Ema(slowPeriod, AD) -``` - -```Golang -co, ad := indicator.ChaikinOscillator(fastPeriod, slowPeriod, high, low, closing) -``` - -Most frequently used fast and short periods are 3 and 10. The [DefaultChaikinOscillator](https://pkg.go.dev/github.com/cinar/indicator#DefaultChaikinOscillator) function calculates Chaikin Oscillator with those periods. - -#### Ichimoku Cloud - -The [IchimokuCloud](https://pkg.go.dev/github.com/cinar/indicator#IchimokuCloud), also known as Ichimoku Kinko Hyo, calculates a versatile indicator that defines support and resistence, identifies tred direction, gauges momentum, and provides trading signals. - -``` -Tenkan-sen (Conversion Line) = (9-Period High + 9-Period Low) / 2 -Kijun-sen (Base Line) = (26-Period High + 26-Period Low) / 2 -Senkou Span A (Leading Span A) = (Conversion Line + Base Line) / 2 -Senkou Span B (Leading Span B) = (52-Period High + 52-Period Low) / 2 -Chikou Span (Lagging Span) = Closing plotted 26 days in the past. -``` - -```Golang -conversionLine, baseLine, leadingSpanA, leadingSpanB, laggingLine := indicator.IchimokuCloud(high, low, closing) -``` - -#### Percentage Price Oscillator (PPO) - -The [PercentagePriceOscillator](https://pkg.go.dev/github.com/cinar/indicator#PercentagePriceOscillator) function calculates a momentum oscillator for the price It is used to indicate the ups and downs based on the price. A breakout is confirmed when PPO is positive. - -``` -PPO = ((EMA(fastPeriod, prices) - EMA(slowPeriod, prices)) / EMA(longPeriod, prices)) * 100 -Signal = EMA(9, PPO) -Histogram = PPO - Signal -``` - -```Golang -ppo, signal, histogram := indicator.PercentagePriceOscillator( - fastPeriod, - slowPeriod, - signalPeriod, - price -) -``` - -The [DefaultPercentagePriceOscillator](https://pkg.go.dev/github.com/cinar/indicator#DefaultPercentagePriceOscillator) function calculates it with the default periods of 12, 26, 9. - -```Golang -ppo, signal, histogram := indicator.DefaultPercentagePriceOscillator(price) -``` - -#### Percentage Volume Oscillator (PVO) - -The [PercentageVolumeOscillator](https://pkg.go.dev/github.com/cinar/indicator#PercentageVolumeOscillator) function calculates a momentum oscillator for the volume It is used to indicate the ups and downs based on the volume. A breakout is confirmed when PVO is positive. - -``` -PVO = ((EMA(fastPeriod, volumes) - EMA(slowPeriod, volumes)) / EMA(longPeriod, volumes)) * 100 -Signal = EMA(9, PVO) -Histogram = PVO - Signal -``` - -```Golang -pvo, signal, histogram := indicator.PercentageVolumeOscillator( - fastPeriod, - slowPeriod, - signalPeriod, - volume -) -``` - -The [DefaultPercentageVolumeOscillator](https://pkg.go.dev/github.com/cinar/indicator#DefaultPercentageVolumeOscillator) function calculates it with the default periods of 12, 26, 9. - -```Golang -pvo, signal, histogram := indicator.DefaultPercentageVolumeOscillator(volume) -``` - -#### Relative Strength Index (RSI) - -The [Rsi](https://pkg.go.dev/github.com/cinar/indicator#Rsi) function calculates a momentum indicator that measures the magnitude of recent price changes to evaluate overbought and oversold conditions. - -``` -RS = Average Gain / Average Loss -RSI = 100 - (100 / (1 + RS)) -``` - -```Golang -rs, rsi := indicator.Rsi(closing) -``` - -#### RSI 2 - -The [Rsi2](https://pkg.go.dev/github.com/cinar/indicator#Rsi2) function calculates a calculates a RSI with 2 period that provides a mean-reversion trading strategy. It is developed by Larry Connors. - -```Golang -rs, rsi := indicator.Rsi2(closing) -``` - -#### RSI Period - -The [RsiPeriod](https://pkg.go.dev/github.com/cinar/indicator#RsiPeriod) allows to calculate the RSI indicator with a non-standard period. - -```Golang -rs, rsi := indicator.RsiPeriod(period, closing) -``` - -#### Stochastic Oscillator - -The [StochasticOscillator](https://pkg.go.dev/github.com/cinar/indicator#StochasticOscillator) function calculates a momentum indicator that shows the location of the closing relative to high-low range over a set number of periods. - -``` -K = (Closing - Lowest Low) / (Highest High - Lowest Low) * 100 -D = 3-Period SMA of K -``` - -```Golang -k, d := indicator.StochasticOscillator(high, low, closing) -``` - -#### Williams R - -The [WilliamsR](https://pkg.go.dev/github.com/cinar/indicator#WilliamsR) function calculates the Williams R based on low, high, and closing prices. It is a type of momentum indicator that moves between 0 and -100 and measures overbought and oversold levels. - -``` -WR = (Highest High - Closing) / (Highest High - Lowest Low) -``` - -```Golang -result := indicator.WilliamsR(low, high, closing) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. diff --git a/momentum_indicators_test.go b/momentum_indicators_test.go deleted file mode 100644 index fb9fc1a..0000000 --- a/momentum_indicators_test.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import ( - "testing" -) - -func TestChaikinOscillator(t *testing.T) { - high := []float64{10, 11, 12, 13, 14, 15, 16, 17} - low := []float64{1, 2, 3, 4, 5, 6, 7, 8} - closing := []float64{5, 6, 7, 8, 9, 10, 11, 12} - volume := []float64{100, 200, 300, 400, 500, 600, 700, 800} - expected := []float64{0, -7.41, -18.52, -31.69, -46.09, -61.27, -76.95, -92.97} - - actual, _ := ChaikinOscillator(2, 5, low, high, closing, volume) - testEquals(t, roundDigitsAll(actual, 2), expected) -} - -func TestPercentagePriceOscillator(t *testing.T) { - price := []float64{ - 3674.84, 3666.77, 3789.99, 3735.48, 3749.63, 3900.86, 4017.82, 4115.77, - 4160.68, 4121.43, 4108.54, 4176.82, 4101.23, 4132.15, 4158.24, 4057.84, - 3978.73, 3941.48, 3973.75, 3901.36, 3900.79, 3923.68, 4088.85, 4008.01, - 4023.89, 3930.08, 3935.18, 4001.05, 3991.24, 4123.34, 4146.87, 4300.17, - 4175.48, 4155.38, 4131.93, 4287.5, 4183.96, 4175.2, 4296.12, 4271.78, - 4393.66, 4459.45, 4462.21, 4391.69, 4392.59, 4446.59, 4397.45, 4412.53, - 4488.28, 4500.21, 4481.15, 4525.12, 4582.64, 4545.86, 4530.41, 4602.45, - 4631.6, 4575.52, 4543.06, 4520.16, 4456.24, 4511.61, 4461.18, 4463.12, - 4411.67, 4357.86, 4262.45, 4173.11, 4204.31, 4259.52, 4277.88, 4170.7, - 4201.09, 4328.87, 4363.49, 4386.54, 4306.26, 4373.94, 4384.65, 4288.7, - 4225.5, 4304.76, 4348.87, 4380.26, 4475.01, 4471.07, 4401.67, 4418.64, - 4504.08, 4587.18, - } - - expectedPpo := []float64{ - 0, -0.02, 0.24, 0.31, 0.4, 0.79, 1.33, 1.93, 2.47, 2.77, 2.95, 3.19, 3.18, - 3.2, 3.22, 3.01, 2.65, 2.26, 2, 1.63, 1.31, 1.1, 1.25, 1.19, 1.16, 0.94, - 0.77, 0.75, 0.71, 0.94, 1.15, 1.6, 1.69, 1.7, 1.64, 1.88, 1.84, 1.77, - 1.93, 1.98, 2.23, 2.52, 2.72, 2.71, 2.68, 2.71, 2.62, 2.55, 2.6, 2.62, - 2.58, 2.6, 2.68, 2.65, 2.56, 2.6, 2.64, 2.55, 2.39, 2.19, 1.9, 1.75, 1.52, - 1.33, 1.07, 0.77, 0.34, -0.16, -0.49, -0.65, -0.73, -0.99, -1.12, -0.98, - -0.79, -0.59, -0.58, -0.43, -0.3, -0.36, -0.53, -0.5, -0.4, -0.25, 0.04, - 0.25, 0.3, 0.36, 0.55, 0.85, - } - - expectedSignal := []float64{ - 0, -0, 0.04, 0.1, 0.16, 0.28, 0.49, 0.78, 1.12, 1.45, 1.75, 2.04, 2.27, - 2.45, 2.61, 2.69, 2.68, 2.6, 2.48, 2.31, 2.11, 1.91, 1.77, 1.66, 1.56, - 1.44, 1.3, 1.19, 1.1, 1.06, 1.08, 1.19, 1.29, 1.37, 1.42, 1.51, 1.58, - 1.62, 1.68, 1.74, 1.84, 1.98, 2.13, 2.24, 2.33, 2.41, 2.45, 2.47, 2.49, - 2.52, 2.53, 2.55, 2.57, 2.59, 2.58, 2.59, 2.6, 2.59, 2.55, 2.48, 2.36, - 2.24, 2.1, 1.94, 1.77, 1.57, 1.32, 1.03, 0.72, 0.45, 0.21, -0.03, -0.25, - -0.39, -0.47, -0.5, -0.51, -0.5, -0.46, -0.44, -0.46, -0.47, -0.45, -0.41, - -0.32, -0.21, -0.11, -0.01, 0.1, 0.25, - } - - expectedHistogram := []float64{ - 0, -0.01, 0.19, 0.22, 0.24, 0.5, 0.83, 1.15, 1.35, 1.32, 1.2, 1.15, 0.91, - 0.75, 0.62, 0.32, -0.03, -0.33, -0.48, -0.68, -0.79, -0.81, -0.52, -0.47, - -0.4, -0.49, -0.54, -0.44, -0.38, -0.13, 0.07, 0.42, 0.4, 0.33, 0.22, - 0.36, 0.26, 0.15, 0.25, 0.24, 0.39, 0.55, 0.6, 0.47, 0.35, 0.31, 0.17, - 0.08, 0.1, 0.1, 0.05, 0.05, 0.11, 0.06, -0.02, 0.01, 0.05, -0.04, -0.16, - -0.28, -0.46, -0.49, -0.57, -0.61, -0.69, -0.8, -0.98, -1.18, -1.22, -1.1, - -0.95, -0.96, -0.88, -0.58, -0.32, -0.09, -0.06, 0.06, 0.16, 0.07, -0.07, - -0.04, 0.05, 0.16, 0.36, 0.46, 0.4, 0.37, 0.45, 0.6, - } - - actualPpo, actualSignal, actualHistogram := DefaultPercentagePriceOscillator(price) - testEquals(t, roundDigitsAll(actualPpo, 2), expectedPpo) - testEquals(t, roundDigitsAll(actualSignal, 2), expectedSignal) - testEquals(t, roundDigitsAll(actualHistogram, 2), expectedHistogram) -} - -func TestPercentageVolumeOscillator(t *testing.T) { - volume := []float64{ - 6954, 4511, 4474, 4126, 4572, 3936, 3192, 3090, 3476, 3852, 3107, 3604, - 4145, 5192, 3560, 3961, 4322, 3901, 3392, 4278, 4212, 4428, 3846, 3824, - 4142, 4964, 4683, 4630, 4746, 4254, 4197, 4236, 3877, 4474, 3943, 3969, - 3876, 3760, 4061, 3930, 3833, 3678, 3197, 3509, 3634, 3273, 3451, 3452, - 3453, 4054, 4137, 3906, 3833, 3828, 3782, 3665, 4239, 3696, 3577, 3573, - 4014, 3962, 3961, 6681, 4174, 5002, 4331, 4757, 3877, 4008, 4220, 6237, - 5506, 4558, 4062, 4409, 4679, 4594, 3941, 5070, 3814, 4007, 3871, 3596, - 3478, 3363, 3466, 4164, 4490, 3662, - } - - expectedPvo := []float64{ - 0, -2.88, -5.28, -7.67, -8.98, -10.88, -13.49, -15.76, -16.92, -17.12, - -18.45, -18.51, -17.41, -14.45, -14.91, -14.4, -13.19, -12.87, -13.46, - -12.11, -11.04, -9.66, -9.57, -9.45, -8.64, -6.34, -5.01, -4.01, -2.97, - -3.01, -3.12, -3.1, -3.72, -3.05, -3.49, -3.75, -4.09, -4.55, -4.28, - -4.28, -4.42, -4.79, -6.03, -6.32, -6.22, -6.85, -6.9, -6.88, -6.78, - -5.32, -3.95, -3.32, -2.94, -2.63, -2.44, -2.52, -1.35, -1.55, -1.94, - -2.23, -1.51, -1.04, -0.66, 4.99, 4.41, 5.45, 4.94, 5.27, 3.86, 2.94, - 2.59, 5.92, 7.04, 6.21, 4.63, 3.94, 3.82, 3.54, 2.13, 2.99, 1.42, 0.49, - -0.5, -1.8, -3.06, -4.27, -5, -4.11, -2.72, -3.24, - } - - expectedSignal := []float64{ - 0, -0.58, -1.52, -2.75, -3.99, -5.37, -6.99, -8.75, -10.38, -11.73, - -13.07, -14.16, -14.81, -14.74, -14.77, -14.7, -14.4, -14.09, -13.97, - -13.59, -13.08, -12.4, -11.83, -11.36, -10.81, -9.92, -8.94, -7.95, -6.96, - -6.17, -5.56, -5.07, -4.8, -4.45, -4.26, -4.15, -4.14, -4.22, -4.23, - -4.24, -4.28, -4.38, -4.71, -5.03, -5.27, -5.59, -5.85, -6.05, -6.2, - -6.02, -5.61, -5.15, -4.71, -4.29, -3.92, -3.64, -3.18, -2.86, -2.67, - -2.58, -2.37, -2.1, -1.81, -0.45, 0.52, 1.51, 2.19, 2.81, 3.02, 3, 2.92, - 3.52, 4.22, 4.62, 4.62, 4.49, 4.35, 4.19, 3.78, 3.62, 3.18, 2.64, 2.02, - 1.25, 0.39, -0.54, -1.43, -1.97, -2.12, -2.34, - } - - expectedHistogram := []float64{ - 0, -2.3, -3.76, -4.92, -4.98, -5.51, -6.5, -7.02, -6.54, -5.39, -5.38, - -4.35, -2.6, 0.29, -0.13, 0.3, 1.21, 1.22, 0.5, 1.48, 2.05, 2.74, 2.26, - 1.91, 2.17, 3.58, 3.93, 3.94, 3.99, 3.15, 2.44, 1.97, 1.08, 1.39, 0.77, - 0.41, 0.05, -0.33, -0.05, -0.03, -0.14, -0.41, -1.32, -1.28, -0.95, -1.26, - -1.06, -0.82, -0.58, 0.7, 1.66, 1.83, 1.77, 1.67, 1.48, 1.12, 1.84, 1.31, - 0.74, 0.35, 0.86, 1.07, 1.16, 5.44, 3.89, 3.94, 2.75, 2.46, 0.84, -0.06, - -0.33, 2.4, 2.82, 1.59, 0.01, -0.55, -0.53, -0.65, -1.65, -0.63, -1.76, - -2.15, -2.51, -3.05, -3.45, -3.73, -3.56, -2.14, -0.6, -0.9, - } - - actualPvo, actualSignal, actualHistogram := DefaultPercentageVolumeOscillator(volume) - testEquals(t, roundDigitsAll(actualPvo, 2), expectedPvo) - testEquals(t, roundDigitsAll(actualSignal, 2), expectedSignal) - testEquals(t, roundDigitsAll(actualHistogram, 2), expectedHistogram) -} - -func TestStochasticOscillator(t *testing.T) { - high := []float64{ - 127.01, 127.62, 126.59, 127.35, 128.17, - 128.43, 127.37, 126.42, 126.90, 126.85, - 125.65, 125.72, 127.16, 127.72, 127.69, - 128.22, 128.27, 128.09, 128.27, 127.74, - } - low := []float64{ - 125.36, 126.16, 124.93, 126.09, 126.82, - 126.48, 126.03, 124.83, 126.39, 125.72, - 124.56, 124.57, 125.07, 126.86, 126.63, - 126.80, 126.71, 126.80, 126.13, 125.92, - } - closing := []float64{ - 126.00, 126.60, 127.10, 127.20, 128.10, - 128.20, 126.30, 126.00, 126.60, 127.00, - 127.50, 128.00, 128.10, 127.29, 127.18, - 128.01, 127.11, 127.73, 127.06, 127.33, - } - expectedK := []float64{ - 38.79, 54.87, 80.67, 84.39, 97.84, - 93.43, 39.14, 32.5, 49.17, 60.28, - 75.97, 88.89, 91.47, 70.54, 67.7, - 89.15, 65.89, 81.91, 64.60, 74.66, - } - - actualK, _ := StochasticOscillator(high, low, closing) - testEquals(t, roundDigitsAll(actualK, 2), expectedK) -} diff --git a/momentum_strategies.go b/momentum_strategies.go deleted file mode 100644 index bf26ccc..0000000 --- a/momentum_strategies.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -// Awesome oscillator strategy function. -func AwesomeOscillatorStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - ao := AwesomeOscillator(asset.Low, asset.High) - - for i := 0; i < len(actions); i++ { - if ao[i] > 0 { - actions[i] = BUY - } else if ao[i] < 0 { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// RSI strategy. Sells above sell at, buys below buy at. -func RsiStrategy(asset *Asset, sellAt, buyAt float64) []Action { - actions := make([]Action, len(asset.Date)) - - _, rsi := Rsi(asset.Closing) - - for i := 0; i < len(actions); i++ { - if rsi[i] <= buyAt { - actions[i] = BUY - } else if rsi[i] >= sellAt { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Default RSI strategy function. It buys -// below 30 and sells above 70. -func DefaultRsiStrategy(asset *Asset) []Action { - return RsiStrategy(asset, 70, 30) -} - -// Make RSI strategy function. -func MakeRsiStrategy(sellAt, buyAt float64) StrategyFunction { - return func(asset *Asset) []Action { - return RsiStrategy(asset, sellAt, buyAt) - } -} - -// RSI 2 strategy. When 2-period RSI moves below 10, it is considered deeply oversold, -// and the other way around when moves above 90. -func Rsi2Strategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - _, rsi := Rsi2(asset.Closing) - - for i := 0; i < len(actions); i++ { - if rsi[i] < 10 { - actions[i] = BUY - } else if rsi[i] > 90 { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Williams R strategy function. -func WilliamsRStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - wr := WilliamsR(asset.Low, asset.High, asset.Closing) - - for i := 0; i < len(actions); i++ { - if wr[i] < -20 { - actions[i] = SELL - } else if wr[i] > -80 { - actions[i] = BUY - } else { - actions[i] = HOLD - } - } - - return actions -} diff --git a/momentum_strategies.md b/momentum_strategies.md deleted file mode 100644 index 0c6badc..0000000 --- a/momentum_strategies.md +++ /dev/null @@ -1,63 +0,0 @@ -### Momentum Strategies - -Momentum strategies generate signals based on a momentum indicator. - -- [Awesome Oscillator Strategy](#awesome-oscillator-strategy) -- [RSI Strategy](#rsi-strategy) -- [RSI 2 Strategy](#rsi-2-strategy) -- [Williams R Strategy](#williams-r-strategy) - -#### Awesome Oscillator Strategy - -The [AwesomeOscillatorStrategy](https://pkg.go.dev/github.com/cinar/indicator#AwesomeOscillatorStrategy) uses the _ao_ values that are generated by the [AwesomeOscillator](https://pkg.go.dev/github.com/cinar/indicator#AwesomeOscillator) indicator function to provide a SELL action when the _ao_ is below zero, and a BUY action when _ao_ is above zero. - -```golang -actions := indicator.AwesomeOscillatorStrategy(asset) -``` - -#### RSI Strategy - -The [RsiStrategy](https://pkg.go.dev/github.com/cinar/indicator#RsiStrategy) uses the _rsi_ values that are generated by the [Rsi](https://pkg.go.dev/github.com/cinar/indicator#Rsi) indicator function to provide a BUY action when _rsi_ is below the _buyAt_ parameter, and a SELL action when _rsi_ is above the _sellAt_ parameter. - -```golang -actions := indicator.RsiStrategy(asset, 70, 30) -``` - -The RSI strategy is usually used with 70-30, or 80-20 values. The [DefaultRsiStrategy](https://pkg.go.dev/github.com/cinar/indicator#DefaultRsiStrategy) function uses the 70-30 values. - -```golang -actions := indicator.DefaultRsiStrategy(asset) -``` - -The function signature of [RsiStrategy](https://pkg.go.dev/github.com/cinar/indicator#RsiStrategy) does not match the [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction) type, as it requires an additional _sellAt_, and _buyAt_ parameters. The [MakeRsiStrategy](https://pkg.go.dev/github.com/cinar/indicator#MakeRsiStrategy) function can be used to return a [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction) instance based on the given _sellAt_, and _buyAt_ values. - -```golang -strategy := indicator.MakeRsiStrategy(80, 20) -actions := strategy(asset) -``` - -#### RSI 2 Strategy - -The [Rsi2Strategy](https://pkg.go.dev/github.com/cinar/indicator#Rsi2Strategy) uses the _rsi_ values that are generated by the [Rsi2](https://pkg.go.dev/github.com/cinar/indicator#Rsi2) indicator function to provide a _BUY_ action when 2-period RSI moves below 10, and a _SELL_ action when the 2-period RSI moved above 90, and a _HOLD_ action otherwise. - -```golang -actions := indicator.Rsi2Strategy(asset) -``` - -#### Williams R Strategy - -The [WilliamsRStrategy](https://pkg.go.dev/github.com/cinar/indicator#WilliamsRStrategy) uses the _wr_ values that are generated by the [WilliamsR](https://pkg.go.dev/github.com/cinar/indicator#WilliamsR) indicator function to provide a SELL action when the _wr_ is below -20, and a BUY action when _wr_ is above -80. - -```golang -actions := indicator.WilliamsRStrategy(asset) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. diff --git a/v2/pre-commit.sh b/pre-commit.sh similarity index 100% rename from v2/pre-commit.sh rename to pre-commit.sh diff --git a/regression.go b/regression.go deleted file mode 100644 index 1ebd050..0000000 --- a/regression.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -// Least square. -// -// y = mx + b -// b = y-intercept -// y = slope -// -// m = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX) -// b = (sumY - m * sumX) / n -func LeastSquare(x, y []float64) (float64, float64) { - checkSameSize(x, y) - - var sumX, sumX2, sumY, sumXY float64 - - for i := 0; i < len(x); i++ { - sumX += x[i] - sumX2 += x[i] * x[i] - sumY += y[i] - sumXY += x[i] * y[i] - } - - n := float64(len(x)) - m := ((n * sumXY) - (sumX * sumY)) / ((n * sumX2) - (sumX * sumX)) - b := (sumY - (m * sumX)) / n - - return m, b -} - -// Moving least square over a period. -// -// y = mx + b -// b = y-intercept -// y = slope -// -// m = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX) -// b = (sumY - m * sumX) / n -func MovingLeastSquare(period int, x, y []float64) ([]float64, []float64) { - checkSameSize(x, y) - - m := make([]float64, len(x)) - b := make([]float64, len(x)) - - var sumX, sumX2, sumY, sumXY float64 - - for i := 0; i < len(x); i++ { - sumX += x[i] - sumX2 += x[i] * x[i] - sumY += y[i] - sumXY += x[i] * y[i] - - n := float64(i + 1) - - if i >= period { - sumX -= x[i-period] - sumX2 -= x[i-period] * x[i-period] - sumY -= y[i-period] - sumXY -= x[i-period] * y[i-period] - n = float64(period) - } - - m[i] = ((n * sumXY) - (sumX * sumY)) / ((n * sumX2) - (sumX * sumX)) - b[i] = (sumY - (m[i] * sumX)) / n - } - - return m, b -} - -// Linear regression using least square method. -// -// y = mx + b -func LinearRegressionUsingLeastSquare(x, y []float64) []float64 { - m, b := LeastSquare(x, y) - - r := make([]float64, len(x)) - - for i := 0; i < len(r); i++ { - r[i] = (m * x[i]) + b - } - - return r -} - -// Moving linear regression using least square. -// -// y = mx + b -func MovingLinearRegressionUsingLeastSquare(period int, x, y []float64) []float64 { - m, b := MovingLeastSquare(period, x, y) - - r := make([]float64, len(x)) - - for i := 0; i < len(r); i++ { - r[i] = (m[i] * x[i]) + b[i] - } - - return r -} diff --git a/regression_test.go b/regression_test.go deleted file mode 100644 index bcde430..0000000 --- a/regression_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import ( - "testing" -) - -func TestLeastSquare(t *testing.T) { - x := []float64{2, 3, 5, 7, 9} - y := []float64{4, 5, 7, 10, 15} - - m, b := LeastSquare(x, y) - - expectedM := 1.5183 - expectedB := 0.3049 - - m = roundDigits(m, 4) - b = roundDigits(b, 4) - - if m != expectedM { - t.Fatalf("m %f expected %f", m, expectedM) - } - - if b != expectedB { - t.Fatalf("b %f expected %f", b, expectedB) - } -} - -func TestMovingLeastSquare(t *testing.T) { - x := []float64{1, 2, 3, 4, 2, 3, 5, 7, 9} - y := []float64{1, 2, 3, 4, 4, 5, 7, 10, 15} - - period := 5 - - m, b := MovingLeastSquare(period, x, y) - - expectedM := 1.5183 - expectedB := 0.3049 - - finalM := roundDigits(m[len(m)-1], 4) - finalB := roundDigits(b[len(b)-1], 4) - - if finalM != expectedM { - t.Fatalf("m %f expected %f", finalM, expectedM) - } - - if finalB != expectedB { - t.Fatalf("m %f expected %f", finalB, expectedB) - } -} - -func TestLinearRegressionUsingLeastSquare(t *testing.T) { - x := []float64{0, 2, 5, 7} - y := []float64{-1, 5, 12, 20} - - expected := []float64{-1.1379, 4.6552, 13.3448, 19.1379} - - actual := LinearRegressionUsingLeastSquare(x, y) - testEquals(t, roundDigitsAll(actual, 4), expected) -} diff --git a/v2/revive.toml b/revive.toml similarity index 100% rename from v2/revive.toml rename to revive.toml diff --git a/strategy.go b/strategy.go deleted file mode 100644 index 6c86977..0000000 --- a/strategy.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import ( - "time" -) - -// Strategy action. -type Action int - -const ( - SELL Action = -1 - HOLD Action = 0 - BUY Action = 1 -) - -// Asset values. -type Asset struct { - Date []time.Time - Opening []float64 - Closing []float64 - High []float64 - Low []float64 - Volume []float64 -} - -// Strategy function. It takes an Asset and returns -// actions for each row. -type StrategyFunction func(*Asset) []Action - -// Buy and hold strategy. Buys at the beginning and holds. -func BuyAndHoldStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - for i := 0; i < len(actions); i++ { - actions[i] = BUY - } - - return actions -} diff --git a/strategy.md b/strategy.md deleted file mode 100644 index 629aee0..0000000 --- a/strategy.md +++ /dev/null @@ -1,67 +0,0 @@ -### Strategies - -The strategies are where the results from one or more indicators gets combined to produce a recommended action. - -**The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security.** - -- [Asset](#asset) -- [Action](#action) -- [Strategy Function](#strategy-function) -- [Buy and Hold Strategy](#buy-and-hold-strategy) - - -#### Asset - -The stragies operates on an [Asset](https://pkg.go.dev/github.com/cinar/indicator#Asset) with the following members. - -```golang -type Asset struct { - Date []time.Time - Opening []float64 - Closing []float64 - High []float64 - Low []float64 - Volume []float64 -} -``` - -#### Strategy Function - -The [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction) takes an [Asset](https://pkg.go.dev/github.com/cinar/indicator#Asset), and provides an array of [Action](https://pkg.go.dev/github.com/cinar/indicator#Action) for each row. - -```golang -// Strategy function. -type StrategyFunction func(*Asset) []Action -``` - -#### Action - -The following [Action](https://pkg.go.dev/github.com/cinar/indicator#Action) values are currently provided. - -```golang -type Action int - -const ( - SELL Action = -1 - HOLD Action = 0 - BUY Action = 1 -) -``` - -#### Buy and Hold Strategy - -The [BuyAndHoldStrategy](https://pkg.go.dev/github.com/cinar/indicator#BuyAndHoldStrategy) provides a simple strategy to buy the given asset and hold it. It provides a good indicator for the change of asset's value without any other strategy is used. - -```golang -actions := indicator.BuyAndHoldStrategy(asset) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. diff --git a/v2/trend/README.md b/trend/README.md similarity index 89% rename from v2/trend/README.md rename to trend/README.md index 8b30b9f..ee1bde4 100644 --- a/v2/trend/README.md +++ b/trend/README.md @@ -3,7 +3,7 @@ # trend ```go -import "github.com/cinar/indicator/v2/trend" +import "github.com/cinar/indicator/trend" ``` Package trend contains the trend indicator functions. @@ -96,7 +96,7 @@ const ( ``` -## func [MovingMax]() +## func [MovingMax]() ```go func MovingMax[T helper.Number](c <-chan T, period int) <-chan T @@ -113,7 +113,7 @@ fmt.Println(helper.ChanToSlice(actual)) // [20, 20, 5, 8, 10, 10, 10] ``` -## func [MovingMin]() +## func [MovingMin]() ```go func MovingMin[T helper.Number](c <-chan T, period int) <-chan T @@ -130,7 +130,7 @@ fmt.Println(helper.ChanToSlice(actual)) // [-10, -5, -5, -5, 1, -20, -20] ``` -## func [MovingSum]() +## func [MovingSum]() ```go func MovingSum[T helper.Number](c <-chan T, period int) <-chan T @@ -147,7 +147,7 @@ fmt.Println(helper.ChanToSlice(actual)) // [1, 12, -3, 9, 24, 3, 2] ``` -## type [Apo]() +## type [Apo]() Apo represents the configuration parameters for calculating the Absolute Price Oscillator \(APO\). An APO value crossing above zero suggests a bullish trend, while crossing below zero indicates a bearish trend. Positive APO values signify an upward trend, while negative values signify a downward trend. @@ -184,7 +184,7 @@ type Apo[T helper.Number] struct { ``` -### func [NewApo]() +### func [NewApo]() ```go func NewApo[T helper.Number]() *Apo[T] @@ -193,7 +193,7 @@ func NewApo[T helper.Number]() *Apo[T] NewApo function initializes a new APO instance with the default parameters. -### func \(\*Apo\[T\]\) [Compute]() +### func \(\*Apo\[T\]\) [Compute]() ```go func (apo *Apo[T]) Compute(c <-chan T) <-chan T @@ -202,7 +202,7 @@ func (apo *Apo[T]) Compute(c <-chan T) <-chan T Compute function takes a channel of numbers and computes the APO over the specified period. -## type [Aroon]() +## type [Aroon]() Aroon represent the configuration for calculating the Aroon indicator. It is a technical analysis tool that gauges trend direction and strength in asset prices. It comprises two lines: Aroon Up and Aroon Down. Aroon Up measures uptrend strength, while Aroon Down measures downtrend strength. When Aroon Up exceeds Aroon Down, it suggests a bullish trend; when Aroon Down surpasses Aroon Up, it indicates a bearish trend. @@ -228,7 +228,7 @@ type Aroon[T helper.Number] struct { ``` -### func [NewAroon]() +### func [NewAroon]() ```go func NewAroon[T helper.Number]() *Aroon[T] @@ -237,7 +237,7 @@ func NewAroon[T helper.Number]() *Aroon[T] NewAroon function initializes a new Aroon instance with the default parameters. -### func \(\*Aroon\[T\]\) [Compute]() +### func \(\*Aroon\[T\]\) [Compute]() ```go func (a *Aroon[T]) Compute(high, low <-chan T) (<-chan T, <-chan T) @@ -246,7 +246,7 @@ func (a *Aroon[T]) Compute(high, low <-chan T) (<-chan T, <-chan T) Compute function takes a channel of numbers and computes the Aroon over the specified period. -## type [Bop]() +## type [Bop]() Bop gauges the strength of buying and selling forces using the Balance of Power \(BoP\) indicator. A positive BoP value suggests an upward trend, while a negative value indicates a downward trend. A BoP value of zero implies equilibrium between the two forces. @@ -260,7 +260,7 @@ type Bop[T helper.Number] struct { ``` -### func [NewBop]() +### func [NewBop]() ```go func NewBop[T helper.Number]() *Bop[T] @@ -269,7 +269,7 @@ func NewBop[T helper.Number]() *Bop[T] NewBop function initializes a new BOP instance with the default parameters. -### func \(\*Bop\[T\]\) [Compute]() +### func \(\*Bop\[T\]\) [Compute]() ```go func (b *Bop[T]) Compute(opening, high, low, closing <-chan T) <-chan T @@ -278,7 +278,7 @@ func (b *Bop[T]) Compute(opening, high, low, closing <-chan T) <-chan T Compute processes a channel of open, high, low, and close values, computing the BOP for each entry. -## type [Ema]() +## type [Ema]() Ema represents the parameters for calculating the Exponential Moving Average. @@ -302,7 +302,7 @@ type Ema[T helper.Number] struct { ``` -### func [NewEma]() +### func [NewEma]() ```go func NewEma[T helper.Number]() *Ema[T] @@ -311,7 +311,7 @@ func NewEma[T helper.Number]() *Ema[T] NewEma function initializes a new EMA instance with the default parameters. -### func \(\*Ema\[T\]\) [Compute]() +### func \(\*Ema\[T\]\) [Compute]() ```go func (ema *Ema[T]) Compute(c <-chan T) <-chan T @@ -320,7 +320,7 @@ func (ema *Ema[T]) Compute(c <-chan T) <-chan T Compute function takes a channel of numbers and computes the EMA over the specified period. -## type [Sma]() +## type [Sma]() Sma represents the parameters for calculating the Simple Moving Average. @@ -341,7 +341,7 @@ type Sma[T helper.Number] struct { ``` -### func [NewSma]() +### func [NewSma]() ```go func NewSma[T helper.Number]() *Sma[T] @@ -350,7 +350,7 @@ func NewSma[T helper.Number]() *Sma[T] NewSma function initializes a new SMA instance with the default parameters. -### func \(\*Sma\[T\]\) [Compute]() +### func \(\*Sma\[T\]\) [Compute]() ```go func (sma *Sma[T]) Compute(c <-chan T) <-chan T diff --git a/v2/trend/apo.go b/trend/apo.go similarity index 97% rename from v2/trend/apo.go rename to trend/apo.go index 4519c98..fbf4cc8 100644 --- a/v2/trend/apo.go +++ b/trend/apo.go @@ -4,7 +4,7 @@ package trend -import "github.com/cinar/indicator/v2/helper" +import "github.com/cinar/indicator/helper" const ( // DefaultApoFastPeriod is the default APO fast period of 14. diff --git a/v2/trend/apo_test.go b/trend/apo_test.go similarity index 91% rename from v2/trend/apo_test.go rename to trend/apo_test.go index 5aff868..1e79389 100644 --- a/v2/trend/apo_test.go +++ b/trend/apo_test.go @@ -8,8 +8,8 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" - "github.com/cinar/indicator/v2/trend" + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/trend" ) func TestApo(t *testing.T) { diff --git a/v2/trend/aroon.go b/trend/aroon.go similarity index 97% rename from v2/trend/aroon.go rename to trend/aroon.go index 1d7cd55..d8eb2c7 100644 --- a/v2/trend/aroon.go +++ b/trend/aroon.go @@ -4,7 +4,7 @@ package trend -import "github.com/cinar/indicator/v2/helper" +import "github.com/cinar/indicator/helper" const ( // DefaultAroonPeriod is the default Aroon period of 25. diff --git a/v2/trend/aroon_test.go b/trend/aroon_test.go similarity index 93% rename from v2/trend/aroon_test.go rename to trend/aroon_test.go index 5debc48..95c1701 100644 --- a/v2/trend/aroon_test.go +++ b/trend/aroon_test.go @@ -7,8 +7,8 @@ package trend_test import ( "testing" - "github.com/cinar/indicator/v2/helper" - "github.com/cinar/indicator/v2/trend" + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/trend" ) func TestAroon(t *testing.T) { diff --git a/v2/trend/bop.go b/trend/bop.go similarity index 95% rename from v2/trend/bop.go rename to trend/bop.go index dc0e2aa..90d1fdd 100644 --- a/v2/trend/bop.go +++ b/trend/bop.go @@ -4,7 +4,7 @@ package trend -import "github.com/cinar/indicator/v2/helper" +import "github.com/cinar/indicator/helper" // Bop gauges the strength of buying and selling forces using // the Balance of Power (BoP) indicator. A positive BoP value diff --git a/v2/trend/bop_test.go b/trend/bop_test.go similarity index 93% rename from v2/trend/bop_test.go rename to trend/bop_test.go index aa55aee..0b6e93a 100644 --- a/v2/trend/bop_test.go +++ b/trend/bop_test.go @@ -7,8 +7,8 @@ package trend_test import ( "testing" - "github.com/cinar/indicator/v2/helper" - "github.com/cinar/indicator/v2/trend" + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/trend" ) func TestBop(t *testing.T) { diff --git a/v2/trend/ema.go b/trend/ema.go similarity index 96% rename from v2/trend/ema.go rename to trend/ema.go index ff1f43a..b58c107 100644 --- a/v2/trend/ema.go +++ b/trend/ema.go @@ -4,7 +4,7 @@ package trend -import "github.com/cinar/indicator/v2/helper" +import "github.com/cinar/indicator/helper" const ( // DefaultEmaPeriod is the default EMA period of 20. diff --git a/v2/trend/ema_test.go b/trend/ema_test.go similarity index 92% rename from v2/trend/ema_test.go rename to trend/ema_test.go index 231a4eb..6b8354c 100644 --- a/v2/trend/ema_test.go +++ b/trend/ema_test.go @@ -8,8 +8,8 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" - "github.com/cinar/indicator/v2/trend" + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/trend" ) // Testing against the example at URL: diff --git a/v2/trend/moving_max.go b/trend/moving_max.go similarity index 94% rename from v2/trend/moving_max.go rename to trend/moving_max.go index 6298b0a..0608b71 100644 --- a/v2/trend/moving_max.go +++ b/trend/moving_max.go @@ -4,7 +4,7 @@ package trend -import "github.com/cinar/indicator/v2/helper" +import "github.com/cinar/indicator/helper" // MovingMax function takes a channel of numbers and computes the // moving maximum over the specified period. diff --git a/v2/trend/moving_max_test.go b/trend/moving_max_test.go similarity index 86% rename from v2/trend/moving_max_test.go rename to trend/moving_max_test.go index 69aecb8..b30976b 100644 --- a/v2/trend/moving_max_test.go +++ b/trend/moving_max_test.go @@ -8,8 +8,8 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" - "github.com/cinar/indicator/v2/trend" + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/trend" ) func TestMovingMax(t *testing.T) { diff --git a/v2/trend/moving_min.go b/trend/moving_min.go similarity index 94% rename from v2/trend/moving_min.go rename to trend/moving_min.go index 01b9bae..64ba386 100644 --- a/v2/trend/moving_min.go +++ b/trend/moving_min.go @@ -4,7 +4,7 @@ package trend -import "github.com/cinar/indicator/v2/helper" +import "github.com/cinar/indicator/helper" // MovingMin function takes a channel of numbers and computes the // moving minimum over the specified period. diff --git a/v2/trend/moving_min_test.go b/trend/moving_min_test.go similarity index 86% rename from v2/trend/moving_min_test.go rename to trend/moving_min_test.go index 0cc4c56..fe7e183 100644 --- a/v2/trend/moving_min_test.go +++ b/trend/moving_min_test.go @@ -8,8 +8,8 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" - "github.com/cinar/indicator/v2/trend" + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/trend" ) func TestMovingMin(t *testing.T) { diff --git a/v2/trend/moving_sum.go b/trend/moving_sum.go similarity index 94% rename from v2/trend/moving_sum.go rename to trend/moving_sum.go index 1bb1b9d..507ba18 100644 --- a/v2/trend/moving_sum.go +++ b/trend/moving_sum.go @@ -4,7 +4,7 @@ package trend -import "github.com/cinar/indicator/v2/helper" +import "github.com/cinar/indicator/helper" // MovingSum function takes a channel of numbers and computes the // moving sum over the specified period. diff --git a/v2/trend/moving_sum_test.go b/trend/moving_sum_test.go similarity index 86% rename from v2/trend/moving_sum_test.go rename to trend/moving_sum_test.go index f9e7089..b5a6523 100644 --- a/v2/trend/moving_sum_test.go +++ b/trend/moving_sum_test.go @@ -8,8 +8,8 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" - "github.com/cinar/indicator/v2/trend" + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/trend" ) func TestMovingSum(t *testing.T) { diff --git a/v2/trend/sma.go b/trend/sma.go similarity index 95% rename from v2/trend/sma.go rename to trend/sma.go index 6bf74d2..d98e230 100644 --- a/v2/trend/sma.go +++ b/trend/sma.go @@ -4,7 +4,7 @@ package trend -import "github.com/cinar/indicator/v2/helper" +import "github.com/cinar/indicator/helper" const ( // DefaultSmaPeriod is the default SMA period. diff --git a/v2/trend/sma_test.go b/trend/sma_test.go similarity index 92% rename from v2/trend/sma_test.go rename to trend/sma_test.go index a80e2fd..b70c3e0 100644 --- a/v2/trend/sma_test.go +++ b/trend/sma_test.go @@ -8,8 +8,8 @@ import ( "reflect" "testing" - "github.com/cinar/indicator/v2/helper" - "github.com/cinar/indicator/v2/trend" + "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/trend" ) // Testing against the example at URL: diff --git a/v2/trend/testdata/aroon.csv b/trend/testdata/aroon.csv similarity index 100% rename from v2/trend/testdata/aroon.csv rename to trend/testdata/aroon.csv diff --git a/v2/trend/testdata/bop.csv b/trend/testdata/bop.csv similarity index 100% rename from v2/trend/testdata/bop.csv rename to trend/testdata/bop.csv diff --git a/v2/trend/testdata/brk-b.csv b/trend/testdata/brk-b.csv similarity index 100% rename from v2/trend/testdata/brk-b.csv rename to trend/testdata/brk-b.csv diff --git a/v2/trend/trend.go b/trend/trend.go similarity index 100% rename from v2/trend/trend.go rename to trend/trend.go diff --git a/trend_indicators.go b/trend_indicators.go deleted file mode 100644 index 96f9cfc..0000000 --- a/trend_indicators.go +++ /dev/null @@ -1,647 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import ( - "math" - - "github.com/cinar/indicator/container/bst" -) - -// Trend indicator. -type Trend int - -const ( - Falling Trend = -1 - Rising Trend = 1 -) - - -// The AbsolutePriceOscillator function calculates a technical indicator that is used -// to follow trends. APO crossing above zero indicates bullish, while crossing below -// zero indicates bearish. Positive value is upward trend, while negative value is -// downward trend. -// -// Fast = Ema(fastPeriod, values) -// Slow = Ema(slowPeriod, values) -// APO = Fast - Slow -// -// Returns apo. -func AbsolutePriceOscillator(fastPeriod, slowPeriod int, values []float64) []float64 { - fast := Ema(fastPeriod, values) - slow := Ema(slowPeriod, values) - apo := subtract(fast, slow) - - return apo -} - -// The DefaultAbsolutePriceOscillator function calculates APO with the most -// frequently used fast and short periods are 14 and 30. -// -// Returns apo. -func DefaultAbsolutePriceOscillator(values []float64) []float64 { - return AbsolutePriceOscillator(14, 30, values) -} - -// Aroon Indicator. It is a technical indicator that is used to identify trend changes -// in the price of a stock, as well as the strength of that trend. It consists of two -// lines, Arron Up, and Aroon Down. The Aroon Up line measures the strength of the -// uptrend, and the Aroon Down measures the strength of the downtrend. When Aroon Up -// is above Aroon Down, it indicates bullish price, and when Aroon Down is above -// Aroon Up, it indicates bearish price. -// -// Aroon Up = ((25 - Period Since Last 25 Period High) / 25) * 100 -// Aroon Down = ((25 - Period Since Last 25 Period Low) / 25) * 100 -// -// Returns aroonUp, aroonDown -func Aroon(high, low []float64) ([]float64, []float64) { - checkSameSize(high, low) - - sinceLastHigh25 := Since(Max(25, high)) - sinceLastLow25 := Since(Min(25, low)) - - aroonUp := make([]float64, len(high)) - aroonDown := make([]float64, len(high)) - - for i := 0; i < len(aroonUp); i++ { - aroonUp[i] = (float64(25-sinceLastHigh25[i]) / 25) * 100 - aroonDown[i] = (float64(25-sinceLastLow25[i]) / 25) * 100 - } - - return aroonUp, aroonDown -} - -// The BalanceOfPower function calculates the strength of buying and selling -// pressure. Positive value indicates an upward trend, and negative value -// indicates a downward trend. Zero indicates a balance between the two. -// -// BOP = (Closing - Opening) / (High - Low) -// -// Returns bop. -func BalanceOfPower(opening, high, low, closing []float64) []float64 { - bop := divide(subtract(closing, opening), subtract(high, low)) - return bop -} - -// The Chande Forecast Oscillator developed by Tushar Chande The Forecast -// Oscillator plots the percentage difference between the closing price and -// the n-period linear regression forecasted price. The oscillator is above -// zero when the forecast price is greater than the closing price and less -// than zero if it is below. -// -// R = Linreg(Closing) -// CFO = ((Closing - R) / Closing) * 100 -// -// Returns cfo. -func ChandeForecastOscillator(closing []float64) []float64 { - x := generateNumbers(0, float64(len(closing)), 1) - r := LinearRegressionUsingLeastSquare(x, closing) - - cfo := multiplyBy(divide(subtract(closing, r), closing), 100) - - return cfo -} - -// The Community Channel Index (CMI) is a momentum-based oscillator -// used to help determine when an investment vehicle is reaching a -// condition of being overbought or oversold. -// -// Moving Average = Sma(Period, Typical Price) -// Mean Deviation = Sma(Period, Abs(Typical Price - Moving Average)) -// CMI = (Typical Price - Moving Average) / (0.015 * Mean Deviation) -// -// Returns cmi. -func CommunityChannelIndex(period int, high, low, closing []float64) []float64 { - tp, _ := TypicalPrice(low, high, closing) - ma := Sma(period, tp) - md := Sma(period, abs(subtract(tp, ma))) - cci := divide(subtract(tp, ma), multiplyBy(md, 0.015)) - cci[0] = 0 - - return cci -} - -// The default community channel index with the period of 20. -func DefaultCommunityChannelIndex(high, low, closing []float64) []float64 { - return CommunityChannelIndex(20, high, low, closing) -} - -// Dema calculates the Double Exponential Moving Average (DEMA). -// -// DEMA = (2 * EMA(values)) - EMA(EMA(values)) -// -// Returns dema. -func Dema(period int, values []float64) []float64 { - ema1 := Ema(period, values) - ema2 := Ema(period, ema1) - - dema := subtract(multiplyBy(ema1, 2), ema2) - - return dema -} - -// Exponential Moving Average (EMA). -func Ema(period int, values []float64) []float64 { - result := make([]float64, len(values)) - - k := float64(2) / float64(1+period) - - for i, value := range values { - if i > 0 { - result[i] = (value * k) + (result[i-1] * float64(1-k)) - } else { - result[i] = value - } - } - - return result -} - -// Moving Average Convergence Divergence (MACD). -// -// MACD = 12-Period EMA - 26-Period EMA. -// Signal = 9-Period EMA of MACD. -// -// Returns MACD, signal. -func Macd(closing []float64) ([]float64, []float64) { - ema12 := Ema(12, closing) - ema26 := Ema(26, closing) - macd := subtract(ema12, ema26) - signal := Ema(9, macd) - - return macd, signal -} - -// The Mass Index (MI) uses the high-low range to identify trend reversals -// based on range expansions. -// -// Singe EMA = EMA(9, Highs - Lows) -// Double EMA = EMA(9, Single EMA) -// Ratio = Single EMA / Double EMA -// MI = Sum(25, Ratio) -// -// Returns mi. -func MassIndex(high, low []float64) []float64 { - ema1 := Ema(9, subtract(high, low)) - ema2 := Ema(9, ema1) - ratio := divide(ema1, ema2) - mi := Sum(25, ratio) - - return mi -} - -// Moving Chande Forecast Oscillator calculates based on -// the given period. -// -// The Chande Forecast Oscillator developed by Tushar Chande The Forecast -// Oscillator plots the percentage difference between the closing price and -// the n-period linear regression forecasted price. The oscillator is above -// zero when the forecast price is greater than the closing price and less -// than zero if it is below. -// -// R = Linreg(Closing) -// CFO = ((Closing - R) / Closing) * 100 -// -// Returns cfo. -func MovingChandeForecastOscillator(period int, closing []float64) []float64 { - x := generateNumbers(0, float64(len(closing)), 1) - r := MovingLinearRegressionUsingLeastSquare(period, x, closing) - - cfo := multiplyBy(divide(subtract(closing, r), closing), 100) - - return cfo -} - -// Moving max for the given period. -func Max(period int, values []float64) []float64 { - result := make([]float64, len(values)) - - buffer := make([]float64, period) - bst := bst.New() - - for i := 0; i < len(values); i++ { - bst.Insert(values[i]) - - if i >= period { - bst.Remove(buffer[i%period]) - } - - buffer[i%period] = values[i] - result[i] = bst.Max().(float64) - } - - return result -} - -// Moving min for the given period. -func Min(period int, values []float64) []float64 { - result := make([]float64, len(values)) - - buffer := make([]float64, period) - bst := bst.New() - - for i := 0; i < len(values); i++ { - bst.Insert(values[i]) - - if i >= period { - bst.Remove(buffer[i%period]) - } - - buffer[i%period] = values[i] - result[i] = bst.Min().(float64) - } - - return result -} - -// 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 psarAfMax, increment it by psarAfStep. -// If the trend is not the same, set AF to psarAfStep. -// -// Based on video https://www.youtube.com/watch?v=MuEpGBAH7pw&t=0s. -// -// Returns psar, trend -func ParabolicSar(high, low, closing []float64, psarAfStep, psarAfMax float64) ([]float64, []Trend) { - checkSameSize(high, low) - - trend := make([]Trend, len(high)) - psar := make([]float64, len(high)) - - var af, ep float64 - - trend[0] = Falling - psar[0] = high[0] - af = psarAfStep - ep = low[0] - - for i := 1; i < len(psar); i++ { - psar[i] = psar[i-1] - ((psar[i-1] - ep) * af) - - if trend[i-1] == Falling { - psar[i] = math.Max(psar[i], high[i-1]) - if i > 1 { - psar[i] = math.Max(psar[i], high[i-2]) - } - - if high[i] >= psar[i] { - psar[i] = ep - } - } else { - psar[i] = math.Min(psar[i], low[i-1]) - if i > 1 { - psar[i] = math.Min(psar[i], low[i-2]) - } - - if low[i] <= psar[i] { - psar[i] = ep - } - } - - prevEp := ep - - if psar[i] > closing[i] { - trend[i] = Falling - ep = math.Min(ep, low[i]) - } else { - trend[i] = Rising - ep = math.Max(ep, high[i]) - } - - if trend[i] != trend[i-1] { - af = psarAfStep - } else if prevEp != ep && af < psarAfMax { - af += psarAfStep - } - } - - return psar, trend -} - -// DefaultParabolicSar function calculates the Parabolic SAR using a psarAfStep -// of 0.02, and psarAfMax of 0.20. -// -// Returns psar, trend -func DefaultParabolicSar(high, low, closing []float64) ([]float64, []Trend) { - return ParabolicSar(high, low, closing, 0.02, 0.20) -} - -// The Qstick function calculates the ratio of recent up and down bars. -// -// QS = Sma(Closing - Opening) -// -// Returns qs. -func Qstick(period int, opening, closing []float64) []float64 { - qs := Sma(period, subtract(closing, opening)) - return qs -} - -// 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) -// -// Returns k, d, j. -func Kdj(rPeriod, kPeriod, dPeriod int, high, low, closing []float64) ([]float64, []float64, []float64) { - highest := Max(rPeriod, high) - lowest := Min(rPeriod, low) - - rsv := multiplyBy(divide(subtract(closing, lowest), subtract(highest, lowest)), 100) - - k := Sma(kPeriod, rsv) - d := Sma(dPeriod, k) - j := subtract(multiplyBy(k, 3), multiplyBy(d, 2)) - - return k, d, j -} - -// The DefaultKdj function calculates KDJ based on default periods -// consisting of rPeriod of 9, kPeriod of 3, and dPeriod of 3. -// -// Returns k, d, j. -func DefaultKdj(high, low, closing []float64) ([]float64, []float64, []float64) { - return Kdj(9, 3, 3, high, low, closing) -} - -// Rolling Moving Average (RMA). -// -// R[0] to R[p-1] is SMA(values) -// R[p] and after is R[i] = ((R[i-1]*(p-1)) + v[i]) / p -// -// Returns r. -func Rma(period int, values []float64) []float64 { - result := make([]float64, len(values)) - sum := float64(0) - - for i, value := range values { - count := i + 1 - - if i < period { - sum += value - } else { - sum = (result[i-1] * float64(period-1)) + value - count = period - } - - result[i] = sum / float64(count) - } - - return result -} - -// Simple Moving Average (SMA). -func Sma(period int, values []float64) []float64 { - result := make([]float64, len(values)) - sum := float64(0) - - for i, value := range values { - count := i + 1 - sum += value - - if i >= period { - sum -= values[i-period] - count = period - } - - result[i] = sum / float64(count) - } - - return result -} - -// Since last values change. -func Since(values []float64) []int { - result := make([]int, len(values)) - - lastValue := math.NaN() - sinceLast := 0 - - for i := 0; i < len(values); i++ { - value := values[i] - - if value != lastValue { - lastValue = value - sinceLast = 0 - } else { - sinceLast++ - } - - result[i] = sinceLast - } - - return result -} - -// Moving sum for the given period. -func Sum(period int, values []float64) []float64 { - result := make([]float64, len(values)) - sum := 0.0 - - for i := 0; i < len(values); i++ { - sum += values[i] - - if i >= period { - sum -= values[i-period] - } - - result[i] = sum - } - - return result -} - -// Tema calculates the Triple Exponential Moving Average (TEMA). -// -// TEMA = (3 * EMA1) - (3 * EMA2) + EMA3 -// EMA1 = EMA(values) -// EMA2 = EMA(EMA1) -// EMA3 = EMA(EMA2) -// -// Returns tema. -func Tema(period int, values []float64) []float64 { - ema1 := Ema(period, values) - ema2 := Ema(period, ema1) - ema3 := Ema(period, ema2) - - tema := add(subtract(multiplyBy(ema1, 3), multiplyBy(ema2, 3)), ema3) - - return tema -} - -// 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(period int, values []float64) []float64 { - var n1, n2 int - - if period%2 == 0 { - n1 = period / 2 - n2 = n1 + 1 - } else { - n1 = (period + 1) / 2 - n2 = n1 - } - - trima := Sma(n1, Sma(n2, values)) - - return trima -} - -// Triple Exponential Average (TRIX) indicator is an oscillator used to -// identify oversold and overbought markets, and it can also be used -// as a momentum indicator. Like many oscillators, TRIX oscillates -// around a zero line. -// -// EMA1 = EMA(period, values) -// EMA2 = EMA(period, EMA1) -// EMA3 = EMA(period, EMA2) -// TRIX = (EMA3 - Previous EMA3) / Previous EMA3 -// -// Returns trix. -func Trix(period int, values []float64) []float64 { - ema1 := Ema(period, values) - ema2 := Ema(period, ema1) - ema3 := Ema(period, ema2) - previous := shiftRightAndFillBy(1, ema3[0], ema3) - trix := divide(subtract(ema3, previous), previous) - - return trix -} - -// Typical Price. It is another approximation of average price for each -// period and can be used as a filter for moving average systems. -// -// Typical Price = (High + Low + Closing) / 3 -// -// Returns typical price, 20-Period SMA. -func TypicalPrice(low, high, closing []float64) ([]float64, []float64) { - checkSameSize(high, low, closing) - - sma20 := Sma(20, closing) - - ta := make([]float64, len(closing)) - for i := 0; i < len(ta); i++ { - ta[i] = (high[i] + low[i] + closing[i]) / float64(3) - } - - return ta, sma20 -} - -// 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 -// -// Returns plusVi, minusVi -func Vortex(high, low, closing []float64) ([]float64, []float64) { - checkSameSize(high, low, closing) - - period := 14 - - plusVi := make([]float64, len(high)) - minusVi := make([]float64, len(high)) - - plusVm := make([]float64, period) - minusVm := make([]float64, period) - tr := make([]float64, period) - - var plusVmSum, minusVmSum, trSum float64 - - for i := 1; i < len(high); i++ { - j := i % period - - plusVmSum -= plusVm[j] - plusVm[j] = math.Abs(high[i] - low[i-1]) - plusVmSum += plusVm[j] - - minusVmSum -= minusVm[j] - minusVm[j] = math.Abs(low[i] - high[i-1]) - minusVmSum += minusVm[j] - - highLow := high[i] - low[i] - highPrevClosing := math.Abs(high[i] - closing[i-1]) - lowPrevClosing := math.Abs(low[i] - closing[i-1]) - - trSum -= tr[j] - tr[j] = math.Max(highLow, math.Max(highPrevClosing, lowPrevClosing)) - trSum += tr[j] - - plusVi[i] = plusVmSum / trSum - minusVi[i] = minusVmSum / trSum - } - - return plusVi, minusVi -} - -// 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. -// -// Returns vwma -func Vwma(period int, closing, volume []float64) []float64 { - vwma := divide(Sum(period, multiply(closing, volume)), Sum(period, volume)) - - return vwma -} - -// The DefaultVwma function calculates VWMA with a period of 20. -func DefaultVwma(closing, volume []float64) []float64 { - return Vwma(20, closing, volume) -} diff --git a/trend_indicators.md b/trend_indicators.md deleted file mode 100644 index 79a1c9a..0000000 --- a/trend_indicators.md +++ /dev/null @@ -1,390 +0,0 @@ -### Trend Indicators - -Trend indicators measure the direction and strength of a trend. - -- [Absolute Price Oscillator (APO)](#absolute-price-oscillator-apo) -- [Aroon Indicator](#aroon-indicator) -- [Balance of Power (BOP)](trend_indicators.md#balance-of-power-bop) -- [Chande Forecast Oscillator (CFO)](#chande-forecast-oscillator-cfo) -- [Community Channel Index (CMI)](#community-channel-index-cmi) -- [Double Exponential Moving Average (DEMA)](#double-exponential-moving-average-dema) -- [Exponential Moving Average (EMA)](#exponential-moving-average-ema) -- [Mass Index (MI)](#mass-index-mi) -- [Moving Average Convergence Divergence (MACD)](#moving-average-convergence-divergence-macd) -- [Moving Max](#moving-max) -- [Moving Min](#moving-min) -- [Moving Sum](#moving-sum) -- [Parabolic SAR](#parabolic-sar) -- [Qstick](trend_indicator.md#qstick) -- [Random Index (KDJ)](#random-index-kdj) -- [Rolling Moving Average (RMA)](#rolling-moving-average-rma) -- [Simple Moving Average (SMA)](#simple-moving-average-sma) -- [Since Change](#since-change) -- [Triple Exponential Moving Average (TEMA)](#triple-exponential-moving-average-tema) -- [Triangular Moving Average (TRIMA)](#triangular-moving-average-trima) -- [Triple Exponential Average (TRIX)](#triple-exponential-average-trix) -- [Typical Price](#typical-price) -- [Volume Weighted Moving Average (VWMA)](#volume-weighted-moving-average-vwma) -- [Vortex Indicator](#vortex-indicator) - -#### Absolute Price Oscillator (APO) - -The [AbsolutePriceOscillator](https://pkg.go.dev/github.com/cinar/indicator#AbsolutePriceOscillator) function calculates a technical indicator that is used to follow trends. APO crossing above zero indicates bullish, while crossing below zero indicates bearish. Positive value is upward trend, while negative value is downward trend. - -``` -Fast = Ema(fastPeriod, values) -Slow = Ema(slowPeriod, values) -APO = Fast - Slow -``` - -```Golang -apo := indicator.AbsolutePriceOscillator(fastPeriod, slowPeriod, values) -``` - -Most frequently used fast and short periods are 14 and 30. The [DefaultAbsoluePriceOscillator](https://pkg.go.dev/github.com/cinar/indicator#DefaultAbsolutePriceOscillator) function calculates APO with those periods. - -#### Aroon Indicator - -The [Aroon](https://pkg.go.dev/github.com/cinar/indicator#Aroon) function calculates a technical indicator that is used to identify trend changes in the price of a stock, as well as the strength of that trend. It consists of two lines, Aroon Up, and Aroon Down. The Aroon Up line measures measures the strength of the uptrend, and the Aroon Down measures the strength of the downtrend. When Aroon Up is above Aroon Down, it indicates bullish price, and when Aroon Down is above Aroon Up, it indicates bearish price. - -``` -Aroon Up = ((25 - Period Since Last 25 Period High) / 25) * 100 -Aroon Down = ((25 - Period Since Last 25 Period Low) / 25) * 100 -``` - -```Golang -aroonUp, aroonDown := indicator.Aroon(high, low) -``` - -#### Balance of Power (BOP) - -The [BalanceOfPower](https://pkg.go.dev/github.com/cinar/indicator#BalanceOfPower) function calculates the strength of buying and selling pressure. Positive value indicates an upward trend, and negative value indicates a downward trend. Zero indicates a balance between the two. - -``` -BOP = (Closing - Opening) / (High - Low) -``` - -```Golang -bop := indicator.BalanceOfPower(opening, high, low, closing) -``` - -#### Chande Forecast Oscillator (CFO) - -The [ChandeForecastOscillator](https://pkg.go.dev/github.com/cinar/indicator#ChandeForecastOscillator) developed by Tushar Chande The Forecast Oscillator plots the percentage difference between the closing price and the n-period linear regression forecasted price. The oscillator is above zero when the forecast price is greater than the closing price and less than zero if it is below. - -``` -R = Linreg(Closing) -CFO = ((Closing - R) / Closing) * 100 -``` - -Based on [Chande Forecast Oscillator Formula, Strategy](https://www.stockmaniacs.net/chande-forecast-oscillator/), [Forecast Oscillator -](https://www.fmlabs.com/reference/default.htm?url=ForecastOscillator.htm), and [Least Squares Regression](https://www.mathsisfun.com/data/least-squares-regression.html). - -```golang -cfo := indicator.ChandeForecastOscillator(closing) -``` -#### Community Channel Index (CMI) - -The [CommunityChannelIndex](https://pkg.go.dev/github.com/cinar/indicator#CommunityChannelIndex) is a momentum-based oscillator used to help determine when an investment vehicle is reaching a condition of being overbought or oversold. - -``` -Moving Average = Sma(Period, Typical Price) -Mean Deviation = Sma(Period, Abs(Typical Price - Moving Average)) -CMI = (Typical Price - Moving Average) / (0.015 * Mean Deviation) -``` - -```golang -result := indicator.CommunityChannelIndex(period, high, low, closing) -``` - -The [DefaultCommunityChannelIndex](https://pkg.go.dev/github.com/cinar/indicator#DefaultCommunityChannelIndex) calculates with the period of 20. - -```golang -result := indicator.DefaultCommunityChannelIndex(high, low, closing) -``` - -#### 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)) -``` - -```Golang -dema := indicator.Dema(period, values) -``` - -Based on [Double Exponential Moving Average (DEMA)](https://www.investopedia.com/terms/d/double-exponential-moving-average.asp). - -#### Exponential Moving Average (EMA) - -The [Ema](https://pkg.go.dev/github.com/cinar/indicator#Ema) function calculates the exponential moving average for a given period. - -```Golang -result := indicator.Ema(2, []float64{2, 4, 6, 8, 12, 14, 16, 18, 20}) -``` - -#### Mass Index (MI) - -The [MassIndex](https://pkg.go.dev/github.com/cinar/indicator#MassIndex) uses the high-low range to identify trend reversals based on range expansions. - -``` -Singe EMA = EMA(9, Highs - Lows) -Double EMA = EMA(9, Single EMA) -Ratio = Single EMA / Double EMA -MI = Sum(25, Ratio) -``` - -```Golang -result := indicator.MassIndex(high, low) -``` - -#### Moving Average Convergence Divergence (MACD) - -The [Macd](https://pkg.go.dev/github.com/cinar/indicator#Macd) function calculates a trend-following momentum indicator that shows the relationship between two moving averages of price. - -``` -MACD = 12-Period EMA - 26-Period EMA. -Signal = 9-Period EMA of MACD. -``` - -```Golang -macd, signal := indicator.Macd(closing) -``` - -#### Moving Max - -The [Max](https://pkg.go.dev/github.com/cinar/indicator#Max) function gives the maximum value within the given moving period. It can be used to get the moving maximum closing price and other values. - -```Golang -max := indicator.Max(period, values) -``` - -#### Moving Min - -The [Min](https://pkg.go.dev/github.com/cinar/indicator#Min) function gives the minimum value within the given moving period. It can be used to get the moving minimum closing price and other values. - -```Golang -max := indicator.Min(period, values) -``` - -#### Moving Sum - -The [Sum](https://pkg.go.dev/github.com/cinar/indicator#Sum) function gives the sum value within the given moving period. - -```Golang -sum := indicator.Sum(period, values) -``` - -#### Parabolic SAR - -The [ParabolicSar](https://pkg.go.dev/github.com/cinar/indicator#ParabolicSar) function calculates an identifier for the trend and the 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 equials to PSAR, use EP. - -If PSAR is greather 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 psarAfMax, increment it by psarAfStep. If the trend is not the same, set AF to psarAfStep. - -Based on video [How to Calculate the PSAR Using Excel - Revised Version](https://www.youtube.com/watch?v=MuEpGBAH7pw&t=0s). - -```Golang -psar, trend := indicator.ParabolicSar(high, low, closing, 0.02, 0.20) -``` - -The `DefaultParabolicSar` function calculates the Parabolic SAR using a psarAfStep of 0.02, and psarAfMax of 0.20. - -```Golang -psar, trend := indicator.DefaultParabolicSar(high, low, closing) -``` - -#### Qstick - -The [Qstick](https://pkg.go.dev/github.com/cinar/indicator#Qstick) function calculates the ratio of recent up and down bars. - -``` -QS = Sma(Closing - Opening) -``` - -```Golang -qs := indicator.Qstick(period, closing, opening) -``` - -#### Random Index (KDJ) - -The [Kdj](https://pkg.go.dev/github.com/cinar/indicator#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) -``` - -```Golang -k, d, j := indicator.Kdj(rPeriod, kPeriod, dPeriod, high, low, closing) -``` - -By default, _rPeriod_ of 9, _kPeriod_ of 3, and _dPeriod_ of 3 are used. The [DefaultKdj](https://pkg.go.dev/github.com/cinar/indicator#DefaultKdj) function can be used with those periods. - -```Golang -k, d, j := indicator.DefaultKdj(high, low, closing) -``` - -#### Rolling Moving Average (RMA) - -The [Rma](https://pkg.go.dev/github.com/cinar/indicator#Rma) function calculates the rolling moving average for a given period. - -``` -R[0] to R[p-1] is SMA(values) -R[p] and after is R[i] = ((R[i-1]*(p-1)) + v[i]) / p -``` - -```Golang -result := indicator.Rma(2, []float64{2, 4, 6, 8, 10, 12}) -``` - -#### Simple Moving Average (SMA) - -The [Sma](https://pkg.go.dev/github.com/cinar/indicator#Sma) function calculates the simple moving average for a given period. - -```Golang -result := indicator.Sma(2, []float64{2, 4, 6, 8, 10}) -``` - -#### Since Change - -The [Since](https://pkg.go.dev/github.com/cinar/indicator#Since) function provides the number values since the last change. - -```Golang -changes := indicator.Since(values) -``` - -#### Triple Exponential Moving Average (TEMA) - -The [Tema](https://pkg.go.dev/github.com/cinar/indicator#Tema) function calculates the Triple Exponential Moving Average (TEMA) for a given period. - -The triple exponential moving average (TEMA) was designed to smooth value fluctuations, thereby making it easier to identify trends without the lag associated with traditional moving averages. It does this by taking multiple exponential moving averages (EMA) of the original EMA and subtracting out some of the lag. - -``` -TEMA = (3 * EMA1) - (3 * EMA2) + EMA3 -EMA1 = EMA(values) -EMA2 = EMA(EMA1) -EMA3 = EMA(EMA2) -``` - -```Golang -tema := indicator.Tema(period, values) -``` - -Based on [Triple Exponential Moving Average (TEMA)](https://www.investopedia.com/terms/t/triple-exponential-moving-average.asp). - -#### Triangular Moving Average (TRIMA) - -The [Trima](https://pkg.go.dev/github.com/cinar/indicator#Trima) function calculates the Triangular Moving Average (TRIMA) for a given period. - -The Triangular Moving Average (TRIMA) is a weighted moving average putting more weight to the middle values. - -``` -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)) -``` - -```Golang -trima := indicator.Trima(period, values) -``` - -Based on [Triangular Moving Average](https://tulipindicators.org/trima). - -#### Triple Exponential Average (TRIX) - -The [Trix](https://pkg.go.dev/github.com/cinar/indicator#Trix) indicator is an oscillator used to identify oversold and overbought markets, and it can also be used as a momentum indicator. Like many oscillators, TRIX oscillates around a zero line. - -``` -EMA1 = EMA(period, values) -EMA2 = EMA(period, EMA1) -EMA3 = EMA(period, EMA2) -TRIX = (EMA3 - Previous EMA3) / Previous EMA3 -``` - -```Golang -trix := indicator.Trix(period, values) -``` - -#### Typical Price - -The [TypicalPrice](https://pkg.go.dev/github.com/cinar/indicator#TypicalPrice) function calculates another approximation of average price for each period and can be used as a filter for moving average systems. - -``` -Typical Price = (High + Low + Closing) / 3 -``` - -```Golang -ta, sma20 := indicator.TypicalPrice(high, low, closing) -``` -#### Volume Weighted Moving Average (VWMA) - -The [Vwma](https://pkg.go.dev/github.com/cinar/indicator#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. -``` - -```Golang -vwma := indicator.Vwma(period, closing, volume) -``` - -#### Vortex Indicator - -The [Vortex](https://pkg.go.dev/github.com/cinar/indicator#Vortex) function 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 = Abs(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 [Vortex Indicator](https://school.stockcharts.com/doku.php?id=technical_indicators:vortex_indicator) - -```Golang -plusVi, minusVi := indicator.Vortex(high, low, closing) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. diff --git a/trend_indicators_test.go b/trend_indicators_test.go deleted file mode 100644 index 72adf04..0000000 --- a/trend_indicators_test.go +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import ( - "testing" -) - -func TestAbsolutePriceOscillator(t *testing.T) { - values := []float64{1, 2, 1, 5, 8, 10, 4, 6, 5, 2} - expected := []float64{0, 0.33, 0, 1.26, 2.26, 2.65, 0.14, 0.22, -0.14, -1.19} - - actual := AbsolutePriceOscillator(2, 5, values) - testEquals(t, roundDigitsAll(actual, 2), expected) -} - -func TestBalanceOfPower(t *testing.T) { - opening := []float64{10, 20, 15, 50} - high := []float64{40, 25, 20, 60} - low := []float64{4, 10, 5, 6} - closing := []float64{20, 15, 50, 55} - expected := []float64{0.28, -0.33, 2.33, 0.09} - - actual := BalanceOfPower(opening, high, low, closing) - testEquals(t, roundDigitsAll(actual, 2), expected) -} - -func TestChandeForecastOscillator(t *testing.T) { - closing := []float64{1, 5, 12, 20} - expected := []float64{110, -26, -5.8333, 4.5} - - actual := ChandeForecastOscillator(closing) - testEquals(t, roundDigitsAll(actual, 4), expected) -} - -func TestCommunityChannelIndex(t *testing.T) { - high := []float64{10, 9, 12, 14, 12} - low := []float64{6, 7, 9, 12, 10} - closing := []float64{9, 11, 7, 10, 8} - expected := []float64{0, 133.33, 114.29, 200, 26.32} - - actual := DefaultCommunityChannelIndex(high, low, closing) - testEquals(t, roundDigitsAll(actual, 2), expected) -} - -func TestEma(t *testing.T) { - values := []float64{2, 4, 6, 8, 12, 14, 16, 18, 20} - expected := []float64{2, 3.333, 5.111, 7.037, 10.346, 12.782, 14.927, 16.976, 18.992} - period := 2 - - actual := Ema(period, values) - testEquals(t, roundDigitsAll(actual, 3), expected) -} - -func TestMassIndex(t *testing.T) { - high := []float64{10, 9, 12, 14, 12} - low := []float64{6, 7, 9, 12, 10} - expected := []float64{1, 1.92, 2.83, 3.69, 4.52} - - actual := MassIndex(high, low) - testEquals(t, roundDigitsAll(actual, 2), expected) -} - -func TestMax(t *testing.T) { - values := []float64{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} - expected := []float64{10, 10, 10, 10, 9, 8, 7, 6, 5, 4} - - actual := Max(4, values) - testEquals(t, actual, expected) -} - -func TestMin(t *testing.T) { - values := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - expected := []float64{1, 1, 1, 1, 2, 3, 4, 5, 6, 7} - - actual := Min(4, values) - testEquals(t, actual, expected) -} - -func TestParabolicSAR(t *testing.T) { - high := []float64{ - 3836.86, - 3766.57, - 3576.17, - 3513.55, - 3529.75, - 3756.17, - 3717.17, - 3572.62, - 3612.43, - } - - low := []float64{ - 3643.25, - 3542.73, - 3371.75, - 3334.02, - 3314.75, - 3558.21, - 3517.79, - 3447.90, - 3494.39, - } - - closing := []float64{ - 3790.55, - 3546.20, - 3507.31, - 3340.81, - 3529.60, - 3717.41, - 3544.35, - 3478.14, - 3612.08, - } - - expectedPsar := []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, - } - - psar, trend := DefaultParabolicSar(high, low, closing) - testEquals(t, roundDigitsAll(psar, 2), expectedPsar) - - for i := 0; i < len(expectedTrend); i++ { - if trend[i] != expectedTrend[i] { - t.Fatalf("at %d actual %d expected %d", i, trend[i], expectedTrend[i]) - } - } -} - -func TestQstick(t *testing.T) { - opening := []float64{10, 20, 15, 50, 40, 41, 43, 80} - closing := []float64{20, 15, 50, 55, 42, 30, 31, 70} - expected := []float64{10, 2.5, 13.33, 11.25, 9.4, 5.2, 3.8, -5.2} - - actual := Qstick(5, opening, closing) - testEquals(t, roundDigitsAll(actual, 2), expected) -} - -func TestKdj(t *testing.T) { - low := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - high := []float64{10, 20, 30, 40, 50, 60, 70, 80, 90, 100} - closing := []float64{5, 10, 15, 20, 25, 30, 35, 40, 45, 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} - - k, d, j := DefaultKdj(high, low, closing) - - testEquals(t, roundDigitsAll(k, 2), expectedK) - testEquals(t, roundDigitsAll(d, 2), expectedD) - testEquals(t, roundDigitsAll(j, 2), expectedJ) -} - -func TestRma(t *testing.T) { - values := []float64{ - 0, - 0.00005, - 0.000017, - 0.000262, - 0.000107, - 0, - 0, - 0.000597, - 0, - 0, - 0.000059, - 0.000198, - 0.000073, - 0, - 0.000006, - 0, - 0.000077, - 0.000032, - 0.000112, - } - - expected := []float64{ - 0.00009735714286, - 0.00009083163265, - 0.00008434365889, - 0.00008381911183, - 0.0000801177467, - 0.00008239505051, - } - period := 14 - - actual := Rma(period, values) - testEquals(t, roundDigitsAll(actual[len(actual)-6:], 14), expected) -} - -func TestSma(t *testing.T) { - values := []float64{2, 4, 6, 8, 10} - expected := []float64{2, 3, 5, 7, 9} - period := 2 - - actual := Sma(period, values) - testEquals(t, actual, expected) -} - -func TestSince(t *testing.T) { - values := []float64{1, 2, 2, 3, 4, 4, 4, 4, 5, 6} - expected := []int{0, 0, 1, 0, 0, 1, 2, 3, 0, 0} - - actual := Since(values) - testEqualsInt(t, actual, expected) -} - -func TestSum(t *testing.T) { - values := []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - expected := []float64{1, 3, 6, 10, 14, 18, 22, 26, 30, 34} - period := 4 - - actual := Sum(period, values) - testEquals(t, actual, expected) -} - -func TestTrix(t *testing.T) { - values := []float64{2, 4, 6, 8, 12, 14, 16, 18, 20} - period := 4 - expected := []float64{0, 0.06, 0.17, 0.26, 0.33, 0.33, 0.3, 0.25, 0.21} - - actual := Trix(period, values) - testEquals(t, roundDigitsAll(actual, 2), expected) -} - -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, - } - - plusVi, minusVi := Vortex(high, low, closing) - testEquals(t, roundDigitsAll(plusVi, 5), expectedPlusVi) - testEquals(t, roundDigitsAll(minusVi, 5), expectedMinusVi) -} - -func TestVwma(t *testing.T) { - closing := []float64{20, 21, 21, 19, 16} - volume := []float64{100, 50, 40, 50, 100} - expected := []float64{20, 20.33, 20.47, 20.29, 17.84} - period := 3 - - actual := Vwma(period, closing, volume) - testEquals(t, roundDigitsAll(actual, 2), expected) -} - -func TestDefaultVwma(t *testing.T) { - closing := []float64{20, 21, 21, 19, 16} - volume := []float64{100, 50, 40, 50, 100} - expected := []float64{20, 20.33, 20.47, 20.17, 18.94} - - actual := DefaultVwma(closing, volume) - testEquals(t, roundDigitsAll(actual, 2), expected) -} diff --git a/trend_strategies.go b/trend_strategies.go deleted file mode 100644 index 978e737..0000000 --- a/trend_strategies.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -// Chande forecast oscillator strategy. -func ChandeForecastOscillatorStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - cfo := ChandeForecastOscillator(asset.Closing) - - for i := 0; i < len(actions); i++ { - if cfo[i] < 0 { - actions[i] = BUY - } else if cfo[i] > 0 { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Moving chande forecast oscillator strategy function. -func MovingChandeForecastOscillatorStrategy(period int, asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - cfo := MovingChandeForecastOscillator(period, asset.Closing) - - for i := 0; i < len(actions); i++ { - if cfo[i] < 0 { - actions[i] = BUY - } else if cfo[i] > 0 { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Make moving chande forecast oscillator strategy. -func MakeMovingChandeForecastOscillatorStrategy(period int) StrategyFunction { - return func(asset *Asset) []Action { - return MovingChandeForecastOscillatorStrategy(period, asset) - } -} - -// The KdjStrategy function uses the k, d, j values that are generated by -// the Kdj indicator function to provide a BUY action when k crosses -// above d and j. It is stronger when below 20%. Also the SELL -// action is when k crosses below d and j. It is strong when -// above 80%. -// -// Returns actions. -func KdjStrategy(rPeriod, kPeriod, dPeriod int, asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - k, d, j := Kdj(rPeriod, kPeriod, dPeriod, asset.High, asset.Low, asset.Closing) - - for i := 0; i < len(actions); i++ { - if (k[i] > d[i]) && (k[i] > j[i]) && (k[i] <= 20) { - actions[i] = BUY - } else if (k[i] < d[i]) && (k[i] < j[i]) && (k[i] >= 80) { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Make KDJ strategy function. -func MakeKdjStrategy(rPeriod, kPeriod, dPeriod int) StrategyFunction { - return func(asset *Asset) []Action { - return KdjStrategy(rPeriod, kPeriod, dPeriod, asset) - } -} - -// Default KDJ strategy function. -func DefaultKdjStrategy(asset *Asset) []Action { - return KdjStrategy(9, 3, 3, asset) -} - -// MACD strategy. -func MacdStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - macd, signal := Macd(asset.Closing) - - for i := 0; i < len(actions); i++ { - if macd[i] > signal[i] { - actions[i] = BUY - } else if macd[i] < signal[i] { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Trend strategy. Buy when trending up for count times, -// sell when trending down for count times. -func TrendStrategy(asset *Asset, count uint) []Action { - actions := make([]Action, len(asset.Date)) - - if len(actions) == 0 { - return actions - } - - lastClosing := asset.Closing[0] - trendCount := uint(1) - trendUp := false - - actions[0] = HOLD - - for i := 1; i < len(actions); i++ { - closing := asset.Closing[i] - - if trendUp && (lastClosing <= closing) { - trendCount++ - } else if !trendUp && (lastClosing >= closing) { - trendCount++ - } else { - trendUp = !trendUp - trendCount = 1 - } - - lastClosing = closing - - if trendCount >= count { - if trendUp { - actions[i] = BUY - } else { - actions[i] = SELL - } - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Make trend strategy function. -func MakeTrendStrategy(count uint) StrategyFunction { - return func(asset *Asset) []Action { - return TrendStrategy(asset, count) - } -} - -// The VwmaStrategy function uses SMA and VWMA indicators to provide -// a BUY action when VWMA is above SMA, and a SELL signal when VWMA -// is below SMA, a HOLD signal otherwse. -// -// Returns actions -func VwmaStrategy(asset *Asset, period int) []Action { - actions := make([]Action, len(asset.Date)) - - sma := Sma(period, asset.Closing) - vwma := Vwma(period, asset.Closing, asset.Volume) - - for i := 0; i < len(actions); i++ { - if vwma[i] > sma[i] { - actions[i] = BUY - } else if vwma[i] < sma[i] { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Makes a VWMA strategy for the given period. -func MakeVwmaStrategy(period int) StrategyFunction { - return func(asset *Asset) []Action { - return VwmaStrategy(asset, period) - } -} - -// Default VWMA strategy function. -func DefaultVwmaStrategy(asset *Asset) []Action { - return VwmaStrategy(asset, 20) -} diff --git a/trend_strategies.md b/trend_strategies.md deleted file mode 100644 index f883785..0000000 --- a/trend_strategies.md +++ /dev/null @@ -1,92 +0,0 @@ -### Trend Strategies - -Trend strategies generate signals based on a trend indicator. - -- [Chande Forecast Oscillator Strategy](#chande-forecast-oscillator-strategy) -- [MACD Strategy](#macd-strategy) -- [KDJ Strategy](#kdj-strategy) -- [Trend Strategy](#trend-strategy) -- [Volume Weighted Moving Average (VWMA) Strategy](#volume-weighted-moving-average-vwma-strategy) - -#### Chande Forecast Oscillator Strategy - -The [ChandeForecastOscillatorStrategy](https://pkg.go.dev/github.com/cinar/indicator#ChandeForecastOscillatorStrategy) uses _cfo_ values that are generated by the [ChandeForecastOscillator](https://pkg.go.dev/github.com/cinar/indicator#ChandeForecastOscillator) indicator function to provide a BUY action when _cfo_ is below zero, and SELL action when _cfo_ is above zero. - -```golang -actions := indicator.ChandeForecastOscillatorStrategy(asset) -``` - -#### KDJ Strategy - -The [KdjStrategy](https://pkg.go.dev/github.com/cinar/indicator#KdjStrategy) function uses the _k_, _d_, _j_ values that are generated by the [Kdj](https://pkg.go.dev/github.com/cinar/indicator#Kdj) indicator function to provide a BUY action when _k_ crosses above _d_ and _j_. It is stronger when below 20%. Also the SELL action is when _k_ crosses below _d_ and _j_. It is strong when above 80%. - -```golang -actions := indicator.KdjStrategy(rPeriod, kPeriod, dPeriod, asset) -``` - -As the [KdjStrategy](https://pkg.go.dev/github.com/cinar/indicator#KdjStrategy) function does not match the [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction), the [MakeKdjStrategy](https://pkg.go.dev/github.com/cinar/indicator#MakeKdjStrategy) function takes _rPeriod_, _kPeriod_, _dPeriod_, and provides a strategy function. - -```golang -strategy := indicator.MakeKdjStrategy(rPeriod, kPeriod, dPeriod) -actions := strategy(asset) -``` - -By default, _rPeriod_ of 9, _kPeriod_ of 3, and _dPeriod_ of 3 are used. The [DefaultKdjStrategy](https://pkg.go.dev/github.com/cinar/indicator#DefaultKdjStrategy) function can be used with those periods. - -```golang -actions := indicator.DefaultKdjStrategy(asset) -``` - -#### MACD Strategy - -The [MacdStrategy](https://pkg.go.dev/github.com/cinar/indicator#MacdStrategy) uses the _macd_, and _signal_ values that are generated by the [Macd](https://pkg.go.dev/github.com/cinar/indicator#Macd) indicator function to provide a BUY action when _macd_ crosses above _signal_, and SELL action when _macd_ crosses below _signal_. - -```golang -actions := indicator.MacdStrategy(asset) -``` - -#### Trend Strategy - -The [TrendStrategy](https://pkg.go.dev/github.com/cinar/indicator#TrendStrategy) provides a simply strategy to buy the given asset following the asset's closing value increases in _count_ subsequent rows. Produces the sell action following the asset's closing value decreases in _count_ subsequent rows. - -```golang -actions := indicator.TrendStrategy(asset, 4) -``` - -The function signature of [TrendStrategy](https://pkg.go.dev/github.com/cinar/indicator#TrendStrategy) does not match the [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction) type, as it requires an additional _count_ parameter. The [MakeTrendStrategy](https://pkg.go.dev/github.com/cinar/indicator#MakeTrendStrategy) function can be used to return a [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction) instance based on the given _count_ value. - -```golang -strategy := indicator.MakeTrendStrategy(4) -actions := strategy(asset) -``` - -#### Volume Weighted Moving Average (VWMA) Strategy - -The [VwmaStrategy](https://pkg.go.dev/github.com/cinar/indicator#VwmaStrategy) function uses SMA and VWMA indicators to provide a _BUY_ action when VWMA is above SMA, and a _SELL_ signal when VWMA is below SMA, a _HOLD_ signal otherwse. - -```golang -actions := indicator.VwmaStrategy(asset, 3) -``` - -The function signature of [VwmaStrategy](https://pkg.go.dev/github.com/cinar/indicator#VwmaStrategy) does not match the [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction) type, as it requires an additional _period_ parameter. The [MakeVwmaStrategy](https://pkg.go.dev/github.com/cinar/indicator#MakeVwmaStrategy) function can be used to return a [StrategyFunction](https://pkg.go.dev/github.com/cinar/indicator#StrategyFunction) instance based on the given _count_ value. - -```golang -strategy := indicator.MakeTrendStrategy(4) -actions := strategy(asset) -``` - -The [DefaultVwmaStrategy](https://pkg.go.dev/github.com/cinar/indicator#DefaultVwmaStrategy) function can be used with the default period of 20. - -```golang -actions := indicator.DefaultVwmaStrategy(asset) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. diff --git a/trend_strategies_test.go b/trend_strategies_test.go deleted file mode 100644 index da8f549..0000000 --- a/trend_strategies_test.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import ( - "testing" - "time" -) - -func TestTrendStrategy(t *testing.T) { - asset := &Asset{ - Date: []time.Time{ - time.Now(), time.Now(), time.Now(), time.Now(), time.Now(), - }, - Opening: []float64{ - 0, 0, 0, 0, 0, - }, - Closing: []float64{ - 0, 1, 2, 1, 0, - }, - High: []float64{ - 0, 0, 0, 0, 0, - }, - Low: []float64{ - 0, 0, 0, 0, 0, - }, - Volume: []float64{ - 0, 0, 0, 0, 0, - }, - } - - expected := []Action{ - HOLD, HOLD, BUY, HOLD, SELL, - } - - actual := TrendStrategy(asset, 2) - testEqualsAction(t, actual, expected) -} - -func TestVwmaStrategy(t *testing.T) { - asset := &Asset{ - Date: []time.Time{ - time.Now(), time.Now(), time.Now(), time.Now(), time.Now(), - }, - Opening: []float64{ - 0, 0, 0, 0, 0, - }, - Closing: []float64{ - 20, 21, 21, 19, 16, - }, - High: []float64{ - 0, 0, 0, 0, 0, - }, - Low: []float64{ - 0, 0, 0, 0, 0, - }, - Volume: []float64{ - 100, 50, 40, 50, 100, - }, - } - - expected := []Action{ - HOLD, SELL, SELL, SELL, SELL, - } - - period := 3 - - strategy := MakeVwmaStrategy(period) - actual := strategy(asset) - testEqualsAction(t, actual, expected) -} - -func TestDefaultVwmaStrategy(t *testing.T) { - asset := &Asset{ - Date: []time.Time{ - time.Now(), time.Now(), time.Now(), time.Now(), time.Now(), - }, - Opening: []float64{ - 0, 0, 0, 0, 0, - }, - Closing: []float64{ - 20, 21, 21, 19, 16, - }, - High: []float64{ - 0, 0, 0, 0, 0, - }, - Low: []float64{ - 0, 0, 0, 0, 0, - }, - Volume: []float64{ - 100, 50, 40, 50, 100, - }, - } - - expected := []Action{ - HOLD, SELL, SELL, SELL, SELL, - } - - actual := DefaultVwmaStrategy(asset) - testEqualsAction(t, actual, expected) -} diff --git a/v2/go.mod b/v2/go.mod deleted file mode 100644 index 65c5464..0000000 --- a/v2/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/cinar/indicator/v2 - -go 1.20 diff --git a/volatility_indicators.go b/volatility_indicators.go deleted file mode 100644 index 4118286..0000000 --- a/volatility_indicators.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import ( - "math" -) - -// Acceleration Bands. Plots upper and lower envelope bands -// around a simple moving average. -// -// Upper Band = SMA(High * (1 + 4 * (High - Low) / (High + Low))) -// Middle Band = SMA(Closing) -// Lower Band = SMA(Low * (1 - 4 * (High - Low) / (High + Low))) -// -// Returns upper band, middle band, lower band. -func AccelerationBands(high, low, closing []float64) ([]float64, []float64, []float64) { - checkSameSize(high, low, closing) - - k := divide(subtract(high, low), add(high, low)) - - upperBand := Sma(20, multiply(high, addBy(multiplyBy(k, 4), 1))) - middleBand := Sma(20, closing) - lowerBand := Sma(20, multiply(low, addBy(multiplyBy(k, -4), 1))) - - return upperBand, middleBand, lowerBand -} - -// Average True Range (ATR). It is a technical analysis indicator that measures market -// volatility by decomposing the entire range of stock prices for that period. -// -// TR = Max((High - Low), (High - Closing), (Closing - Low)) -// ATR = SMA TR -// -// Returns tr, atr -func Atr(period int, high, low, closing []float64) ([]float64, []float64) { - checkSameSize(high, low, closing) - - tr := make([]float64, len(closing)) - - for i := 0; i < len(tr); i++ { - tr[i] = math.Max(high[i]-low[i], math.Max(high[i]-closing[i], closing[i]-low[i])) - } - - atr := Sma(period, tr) - - return tr, atr -} - -// Bollinger Band Width. It measures the percentage difference between the -// upper band and the lower band. It decreases as Bollinger Bands narrows -// and increases as Bollinger Bands widens -// -// During a period of rising price volatity the band width widens, and -// during a period of low market volatity band width contracts. -// -// Band Width = (Upper Band - Lower Band) / Middle Band -// -// Returns bandWidth, bandWidthEma90 -func BollingerBandWidth(middleBand, upperBand, lowerBand []float64) ([]float64, []float64) { - checkSameSize(middleBand, upperBand, lowerBand) - - bandWidth := make([]float64, len(middleBand)) - for i := 0; i < len(bandWidth); i++ { - bandWidth[i] = (upperBand[i] - lowerBand[i]) / middleBand[i] - } - - bandWidthEma90 := Ema(90, bandWidth) - - return bandWidth, bandWidthEma90 -} - -// Bollinger Bands. -// -// Middle Band = 20-Period SMA. -// Upper Band = 20-Period SMA + 2 (20-Period Std) -// Lower Band = 20-Period SMA - 2 (20-Period Std) -// -// Returns middle band, upper band, lower band. -func BollingerBands(closing []float64) ([]float64, []float64, []float64) { - middleBand := Sma(20, closing) - - std := StdFromSma(20, closing, middleBand) - std2 := multiplyBy(std, 2) - - upperBand := add(middleBand, std2) - lowerBand := subtract(middleBand, std2) - - return middleBand, upperBand, lowerBand -} - -// 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(high, low, closing []float64) ([]float64, []float64) { - _, atr22 := Atr(22, high, low, closing) - highestHigh22 := Max(22, high) - lowestLow22 := Min(22, low) - - chandelierExitLong := make([]float64, len(closing)) - chandelierExitShort := make([]float64, len(closing)) - - for i := 0; i < len(chandelierExitLong); i++ { - chandelierExitLong[i] = highestHigh22[i] - (atr22[i] * float64(3)) - chandelierExitShort[i] = lowestLow22[i] + (atr22[i] * float64(3)) - } - - return chandelierExitLong, chandelierExitShort -} - -// Standard deviation. -func Std(period int, values []float64) []float64 { - return StdFromSma(period, values, Sma(period, values)) -} - -// Standard deviation from the given SMA. -func StdFromSma(period int, values, sma []float64) []float64 { - result := make([]float64, len(values)) - - sum2 := 0.0 - for i, v := range values { - sum2 += v * v - if i < period-1 { - result[i] = 0.0 - } else { - result[i] = math.Sqrt(sum2/float64(period) - sma[i]*sma[i]) - w := values[i-(period-1)] - sum2 -= w * w - } - } - - return result -} - -// ProjectionOscillator calculates the Projection Oscillator (PO). The PO -// uses the linear regression slope, along with highs and lows. -// -// Period defines the moving window to calculates the PO, and the smooth -// period defines the moving windows to take EMA of PO. -// -// PL = Min(period, (high + MLS(period, x, high))) -// PU = Max(period, (low + MLS(period, x, low))) -// PO = 100 * (Closing - PL) / (PU - PL) -// SPO = EMA(smooth, PO) -// -// Returns po, spo. -func ProjectionOscillator(period, smooth int, high, low, closing []float64) ([]float64, []float64) { - x := generateNumbers(0, float64(len(closing)), 1) - mHigh, _ := MovingLeastSquare(period, x, high) - mLow, _ := MovingLeastSquare(period, x, low) - - vHigh := add(high, multiply(mHigh, x)) - vLow := add(low, multiply(mLow, x)) - - pu := Max(period, vHigh) - pl := Min(period, vLow) - - po := divide(multiplyBy(subtract(closing, pl), 100), subtract(pu, pl)) - spo := Ema(smooth, po) - - return po, spo -} - -// 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) -// -// Returns ui. -func UlcerIndex(period int, closing []float64) []float64 { - highClosing := Max(period, closing) - percentageDrawdown := multiplyBy(divide(subtract(closing, highClosing), highClosing), 100) - squaredAverage := Sma(period, multiply(percentageDrawdown, percentageDrawdown)) - ui := sqrt(squaredAverage) - - return ui -} - -// The default ulcer index with the default period of 14. -func DefaultUlcerIndex(closing []float64) []float64 { - return UlcerIndex(14, closing) -} - -// 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 -// -// Returns upperChannel, middleChannel, lowerChannel. -func DonchianChannel(period int, closing []float64) ([]float64, []float64, []float64) { - upperChannel := Max(period, closing) - lowerChannel := Min(period, closing) - middleChannel := divideBy(add(upperChannel, lowerChannel), 2) - - return upperChannel, middleChannel, lowerChannel -} - -// The Keltner Channel (KC) provides volatility-based bands that are placed -// on either side of an asset's price and can aid in determining the -// direction of a trend. -// -// Middle Line = EMA(period, closings) -// Upper Band = EMA(period, closings) + 2 * ATR(period, highs, lows, closings) -// Lower Band = EMA(period, closings) - 2 * ATR(period, highs, lows, closings) -// -// Returns upperBand, middleLine, lowerBand. -func KeltnerChannel(period int, high, low, closing []float64) ([]float64, []float64, []float64) { - _, atr := Atr(period, high, low, closing) - atr2 := multiplyBy(atr, 2) - - middleLine := Ema(period, closing) - upperBand := add(middleLine, atr2) - lowerBand := subtract(middleLine, atr2) - - return upperBand, middleLine, lowerBand -} - -// The default keltner channel with the default period of 20. -func DefaultKeltnerChannel(high, low, closing []float64) ([]float64, []float64, []float64) { - return KeltnerChannel(20, high, low, closing) -} diff --git a/volatility_indicators.md b/volatility_indicators.md deleted file mode 100644 index 501831e..0000000 --- a/volatility_indicators.md +++ /dev/null @@ -1,172 +0,0 @@ -### Volatility Indicators - -Volatility indicators measure the rate of movement regardless of its direction. - -- [Acceleration Bands](#acceleration-bands) -- [Actual True Range (ATR)](#actual-true-range-atr) -- [Bollinger Band Width](#bollinger-band-width) -- [Bollinger Bands](#bollinger-bands) -- [Chandelier Exit](#chandelier-exit) -- [Donchian Channel (DC)](#donchian-channel-dc) -- [Keltner Channel (KC)](#keltner-channel-kc) -- [Moving Standard Deviation (Std)](#moving-standard-deviation-std) -- [Projection Oscillator (PO)](#projection-oscillator-po) -- [Ulcer Index (UI)](#ulcer-index-ui) - -#### Acceleration Bands - -The [AccelerationBands](https://pkg.go.dev/github.com/cinar/indicator#AccelerationBands) plots upper and lower envelope bands around a simple moving average. - -``` -Upper Band = SMA(High * (1 + 4 * (High - Low) / (High + Low))) -Middle Band = SMA(Closing) -Lower Band = SMA(Low * (1 + 4 * (High - Low) / (High + Low))) -``` - -```golang -upperBand, middleBand, lowerBand := indicator.AccelerationBands(high, low, closing) -``` - -#### Actual True Range (ATR) - -The [Atr](https://pkg.go.dev/github.com/cinar/indicator#Atr) function calculates a technical analysis indicator that measures market volatility by decomposing the entire range of stock prices for that period. - -``` -TR = Max((High - Low), (High - Closing), (Closing - Low)) -ATR = 14-Period SMA TR -``` - -```Golang -tr, atr := indicator.Atr(14, high, low, closing) -``` - -#### Bollinger Bands - -The [BollingerBands](https://pkg.go.dev/github.com/cinar/indicator#BollingerBands) function calculates the bollinger bands, middle band, upper band, lower band, provides identification of when a stock is oversold or overbought. - -``` -Middle Band = 20-Period SMA. -Upper Band = 20-Period SMA + 2 (20-Period Std) -Lower Band = 20-Period SMA - 2 (20-Period Std) -``` - -```Golang -middleBand, upperBand, lowerBand := indicator.BollingerBands(closing) -``` - -#### Bollinger Band Width - -The [BollingerBandWidth](https://pkg.go.dev/github.com/cinar/indicator#BollingerBandWidth) function measures the percentage difference between the upper band and the lower band. It decreases as Bollinger Bands narrows and increases as Bollinger Bands widens. - -During a period of rising price volatility the band width widens, and during a period of low market volatility band width contracts. - -``` -Band Width = (Upper Band - Lower Band) / Middle Band -``` - -```Golang -bandWidth, bandWidthEma90 := indicator.BollingerBandWidth(middleBand, upperBand, lowerBand) -``` - -#### Chandelier Exit - -The [ChandelierExit](https://pkg.go.dev/github.com/cinar/indicator#ChandelierExit) function 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 -``` - -```Golang -chandelierExitLong, chandelierExitShort := indicator.ChandelierExit(high, low, closing) -``` - -#### Donchian Channel (DC) - -The [DonchianChannel](https://pkg.go.dev/github.com/cinar/indicator#DonchianChannel) 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 -``` - -```golang -upperChannel, middleChannel, lowerChannel := indicator.DonchianChannel(period, closing) -``` - -#### Keltner Channel (KC) - -The [KeltnerChannel](https://pkg.go.dev/github.com/cinar/indicator#KeltnerChannel) provides volatility-based bands that are placed on either side of an asset's price and can aid in determining the direction of a trend. - -``` -Middle Line = EMA(period, closings) -Upper Band = EMA(period, closings) + 2 * ATR(period, highs, lows, closings) -Lower Band = EMA(period, closings) - 2 * ATR(period, highs, lows, closings) -``` - -```golang -upperBand, middleLine, lowerBand := indicator.KeltnerChannel(period, high, low, closing) -``` - -The [DefaultKeltnerChannel](https://pkg.go.dev/github.com/cinar/indicator#DefaultKeltnerChannel) calculates it with the default period of 20. - -```golang -upperBand, middleLine, lowerBand := indicator.DefaultKeltnerChannel(high, low, closing) -``` - -#### Moving Standard Deviation (Std) - -The [Std](https://pkg.go.dev/github.com/cinar/indicator#Std) function calculates the moving standard deviation for a given period. - -```Golang -result := indicator.Std(2, []float64{2, 4, 6, 8, 12, 14, 16, 18, 20}) -``` - -#### Projection Oscillator (PO) - -The [ProjectionOscillator](https://pkg.go.dev/github.com/cinar/indicator#ProjectionOscillator) calculates the Projection Oscillator (PO). The PO uses the linear regression slope, along with highs and lows. - -Period defines the moving window to calculates the PO, and the smooth period defines the moving windows to take EMA of PO. - -``` -PL = Min(period, (high + MLS(period, x, high))) -PU = Max(period, (low + MLS(period, x, low))) -PO = 100 * (Closing - PL) / (PU - PL) -SPO = EMA(smooth, PO) -``` - -```golang -po, spo := indicator.ProjectionOscillator(12, 4, high, low, closing) -``` - -#### Ulcer Index (UI) - -The [UlcerIndex](https://pkg.go.dev/github.com/cinar/indicator#UlcerIndex) 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) -``` - -```golang -ui := indicator.UlcerIndex(period, closing) -``` - -The [DefaultUlcerIndex](https://pkg.go.dev/github.com/cinar/indicator#DefaultUlcerIndex) measures the ulcer index with the default period of 14. - -```golang -ui := indicator.DefaultUlcerIndex(closing) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. diff --git a/volatility_indicators_test.go b/volatility_indicators_test.go deleted file mode 100644 index 6d1fd1e..0000000 --- a/volatility_indicators_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import ( - "testing" -) - -func TestBollingerBands(t *testing.T) { - closing := []float64{ - 2, 4, 6, 8, 12, 14, 16, 18, 20, - 2, 4, 6, 8, 12, 14, 16, 18, 20, - 2, 4, 6, 8, 12, 14, 16, 18, 20, - 2, 4, 6, 8, 12, 14, 16, 18, 20, - } - - expectedMiddleBand := []float64{ - 2, 3, 4, 5, 6.4, 7.67, 8.86, 10, 11.11, - 10.2, 9.64, 9.33, 9.23, 9.43, 9.73, 10.13, 10.59, 11.11, - 10.63, 10.3, 10.5, 10.7, 11, 11.3, 11.5, 11.7, 11.9, - 11.1, 10.3, 10.5, 10.7, 11, 11.3, 11.5, 11.7, 11.9, - } - - expectedUpperBand := []float64{ - 2, 3, 4, 5, 6.4, 7.67, 8.86, 10, 11.11, 10.2, - 9.64, 9.33, 9.23, 9.43, 9.73, 10.13, 10.59, 11.11, - 10.63, 22.78, 22.56, 22.45, 22.56, 22.84, 23.22, 23.72, 24.32, - 23.9, 22.78, 22.56, 22.45, 22.56, 22.84, 23.22, 23.72, 24.32, - } - - expectedLowerBand := []float64{ - 2, 3, 4, 5, 6.4, 7.67, 8.86, 10, 11.11, - 10.2, 9.64, 9.33, 9.23, 9.43, 9.73, 10.13, 10.59, 11.11, - 10.63, -2.18, -1.56, -1.05, -0.56, -0.24, -0.22, -0.32, -0.52, - -1.7, -2.18, -1.56, -1.05, -0.56, -0.24, -0.22, -0.32, -0.52, - } - - actualMiddleBand, actualUpperBand, actualLowerBand := BollingerBands(closing) - testEquals(t, roundDigitsAll(actualMiddleBand, 2), expectedMiddleBand) - testEquals(t, roundDigitsAll(actualUpperBand, 2), expectedUpperBand) - testEquals(t, roundDigitsAll(actualLowerBand, 2), expectedLowerBand) -} - -func TestStd(t *testing.T) { - values := []float64{2, 4, 6, 8, 12, 14, 16, 18, 20} - expected := []float64{0, 1, 1, 1, 2, 1, 1, 1, 1} - period := 2 - - actual := Std(period, values) - testEquals(t, roundDigitsAll(actual, 3), expected) -} - -func TestStdFromSma(t *testing.T) { - values := []float64{2, 4, 6, 8, 12, 14, 16, 18, 20} - expected := []float64{0, 1, 1, 1, 2, 1, 1, 1, 1} - period := 2 - - actual := StdFromSma(period, values, Sma(period, values)) - testEquals(t, roundDigitsAll(actual, 3), expected) -} - -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} - - actual := DefaultUlcerIndex(closing) - testEquals(t, roundDigitsAll(actual, 2), expected) -} - -func TestDonchianChannel(t *testing.T) { - closing := []float64{9, 11, 7, 10, 8} - period := 4 - expectedUpperChannel := []float64{9, 11, 11, 11, 11} - expectedMiddleChannel := []float64{9, 10, 9, 9, 9} - expectedLowerChannel := []float64{9, 9, 7, 7, 7} - - actualUpperChannel, actualMiddleChannel, actualLowerChannel := DonchianChannel(period, closing) - testEquals(t, roundDigitsAll(actualUpperChannel, 2), expectedUpperChannel) - testEquals(t, roundDigitsAll(actualMiddleChannel, 2), expectedMiddleChannel) - testEquals(t, roundDigitsAll(actualLowerChannel, 2), expectedLowerChannel) -} - -func TestKeltnerChannel(t *testing.T) { - high := []float64{10, 9, 12, 14, 12} - low := []float64{6, 7, 9, 12, 10} - closing := []float64{9, 11, 7, 10, 8} - expectedUpperBand := []float64{17, 17.19, 17.65, 17.58, 17.38} - expectedMiddleLine := []float64{9, 9.19, 8.98, 9.08, 8.98} - expectedLowerBand := []float64{1, 1.19, 0.32, 0.58, 0.58} - - actualUpperBand, actualMiddleLine, actualLowerBand := DefaultKeltnerChannel(high, low, closing) - testEquals(t, roundDigitsAll(actualUpperBand, 2), expectedUpperBand) - testEquals(t, roundDigitsAll(actualMiddleLine, 2), expectedMiddleLine) - testEquals(t, roundDigitsAll(actualLowerBand, 2), expectedLowerBand) -} diff --git a/volatility_strategies.go b/volatility_strategies.go deleted file mode 100644 index 689d8b1..0000000 --- a/volatility_strategies.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -// Bollinger bands strategy function. -func BollingerBandsStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - _, upperBand, lowerBand := BollingerBands(asset.Closing) - - for i := 0; i < len(actions); i++ { - if asset.Closing[i] > upperBand[i] { - actions[i] = SELL - } else if asset.Closing[i] < lowerBand[i] { - actions[i] = BUY - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Projection oscillator strategy function. -func ProjectionOscillatorStrategy(period, smooth int, asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - po, spo := ProjectionOscillator( - period, - smooth, - asset.High, - asset.Low, - asset.Closing) - - for i := 0; i < len(actions); i++ { - if po[i] > spo[i] { - actions[i] = BUY - } else if po[i] < spo[i] { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Make projection oscillator strategy. -func MakeProjectionOscillatorStrategy(period, smooth int) StrategyFunction { - return func(asset *Asset) []Action { - return ProjectionOscillatorStrategy(period, smooth, asset) - } -} diff --git a/volatility_strategies.md b/volatility_strategies.md deleted file mode 100644 index 8cb285d..0000000 --- a/volatility_strategies.md +++ /dev/null @@ -1,32 +0,0 @@ -### Volatility Strategies - -Volatility strategies generate signals based on a volatility indicator. - -- [Bollinger Bands Strategy](#bollinger-bands-strategy) -- [Projection Oscillator Strategy](#projection-oscillator-strategy) - -#### Bollinger Bands Strategy - -The [BollingerBandsStrategy](https://pkg.go.dev/github.com/cinar/indicator#BollingerBandsStrategy) uses the _upperBand_, and _lowerBand_ values that are generated by the [BollingerBands](https://pkg.go.dev/github.com/cinar/indicator#BollingerBands) indicator function to provide a SELL action when the asset's closing is above the _upperBand_, and a BUY action when the asset's closing is below the _lowerBand_ values. - -```golang -actions := indicator.BollingerBandsStrategy(asset) -``` - -#### Projection Oscillator Strategy - -The [ProjectionOscillatorStrategy](https://pkg.go.dev/github.com/cinar/indicator#ProjectionOscillatorStrategy) uses _po_ and _spo_ values that are generated by the [ProjectionOscillator](https://pkg.go.dev/github.com/cinar/indicator#ProjectionOscillator) indicator function to provide a BUY action when _po_ is above _spo_, and SELL action when _po_ is below _spo_. - -```golang -actions := indicator.ProjectionOscillatorStrategy(period, smooth, asset) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. diff --git a/volume_indicators.go b/volume_indicators.go deleted file mode 100644 index aeb4a2f..0000000 --- a/volume_indicators.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -// Starting value for NVI. -const NVI_STARTING_VALUE = 1000 - -// Default period of CMF. -const CMF_DEFAULT_PERIOD = 20 - -// 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 -// -// Returns ad. -func AccumulationDistribution(high, low, closing, volume []float64) []float64 { - checkSameSize(high, low, closing) - - ad := make([]float64, len(closing)) - - for i := 0; i < len(ad); i++ { - if i > 0 { - ad[i] = ad[i-1] - } - - ad[i] += float64(volume[i]) * (((closing[i] - low[i]) - (high[i] - closing[i])) / (high[i] - low[i])) - } - - return ad -} - -// On-Balance Volume (OBV). It is a technical trading momentum indicator that -// uses volume flow to predict changes in stock price. -// -// volume, if Closing > Closing-Prev -// -// OBV = OBV-Prev + 0, if Closing = Closing-Prev -// -// -volume, if Closing < Closing-Prev -// -// Returns obv -func Obv(closing, volume []float64) []float64 { - if len(closing) != len(volume) { - panic("not all same size") - } - - obv := make([]float64, len(volume)) - - for i := 1; i < len(obv); i++ { - obv[i] = obv[i-1] - - if closing[i] > closing[i-1] { - obv[i] += volume[i] - } else if closing[i] < closing[i-1] { - obv[i] -= volume[i] - } - } - - return obv -} - -// The Money Flow Index (MFI) analyzes both the closing price and the volume -// to measure to identify overbought and oversold states. It is similar to -// the Relative Strength Index (RSI), but it also uses the volume. -// -// Raw Money Flow = Typical Price * Volume -// Money Ratio = Positive Money Flow / Negative Money Flow -// Money Flow Index = 100 - (100 / (1 + Money Ratio)) -// -// Retruns money flow index values. -func MoneyFlowIndex(period int, high, low, closing, volume []float64) []float64 { - typicalPrice, _ := TypicalPrice(low, high, closing) - rawMoneyFlow := multiply(typicalPrice, volume) - - signs := extractSign(diff(rawMoneyFlow, 1)) - moneyFlow := multiply(signs, rawMoneyFlow) - - positiveMoneyFlow := keepPositives(moneyFlow) - negativeMoneyFlow := keepNegatives(moneyFlow) - - moneyRatio := divide( - Sum(period, positiveMoneyFlow), - Sum(period, multiplyBy(negativeMoneyFlow, -1))) - - moneyFlowIndex := addBy(multiplyBy(pow(addBy(moneyRatio, 1), -1), -100), 100) - - return moneyFlowIndex -} - -// Default money flow index with period 14. -func DefaultMoneyFlowIndex(high, low, closing, volume []float64) []float64 { - return MoneyFlowIndex(14, high, low, closing, volume) -} - -// The Force Index (FI) uses the closing price and the volume to assess -// the power behind a move and identify turning points. -// -// Force Index = EMA(period, (Current - Previous) * Volume) -// -// Returns force index. -func ForceIndex(period int, closing, volume []float64) []float64 { - return Ema(period, multiply(diff(closing, 1), volume)) -} - -// The default Force Index (FI) with window size of 13. -func DefaultForceIndex(closing, volume []float64) []float64 { - return ForceIndex(13, closing, volume) -} - -// The Ease of Movement (EMV) is a volume based oscillator measuring -// the ease of price movement. -// -// Distance Moved = ((High + Low) / 2) - ((Priod High + Prior Low) /2) -// Box Ratio = ((Volume / 100000000) / (High - Low)) -// EMV(1) = Distance Moved / Box Ratio -// EMV(14) = SMA(14, EMV(1)) -// -// Returns ease of movement values. -func EaseOfMovement(period int, high, low, volume []float64) []float64 { - distanceMoved := diff(divideBy(add(high, low), 2), 1) - boxRatio := divide(divideBy(volume, float64(100000000)), subtract(high, low)) - emv := Sma(period, divide(distanceMoved, boxRatio)) - return emv -} - -// The default Ease of Movement with the default period of 14. -func DefaultEaseOfMovement(high, low, volume []float64) []float64 { - return EaseOfMovement(14, high, low, volume) -} - -// The Volume Price Trend (VPT) provides a correlation between the -// volume and the price. -// -// VPT = Previous VPT + (Volume * (Current Closing - Previous Closing) / Previous Closing) -// -// Returns volume price trend values. -func VolumePriceTrend(closing, volume []float64) []float64 { - previousClosing := shiftRightAndFillBy(1, closing[0], closing) - vpt := multiply(volume, divide(subtract(closing, previousClosing), previousClosing)) - return Sum(len(vpt), vpt) -} - -// The Volume Weighted Average Price (VWAP) provides the average price -// the asset has traded. -// -// VWAP = Sum(Closing * Volume) / Sum(Volume) -// -// Returns vwap values. -func VolumeWeightedAveragePrice(period int, closing, volume []float64) []float64 { - return divide(Sum(period, multiply(closing, volume)), Sum(period, volume)) -} - -// Default volume weighted average price with period of 14. -func DefaultVolumeWeightedAveragePrice(closing, volume []float64) []float64 { - return VolumeWeightedAveragePrice(14, closing, volume) -} - -// The Negative Volume Index (NVI) is 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) -// -// Returns nvi values. -func NegativeVolumeIndex(closing, volume []float64) []float64 { - if len(closing) != len(volume) { - panic("not all same size") - } - - nvi := make([]float64, len(closing)) - - for i := 0; i < len(nvi); i++ { - if i == 0 { - nvi[i] = NVI_STARTING_VALUE - } else if volume[i-1] < volume[i] { - nvi[i] = nvi[i-1] - } else { - nvi[i] = nvi[i-1] + (((closing[i] - closing[i-1]) / closing[i-1]) * nvi[i-1]) - } - } - - return nvi -} - -// 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(high, low, closing, volume []float64) []float64 { - moneyFlowMultiplier := divide( - subtract(subtract(closing, low), subtract(high, closing)), - subtract(high, low)) - - moneyFlowVolume := multiply(moneyFlowMultiplier, volume) - - cmf := divide( - Sum(CMF_DEFAULT_PERIOD, moneyFlowVolume), - Sum(CMF_DEFAULT_PERIOD, volume)) - - return cmf -} diff --git a/volume_indicators.md b/volume_indicators.md deleted file mode 100644 index e37114a..0000000 --- a/volume_indicators.md +++ /dev/null @@ -1,172 +0,0 @@ -### Volume Indicators - -Volumne indicators measure the strength of a trend based the volume. - -- [Accumulation/Distribution (A/D)](#accumulationdistribution-ad) -- [Chaikin Money Flow (CMF)](#chaikin-money-flow-cmf) -- [Ease of Movement (EMV)](#ease-of-movement-emv) -- [Force Index (FI)](#force-index-fi) -- [Money Flow Index (MFI)](#money-flow-index-mfi) -- [Negative Volume Index (NVI)](#negative-volume-index-nvi) -- [On-Balance Volume (OBV)](#on-balance-volume-obv) -- [Volume Price Trend (VPT)](#volume-price-trend-vpt) -- [Volume Weighted Average Price (VWAP)](#volume-weighted-average-price-vwap) - -#### Accumulation/Distribution (A/D) - -The [AccumulationDistribution](https://pkg.go.dev/github.com/cinar/indicator#AccumulationDistribution) is a cumulative indicator that uses volume and price to assess whether a stock is being accumulated or distributed. - -The Accumulation/Distribution seeks to identify divergences between the stock price and the volume flow. - -``` -MFM = ((Closing - Low) - (High - Closing)) / (High - Low) -MFV = MFM * Period Volume -AD = Previous AD + CMFV -``` - -Based on [Accumulation/Distribution Indicator (A/D)](https://www.investopedia.com/terms/a/accumulationdistribution.asp). - -```golang -ad := indicator.AccumulationDistribution(high, low, closing, volume) -``` - -#### Chaikin Money Flow (CMF) - -The [ChaikinMoneyFlow](https://pkg.go.dev/github.com/cinar/indicator#ChaikinMoneyFlow) 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) -``` - -```Golang -cmf := indicator.ChaikinMoneyFlow(high, low, closing, volume) -``` - -#### Ease of Movement (EMV) - -The [EaseOfMovement](https://pkg.go.dev/github.com/cinar/indicator#EaseOfMovement) is a volume based oscillator measuring the ease of price movement. - -``` -Distance Moved = ((High + Low) / 2) - ((Priod High + Prior Low) /2) -Box Ratio = ((Volume / 100000000) / (High - Low)) -EMV(1) = Distance Moved / Box Ratio -EMV(14) = SMA(14, EMV(1)) -``` - -```Golang -emv := indicator.EaseOfMovement(period, high, low, volume) -``` - -The [DefaultEaseOfMovement](https://pkg.go.dev/github.com/cinar/indicator#DefaultEaseOfMovement) functio uses the default period of 14. - -```Golang -emv := indicator.DefaultEaseOfMovement(high, low, volume) -``` - -#### Force Index (FI) - -The [ForceIndex](https://pkg.go.dev/github.com/cinar/indicator#ForceIndex) uses the closing price and the volume to assess the power behind a move and identify turning points. - -``` -Force Index = EMA(period, (Current - Previous) * Volume) -``` - -```Golang -fi := indicator.ForceIndex(period, closing, volume) -``` - -The [DefaultForceIndex](https://pkg.go.dev/github.com/cinar/indicator#DefaultForceIndex) function uses the default period of 13. - -```Golang -fi := DefaultForceIndex(closing, volume) -``` - -#### Money Flow Index (MFI) - -The [MoneyFlowIndex](https://pkg.go.dev/github.com/cinar/indicator#MoneyFlowIndex) function analyzes both the closing price and the volume to measure to identify overbought and oversold states. It is similar to the Relative Strength Index (RSI), but it also uses the volume. - -``` -Raw Money Flow = Typical Price * Volume -Money Ratio = Positive Money Flow / Negative Money Flow -Money Flow Index = 100 - (100 / (1 + Money Ratio)) -``` - -```Golang -result := indicator.MoneyFlowIndex(period, high, low, closing, volume) -``` - -The [DefaultMoneyFlowIndex](https://pkg.go.dev/github.com/cinar/indicator#DefaultMoneyFlowIndex) function uses the default period of 14. - -#### Negative Volume Index (NVI) - -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) -``` - -```Golang -result := indicator.NegativeVolumeIndex(closing, volume) -``` - -#### On-Balance Volume (OBV) - -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. - -``` - volume, if Closing > Closing-Prev -OBV = OBV-Prev + 0, if Closing = Closing-Prev - -volume, if Closing < Closing-Prev -``` - -```Golang -result := indicator.Obv(closing, volume) -``` - -#### Volume Price Trend (VPT) - -The [VolumePriceTrend](https://pkg.go.dev/github.com/cinar/indicator#VolumePriceTrend) provides a correlation between the volume and the price. - -``` -VPT = Previous VPT + (Volume * (Current Closing - Previous Closing) / Previous Closing) -``` - -```Golang -result := indicator.VolumePriceTrend(closing, volume) -``` - -#### Volume Weighted Average Price (VWAP) - -The [VolumeWeightedAveragePrice](https://pkg.go.dev/github.com/cinar/indicator#VolumeWeightedAveragePrice) provides the average price the asset has traded. - -``` -VWAP = Sum(Closing * Volume) / Sum(Volume) -``` - -```Golang -result := indicator.VolumeWeightedAveragePrice(period, closing, volume) -``` - -The [DefaultVolumeWeightedAveragePrice](https://pkg.go.dev/github.com/cinar/indicator#DefaultVolumeWeightedAveragePrice) function uses the default period of 14. - -```Golang -result := indicator.DefaultVolumeWeightedAveragePrice(closing, volume) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License. diff --git a/volume_indicators_test.go b/volume_indicators_test.go deleted file mode 100644 index 153e2e2..0000000 --- a/volume_indicators_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -import "testing" - -func TestMoneyFlowIndex(t *testing.T) { - high := []float64{10, 9, 12, 14, 12} - low := []float64{6, 7, 9, 12, 10} - closing := []float64{9, 11, 7, 10, 8} - volume := []float64{100, 110, 80, 120, 90} - expected := []float64{100, 100, 57.01, 65.85, 61.54} - period := 2 - - actual := roundDigitsAll(MoneyFlowIndex(period, high, low, closing, volume), 2) - testEquals(t, actual, expected) -} - -func TestMoneyFlowIndex2(t *testing.T) { - high := []float64{2390.9, 2386.3, 2395.33, 2399.0, 2402.46, 2401.15, 2421.98, 2430.31, 2426.33, 2434.93, 2470.83, 2483.36, 2467.19, 2450.72} - low := []float64{2373.15, 2370.0, 2380.77, 2384.28, 2387.46, 2385.02, 2383.18, 2408.39, 2410.59, 2420.89, 2428.92, 2456.77, 2437.65, 2440.87} - closing := []float64{2373.39, 2382.47, 2394.4, 2387.51, 2395.64, 2389.47, 2410.24, 2425.37, 2422.33, 2430.29, 2465.76, 2466.27, 2440.07, 2445.85} - volume := []float64{1621, 1387, 1444, 1298, 1629, 1598, 2311, 2934, 2128, 1823, 5078, 6693, 3960, 1927} - expected := []float64{100, 53.884998, 68.888204, 53.300054, 63.645476, 52.296425, 62.119198, 70.011701, 60.826077, 54.659774, 64.728457, 72.749002, 64.185176, 60.710993} - period := 14 - - actual := roundDigitsAll(MoneyFlowIndex(period, high, low, closing, volume), 6) - testEquals(t, actual, expected) -} - -func TestForceIndex(t *testing.T) { - closing := []float64{9, 11, 7, 10, 8} - volume := []float64{100, 110, 80, 120, 90} - expected := []float64{900, 220, -320, 360, -180} - period := 1 - - actual := roundDigitsAll(ForceIndex(period, closing, volume), 2) - testEquals(t, actual, expected) -} - -func TestDefaultForceIndex(t *testing.T) { - closing := []float64{9, 11, 7, 10, 8} - volume := []float64{100, 110, 80, 120, 90} - expected := []float64{900, 802.86, 642.45, 602.1, 490.37} - - actual := roundDigitsAll(DefaultForceIndex(closing, volume), 2) - testEquals(t, actual, expected) -} - -func TestDefaultEaseOfMovement(t *testing.T) { - high := []float64{10, 9, 12, 14, 12} - low := []float64{6, 7, 9, 12, 10} - volume := []float64{100, 110, 80, 120, 90} - expected := []float64{32000000, 16000000, 13791666.67, 11385416.67, 8219444.44} - - actual := roundDigitsAll(DefaultEaseOfMovement(high, low, volume), 2) - testEquals(t, actual, expected) -} - -func TestVolumePriceTrend(t *testing.T) { - closing := []float64{9, 11, 7, 10, 8} - volume := []float64{100, 110, 80, 120, 90} - expected := []float64{0, 24.44, -4.65, 46.78, 28.78} - - actual := roundDigitsAll(VolumePriceTrend(closing, volume), 2) - testEquals(t, actual, expected) -} - -func TestVolumeWeightedAveragePrice(t *testing.T) { - closing := []float64{9, 11, 7, 10, 8} - volume := []float64{100, 110, 80, 120, 90} - period := 2 - expected := []float64{9, 10.05, 9.32, 8.8, 9.14} - - actual := roundDigitsAll(VolumeWeightedAveragePrice(period, closing, volume), 2) - testEquals(t, actual, expected) -} - -func TestDefaultVolumeWeightedAveragePrice(t *testing.T) { - closing := []float64{9, 11, 7, 10, 8} - volume := []float64{100, 110, 80, 120, 90} - expected := []float64{9, 10.05, 9.21, 9.44, 9.18} - - actual := roundDigitsAll(DefaultVolumeWeightedAveragePrice(closing, volume), 2) - testEquals(t, actual, expected) -} - -func TestNegativeVolumeIndex(t *testing.T) { - closing := []float64{9, 11, 7, 10, 8} - volume := []float64{100, 110, 80, 120, 90} - expected := []float64{1000, 1000, 636.36, 636.36, 509.09} - - actual := roundDigitsAll(NegativeVolumeIndex(closing, volume), 2) - testEquals(t, actual, expected) -} - -func TestChaikinMoneyFlow(t *testing.T) { - high := []float64{10, 9, 12, 14, 12} - low := []float64{6, 7, 9, 12, 10} - volume := []float64{100, 110, 80, 120, 90} - closing := []float64{9, 11, 7, 10, 8} - expected := []float64{0.5, 1.81, 0.67, -0.41, -0.87} - - actual := roundDigitsAll(ChaikinMoneyFlow(high, low, closing, volume), 2) - testEquals(t, actual, expected) -} diff --git a/volume_strategies.go b/volume_strategies.go deleted file mode 100644 index 7115504..0000000 --- a/volume_strategies.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) 2021 Onur Cinar. All Rights Reserved. -// The source code is provided under MIT License. -// -// https://github.com/cinar/indicator - -package indicator - -// Money flow index strategy. -func MoneyFlowIndexStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - moneyFlowIndex := DefaultMoneyFlowIndex( - asset.High, - asset.Low, - asset.Closing, - asset.Volume) - - for i := 0; i < len(actions); i++ { - if moneyFlowIndex[i] >= 80 { - actions[i] = SELL - } else { - actions[i] = BUY - } - } - - return actions -} - -// Force index strategy function. -func ForceIndexStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - forceIndex := DefaultForceIndex(asset.Closing, asset.Volume) - - for i := 0; i < len(actions); i++ { - if forceIndex[i] > 0 { - actions[i] = BUY - } else if forceIndex[i] < 0 { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Ease of movement strategy. -func EaseOfMovementStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - emv := DefaultEaseOfMovement(asset.High, asset.Low, asset.Volume) - - for i := 0; i < len(actions); i++ { - if emv[i] > 0 { - actions[i] = BUY - } else if emv[i] < 0 { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Volume weighted average price strategy function. -func VolumeWeightedAveragePriceStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - vwap := DefaultVolumeWeightedAveragePrice(asset.Closing, asset.Volume) - - for i := 0; i < len(actions); i++ { - if vwap[i] > asset.Closing[i] { - actions[i] = BUY - } else if vwap[i] < asset.Closing[i] { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Negative volume index strategy. -func NegativeVolumeIndexStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - nvi := NegativeVolumeIndex(asset.Closing, asset.Volume) - nvi255 := Ema(255, nvi) - - for i := 0; i < len(actions); i++ { - if nvi[i] < nvi255[i] { - actions[i] = BUY - } else if nvi[i] > nvi255[i] { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} - -// Chaikin money flow strategy. -func ChaikinMoneyFlowStrategy(asset *Asset) []Action { - actions := make([]Action, len(asset.Date)) - - cmf := ChaikinMoneyFlow( - asset.High, - asset.Low, - asset.Closing, - asset.Volume) - - for i := 0; i < len(actions); i++ { - if cmf[i] < 0 { - actions[i] = BUY - } else if cmf[i] > 0 { - actions[i] = SELL - } else { - actions[i] = HOLD - } - } - - return actions -} diff --git a/volume_strategies.md b/volume_strategies.md deleted file mode 100644 index 4c6ad7f..0000000 --- a/volume_strategies.md +++ /dev/null @@ -1,68 +0,0 @@ -### Volume Strategies - -Volume strategies generate signals based on a volume indicator. - -- [Chaikin Money Flow Strategy](#chaikin-money-flow-strategy) -- [Ease of Movement Strategy](#ease-of-movement-strategy) -- [Force Index Strategy](#force-index-strategy) -- [Money Flow Index Strategy](#money-flow-index-strategy) -- [Negative Volume Index Strategy](#negative-volume-index-strategy) -- [Volume Weighted Average Price Strategy](#volume-weighted-average-price-strategy) - -#### Chaikin Money Flow Strategy - -The [ChaikinMoneyFlowStrategy](https://pkg.go.dev/github.com/cinar/indicator#ChaikinMoneyFlowStrategy) uses the _cmf_ values that are generated by the [Chaikin Money Flow (CMF)](volume_indicators.md#chaikin-money-flow-cmf) indicator function to provide a _BUY_ action when _cmf_ is less than zero, a _SELL_ action when _cmf_ is greather than zero, a _HOLD_ action otherwise. - -```Golang -actions := indicator.ChaikinMoneyFlowStrategy(asset) -``` - -#### Ease of Movement Strategy - -The [EaseOfMovementStrategy](https://pkg.go.dev/github.com/cinar/indicator#EaseOfMovementStrategy) uses the _emv_ values that are generated by the [Ease of Movement (EMV)](volume_indicators.md#ease-of-movement-emv) indicator function to provide a _BUY_ action when _emv_ is greather than zero, and a _SELL_ action when _emv_ is less than zero, a _HOLD_ action otherwise. - -```Golang -actions := indicator.EaseOfMovementStrategy(asset) -``` - -#### Force Index Strategy - -The [ForceIndexStrategy](https://pkg.go.dev/github.com/cinar/indicator#ForceIndexStrategy) uses the _fi_ values that are generated by the [Force Index (FI)](volume_indicators.md#force-index-fi) indicator function to provide a _BUY_ action when _fi_ is greather than zero, and a _SELL_ action when _fi_ is less than zero, a _HOLD_ action otherwise. - -```Golang -actions := indicator.ForceIndexStrategy(asset) -``` - -#### Money Flow Index Strategy - -The [MoneyFlowIndexStrategy](https://pkg.go.dev/github.com/cinar/indicator#MoneyFlowIndexStrategy) uses the _mfi_ values that are generated by the [Money Flow Index (MFI)](volume_indicators.md#money-flow-index-mfi) indicator function to provide a _SELL_ action when _mfi_ is greather than or equal to 80, and a _BUY_ action when _mfi_ is less than or equal to 20. - -```Golang -actions := indicator.MoneyFlowIndexStrategy(asset) -``` - -#### Negative Volume Index Strategy - -The [NegativeVolumeIndexStrategy](https://pkg.go.dev/github.com/cinar/indicator#NegativeVolumeIndexStrategy) uses the _nvi_ values that are generated by the [Negative Volume Index (NVI)](volume_indicators.md#negative-volume-index-nvi) indicator function to provide a _BUY_ action when _nvi_ is less than its 255-period EMA, and a _SELL_ action when it is greather than its 255-period EMA, otherwise a _HOLD_ action. - -```Golang -actions := indicator.NegativeVolumeIndexStrategy(asset) -``` - -#### Volume Weighted Average Price Strategy - -The [VolumeWeightedAveragePriceStrategy]([./volumeWeightedAveragePriceStrategy.ts](https://pkg.go.dev/github.com/cinar/indicator#VolumeWeightedAveragePriceStrategy)) uses the values that are generated by the [Volume Weighted Average Price (VWAP)](volume_indicators.md#volume-weighted-average-price-vwap) indicator function to provide a _BUY_ action when the _closing_ is below the _VWAP_, and a _SELL_ action when the _closing_ is below the _VWAP_, a _HOLD_ action otherwise. - -```Golang -actions := indicator.VolumeWeightedAveragePriceStrategy(asset) -``` - -## Disclaimer - -The information provided on this project is strictly for informational purposes and is not to be construed as advice or solicitation to buy or sell any security. - -## License - -Copyright (c) 2021 Onur Cinar. All Rights Reserved. - -The source code is provided under MIT License.