From 3be9665c804be406d98acfe2982259f3607cf4ca Mon Sep 17 00:00:00 2001 From: Onur Cinar Date: Fri, 10 Sep 2021 21:13:05 +0000 Subject: [PATCH] Buy and hold, trend, MACD, RSI, Bollinger Bands, Awesome Oscillator, and Williams R strategies are added. --- README.md | 141 ++++++++++++++++++++++++++++++++- go.mod | 2 +- strategy.go | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 360 insertions(+), 3 deletions(-) create mode 100644 strategy.go diff --git a/README.md b/README.md index 6e1fbc4..d742ecc 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,11 @@ # Indicator Go -Indicator is a Golang module providing various stock technical analysis indicators for trading. The following list of indicators are currently supported by this package: +Indicator is a Golang module providing various stock technical analysis indicators, and strategies for trading. + +## Indicators Provided + +The following list of indicators are currently supported by this package: - [Simple Moving Average (SMA)](#simple-moving-average-sma) - [Moving Standard Deviation (Std)](#moving-standard-deviation-std) @@ -27,6 +31,19 @@ Indicator is a Golang module providing various stock technical analysis indicato - [Acceleration Bands](#acceleration-bands) - [Accumulation/Distribution (A/D)](#accumulationdistribution-ad) +## Strategies Provided + +The following list of strategies are currently supported by this package: + +- [Buy and Hold Strategy](#buy-and-hold-strategy) +- [Trend Strategy](#trend-strategy) +- [MACD Strategy](#macd-strategy) +- [RSI Strategy](#rsi-strategy) +- [MACD and RSI Strategy](#macd-and-rsi-strategy) +- [Bollinger Bands Strategy](#bollinger-bands-strategy) +- [Awesome Oscillator Strategy](#awesome-oscillator-strategy) +- [Williams R Strategy](#williams-r-strategy) + ## Usage Install package. @@ -39,7 +56,7 @@ Import indicator. ```Golang import ( - "indicator" + "github.com/cinar/indicator" ) ``` @@ -328,6 +345,126 @@ Based on [Accumulation/Distribution Indicator (A/D)](https://www.investopedia.co ad := indicator.AccumulationDistribution(high, low, close, ``` +### Strategies + +The strategies are where the results from one or more indicators gets combined to produce a recommended action. + +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 + Open []float64 + Close []float64 + High []float64 + Low []float64 + Volume []int64 +} +``` + +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 +``` + +The following [Action](https://pkg.go.dev/github.com/cinar/indicator#Action) values are currently provided. + +```golang +type Action int + +const ( + HOLD Action = iota + BUY + SELL +) +``` + +#### 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) +``` + +#### 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) +``` + +#### 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) +``` + +#### 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) +``` + +#### MACD and RSI Strategy + +The [MacdAndRsiStrategy](https://pkg.go.dev/github.com/cinar/indicator#MacdAndRsiStrategy) function uses the actions generated by the [MacdStrategy](https://pkg.go.dev/github.com/cinar/indicator#MacdStrategy) and the [DefaultRsiStrategy](https://pkg.go.dev/github.com/cinar/indicator#DefaultRsiStrategy) to provide BUY and SELL actions. + +```golang +actions := indicator.MacdAndRsiStrategy(asset) +``` + +#### 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) +``` + +#### 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) +``` + +#### 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) +``` + ## License The source code is provided under MIT License. diff --git a/go.mod b/go.mod index c714fca..22cb308 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/cinar/indicator -go 1.16 +go 1.14 diff --git a/strategy.go b/strategy.go new file mode 100644 index 0000000..1de3348 --- /dev/null +++ b/strategy.go @@ -0,0 +1,220 @@ +package indicator + +import ( + "log" + "time" +) + +// Strategy action. +type Action int + +const ( + HOLD Action = iota + BUY + SELL +) + +// Asset values. +type Asset struct { + Date []time.Time + Open []float64 + Close []float64 + High []float64 + Low []float64 + Volume []int64 +} + +// 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 +} + +// Trend strategy. Buy when trending up for count times, +// sell when trending down for count times. +func TrendStrategy(asset Asset, count int) []Action { + if count < 1 { + log.Fatal("count cannot be less than 1") + } + + actions := make([]Action, len(asset.Date)) + + if len(actions) == 0 { + return actions + } + + var lastClose float64 = asset.Close[0] + var trendCount int + var trendUp bool + + actions[0] = HOLD + + for i := 1; i < len(actions); i++ { + close := asset.Close[i] + + if trendUp && (lastClose <= close) { + trendCount++ + } else if !trendUp && (lastClose >= close) { + trendCount++ + } else { + trendUp = !trendUp + trendCount = 1 + } + + lastClose = close + + 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 int) StrategyFunction { + return func(asset Asset) []Action { + return TrendStrategy(asset, count) + } +} + +// MACD strategy. +func MacdStrategy(asset Asset) []Action { + actions := make([]Action, len(asset.Date)) + + macd, signal := Macd(asset.Close) + + 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 +} + +// 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.Close) + + 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) + } +} + +// MACD and RSI strategy. +func MacdAndRsiStrategy(asset Asset) []Action { + actions := make([]Action, len(asset.Date)) + + macdActions := MacdStrategy(asset) + rsiActions := DefaultRsiStrategy(asset) + + for i := 0; i < len(actions); i++ { + if macdActions[i] == rsiActions[i] { + actions[i] = macdActions[i] + } else { + actions[i] = HOLD + } + } + + return actions +} + +// Bollinger bands strategy function. +func BollingerBandsStrategy(asset Asset) []Action { + actions := make([]Action, len(asset.Date)) + + _, upperBand, lowerBand := BollingerBands(asset.Close) + + for i := 0; i < len(actions); i++ { + if asset.Close[i] > upperBand[i] { + actions[i] = SELL + } else if asset.Close[i] < lowerBand[i] { + actions[i] = BUY + } else { + actions[i] = HOLD + } + } + + return actions +} + +// 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 +} + +// Williams R strategy function. +func WilliamsRStrategy(asset Asset) []Action { + actions := make([]Action, len(asset.Date)) + + wr := WilliamsR(asset.Low, asset.High, asset.Close) + + 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 +}