From 53c4214340e7e46dc1e546e68d779129928d4b6d Mon Sep 17 00:00:00 2001 From: Onur Cinar Date: Fri, 29 Dec 2023 14:38:38 -0800 Subject: [PATCH] Trend strategies are moved to their dedicated module. --- README.md | 18 +- cmd/indicator-backtest/main.go | 2 + strategy/README.md | 545 --------------- strategy/backtest.go | 10 - strategy/backtest_test.go | 3 +- strategy/trend/README.md | 641 ++++++++++++++++++ strategy/{ => trend}/apo_strategy.go | 21 +- strategy/{ => trend}/apo_strategy_test.go | 11 +- strategy/{ => trend}/aroon_strategy.go | 25 +- strategy/{ => trend}/aroon_strategy_test.go | 11 +- strategy/{ => trend}/bop_strategy.go | 19 +- strategy/{ => trend}/bop_strategy_test.go | 11 +- strategy/{ => trend}/dema_strategy.go | 25 +- strategy/{ => trend}/dema_strategy_test.go | 11 +- strategy/{ => trend}/kdj_strategy.go | 25 +- strategy/{ => trend}/kdj_strategy_test.go | 11 +- strategy/{ => trend}/macd_strategy.go | 21 +- strategy/{ => trend}/macd_strategy_test.go | 11 +- strategy/{ => trend}/qstick_strategy.go | 21 +- strategy/{ => trend}/qstick_strategy_test.go | 11 +- .../{ => trend}/testdata/apo_strategy.csv | 0 .../{ => trend}/testdata/aroon_strategy.csv | 0 .../{ => trend}/testdata/bop_strategy.csv | 0 .../{ => trend}/testdata/dema_strategy.csv | 0 .../{ => trend}/testdata/kdj_strategy.csv | 0 .../{ => trend}/testdata/macd_strategy.csv | 0 .../{ => trend}/testdata/qstick_strategy.csv | 0 .../{ => trend}/testdata/trima_strategy.csv | 0 .../{ => trend}/testdata/vwma_strategy.csv | 0 strategy/trend/trend.go | 18 + strategy/{ => trend}/trima_strategy.go | 25 +- strategy/{ => trend}/trima_strategy_test.go | 11 +- strategy/{ => trend}/vwma_strategy.go | 25 +- strategy/{ => trend}/vwma_strategy_test.go | 11 +- 34 files changed, 840 insertions(+), 703 deletions(-) create mode 100644 strategy/trend/README.md rename strategy/{ => trend}/apo_strategy.go (87%) rename strategy/{ => trend}/apo_strategy_test.go (86%) rename strategy/{ => trend}/aroon_strategy.go (85%) rename strategy/{ => trend}/aroon_strategy_test.go (85%) rename strategy/{ => trend}/bop_strategy.go (86%) rename strategy/{ => trend}/bop_strategy_test.go (86%) rename strategy/{ => trend}/dema_strategy.go (87%) rename strategy/{ => trend}/dema_strategy_test.go (85%) rename strategy/{ => trend}/kdj_strategy.go (86%) rename strategy/{ => trend}/kdj_strategy_test.go (86%) rename strategy/{ => trend}/macd_strategy.go (88%) rename strategy/{ => trend}/macd_strategy_test.go (85%) rename strategy/{ => trend}/qstick_strategy.go (89%) rename strategy/{ => trend}/qstick_strategy_test.go (85%) rename strategy/{ => trend}/testdata/apo_strategy.csv (100%) rename strategy/{ => trend}/testdata/aroon_strategy.csv (100%) rename strategy/{ => trend}/testdata/bop_strategy.csv (100%) rename strategy/{ => trend}/testdata/dema_strategy.csv (100%) rename strategy/{ => trend}/testdata/kdj_strategy.csv (100%) rename strategy/{ => trend}/testdata/macd_strategy.csv (100%) rename strategy/{ => trend}/testdata/qstick_strategy.csv (100%) rename strategy/{ => trend}/testdata/trima_strategy.csv (100%) rename strategy/{ => trend}/testdata/vwma_strategy.csv (100%) rename strategy/{ => trend}/trima_strategy.go (87%) rename strategy/{ => trend}/trima_strategy_test.go (85%) rename strategy/{ => trend}/vwma_strategy.go (86%) rename strategy/{ => trend}/vwma_strategy_test.go (85%) diff --git a/README.md b/README.md index ba9fcb0..cee2d2e 100644 --- a/README.md +++ b/README.md @@ -104,17 +104,17 @@ The following list of strategies are currently supported by this package: ### 📈 Trend Strategies -- [Absolute Price Oscillator (APO) Strategy](strategy/README.md#type-apostrategy) -- [Aroon Strategy](strategy/README.md#type-aroonstrategy) -- [Balance of Power (BoP) Strategy](strategy/README.md#type-bopstrategy) -- [Double Exponential Moving Average (DEMA) Strategy](strategy/README.md#type-demastrategy) +- [Absolute Price Oscillator (APO) Strategy](strategy/trend/README.md#type-apostrategy) +- [Aroon Strategy](strategy/trend/README.md#type-aroonstrategy) +- [Balance of Power (BoP) Strategy](strategy/trend/README.md#type-bopstrategy) +- [Double Exponential Moving Average (DEMA) Strategy](strategy/trend/README.md#type-demastrategy) - Chande Forecast Oscillator Strategy -- [Random Index (KDJ) Strategy](strategy/README.md#type-kdjstrategy) -- [Moving Average Convergence Divergence (MACD) Strategy](strategy/README.md#type-macdstrategy) -- [Qstick Strategy](strategy/README.md#type-qstickstrategy) -- [Triangular Moving Average (TRIMA) Strategy](strategy/README.md#type-trimastrategy) +- [Random Index (KDJ) Strategy](strategy/trend/README.md#type-kdjstrategy) +- [Moving Average Convergence Divergence (MACD) Strategy](strategy/trend/README.md#type-macdstrategy) +- [Qstick Strategy](strategy/trend/README.md#type-qstickstrategy) +- [Triangular Moving Average (TRIMA) Strategy](strategy/trend/README.md#type-trimastrategy) - [Triple Exponential Average (TRIX) Strategy](strategy/trend/README.md#type-trixstrategy) -- [Volume Weighted Moving Average (VWMA) Strategy](strategy/README.md#type-vwmastrategy) +- [Volume Weighted Moving Average (VWMA) Strategy](strategy/trend/README.md#type-vwmastrategy) ### 🚀 Momentum Strategies diff --git a/cmd/indicator-backtest/main.go b/cmd/indicator-backtest/main.go index 7837e7b..028e930 100644 --- a/cmd/indicator-backtest/main.go +++ b/cmd/indicator-backtest/main.go @@ -13,6 +13,7 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func main() { @@ -36,6 +37,7 @@ func main() { backtest := strategy.NewBacktest(repository, outputDir) backtest.Workers = workers backtest.Names = append(backtest.Names, flag.Args()...) + backtest.Strategies = append(backtest.Strategies, trend.AllStrategies()...) err := backtest.Run() if err != nil { diff --git a/strategy/README.md b/strategy/README.md index c7e3f7b..74059d2 100644 --- a/strategy/README.md +++ b/strategy/README.md @@ -24,7 +24,6 @@ The information provided on this project is strictly for informational purposes ## Index -- [Constants](<#constants>) - [func ActionsToAnnotations\(ac \<\-chan Action\) \<\-chan string](<#ActionsToAnnotations>) - [func CheckResults\(results \<\-chan \*Result, actions \<\-chan Action, outcomes \<\-chan float64\) error](<#CheckResults>) - [func ComputeWithOutcome\(s Strategy, c \<\-chan \*asset.Snapshot\) \(\<\-chan Action, \<\-chan float64\)](<#ComputeWithOutcome>) @@ -33,98 +32,18 @@ The information provided on this project is strictly for informational purposes - [func SpreadActions\(ac \<\-chan Action\) \<\-chan Action](<#SpreadActions>) - [type Action](<#Action>) - [func \(a Action\) Annotation\(\) string](<#Action.Annotation>) -- [type ApoStrategy](<#ApoStrategy>) - - [func NewApoStrategy\(\) \*ApoStrategy](<#NewApoStrategy>) - - [func \(a \*ApoStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan Action](<#ApoStrategy.Compute>) - - [func \(\*ApoStrategy\) Name\(\) string](<#ApoStrategy.Name>) - - [func \(a \*ApoStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#ApoStrategy.Report>) -- [type AroonStrategy](<#AroonStrategy>) - - [func NewAroonStrategy\(\) \*AroonStrategy](<#NewAroonStrategy>) - - [func \(a \*AroonStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan Action](<#AroonStrategy.Compute>) - - [func \(\*AroonStrategy\) Name\(\) string](<#AroonStrategy.Name>) - - [func \(a \*AroonStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#AroonStrategy.Report>) - [type Backtest](<#Backtest>) - [func NewBacktest\(repository asset.Repository, outputDir string\) \*Backtest](<#NewBacktest>) - [func \(b \*Backtest\) Run\(\) error](<#Backtest.Run>) -- [type BopStrategy](<#BopStrategy>) - - [func NewBopStrategy\(\) \*BopStrategy](<#NewBopStrategy>) - - [func \(b \*BopStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan Action](<#BopStrategy.Compute>) - - [func \(\*BopStrategy\) Name\(\) string](<#BopStrategy.Name>) - - [func \(b \*BopStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#BopStrategy.Report>) - [type BuyAndHoldStrategy](<#BuyAndHoldStrategy>) - [func NewBuyAndHoldStrategy\(\) \*BuyAndHoldStrategy](<#NewBuyAndHoldStrategy>) - [func \(\*BuyAndHoldStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan Action](<#BuyAndHoldStrategy.Compute>) - [func \(\*BuyAndHoldStrategy\) Name\(\) string](<#BuyAndHoldStrategy.Name>) - [func \(b \*BuyAndHoldStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#BuyAndHoldStrategy.Report>) -- [type DemaStrategy](<#DemaStrategy>) - - [func NewDemaStrategy\(\) \*DemaStrategy](<#NewDemaStrategy>) - - [func \(d \*DemaStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan Action](<#DemaStrategy.Compute>) - - [func \(\*DemaStrategy\) Name\(\) string](<#DemaStrategy.Name>) - - [func \(d \*DemaStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#DemaStrategy.Report>) -- [type KdjStrategy](<#KdjStrategy>) - - [func NewKdjStrategy\(\) \*KdjStrategy](<#NewKdjStrategy>) - - [func \(kdj \*KdjStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan Action](<#KdjStrategy.Compute>) - - [func \(\*KdjStrategy\) Name\(\) string](<#KdjStrategy.Name>) - - [func \(kdj \*KdjStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#KdjStrategy.Report>) -- [type MacdStrategy](<#MacdStrategy>) - - [func NewMacdStrategy\(\) \*MacdStrategy](<#NewMacdStrategy>) - - [func \(m \*MacdStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan Action](<#MacdStrategy.Compute>) - - [func \(\*MacdStrategy\) Name\(\) string](<#MacdStrategy.Name>) - - [func \(m \*MacdStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#MacdStrategy.Report>) -- [type QstickStrategy](<#QstickStrategy>) - - [func NewQstickStrategy\(\) \*QstickStrategy](<#NewQstickStrategy>) - - [func \(q \*QstickStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan Action](<#QstickStrategy.Compute>) - - [func \(\*QstickStrategy\) Name\(\) string](<#QstickStrategy.Name>) - - [func \(q \*QstickStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#QstickStrategy.Report>) - [type Result](<#Result>) - [type Strategy](<#Strategy>) -- [type TrimaStrategy](<#TrimaStrategy>) - - [func NewTrimaStrategy\(\) \*TrimaStrategy](<#NewTrimaStrategy>) - - [func \(t \*TrimaStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan Action](<#TrimaStrategy.Compute>) - - [func \(\*TrimaStrategy\) Name\(\) string](<#TrimaStrategy.Name>) - - [func \(t \*TrimaStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#TrimaStrategy.Report>) -- [type VwmaStrategy](<#VwmaStrategy>) - - [func NewVwmaStrategy\(\) \*VwmaStrategy](<#NewVwmaStrategy>) - - [func \(v \*VwmaStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan Action](<#VwmaStrategy.Compute>) - - [func \(\*VwmaStrategy\) Name\(\) string](<#VwmaStrategy.Name>) - - [func \(v \*VwmaStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#VwmaStrategy.Report>) -## Constants - - - -```go -const ( - // DefaultDemaStrategyPeriod1 is the first DEMA period. - DefaultDemaStrategyPeriod1 = 5 - - // DefaultDemaStrategyPeriod2 is the second DEMA period. - DefaultDemaStrategyPeriod2 = 35 -) -``` - - - -```go -const ( - // DefaultTrimaStrategyShortPeriod is the first TRIMA period. - DefaultTrimaStrategyShortPeriod = 20 - - // DefaultTrimaStrategyLongPeriod is the second TRIMA period. - DefaultTrimaStrategyLongPeriod = 50 -) -``` - - - -```go -const ( - // DefaultVwmaStrategyPeriod is the default VWMA period. - DefaultVwmaStrategyPeriod = 20 -) -``` - ## func [ActionsToAnnotations]() @@ -217,105 +136,6 @@ func (a Action) Annotation() string Annotation returns a single character string representing the recommended action. It returns "S" for Sell, "B" for Buy, and an empty string for Hold. - -## type [ApoStrategy]() - -ApoStrategy represents the configuration parameters for calculating the APO strategy. 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. - -```go -type ApoStrategy struct { - // Apo represents the configuration parameters for calculating the - // Absolute Price Oscillator (APO). - Apo *trend.Apo[float64] -} -``` - - -### func [NewApoStrategy]() - -```go -func NewApoStrategy() *ApoStrategy -``` - -NewApoStrategy function initializes a new APO strategy instance with the default parameters. - - -### func \(\*ApoStrategy\) [Compute]() - -```go -func (a *ApoStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action -``` - -Compute processes the provided asset snapshots and generates a stream of actionable recommendations. - - -### func \(\*ApoStrategy\) [Name]() - -```go -func (*ApoStrategy) Name() string -``` - -Name returns the name of the strategy. - - -### func \(\*ApoStrategy\) [Report]() - -```go -func (a *ApoStrategy) Report(c <-chan *asset.Snapshot) *helper.Report -``` - -Report processes the provided asset snapshots and generates a report annotated with the recommended actions. - - -## type [AroonStrategy]() - -AroonStrategy represents the configuration parameters for calculating the Aroon strategy. Aroon 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. - -```go -type AroonStrategy struct { - Strategy - - // Aroon represent the configuration for calculating the Aroon indicator. - Aroon *trend.Aroon[float64] -} -``` - - -### func [NewAroonStrategy]() - -```go -func NewAroonStrategy() *AroonStrategy -``` - -NewAroonStrategy function initializes a new Aroon strategy instance with the default parameters. - - -### func \(\*AroonStrategy\) [Compute]() - -```go -func (a *AroonStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action -``` - -Compute processes the provided asset snapshots and generates a stream of actionable recommendations. - - -### func \(\*AroonStrategy\) [Name]() - -```go -func (*AroonStrategy) Name() string -``` - -Name returns the name of the strategy. - - -### func \(\*AroonStrategy\) [Report]() - -```go -func (a *AroonStrategy) Report(c <-chan *asset.Snapshot) *helper.Report -``` - -Report processes the provided asset snapshots and generates a report annotated with the recommended actions. - ## type [Backtest]() @@ -354,57 +174,6 @@ func (b *Backtest) Run() error Run executes a comprehensive performance evaluation of the designated strategies, applied to a specified collection of assets. In the absence of explicitly defined assets, encompasses all assets within the repository. Likewise, in the absence of explicitly defined strategies, encompasses all the registered strategies. - -## type [BopStrategy]() - -BopStrategy 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. - -```go -type BopStrategy struct { - Strategy - - // Bop represents the configuration parameters for calculating the - // Balance of Power (BoP). - Bop *trend.Bop[float64] -} -``` - - -### func [NewBopStrategy]() - -```go -func NewBopStrategy() *BopStrategy -``` - -NewBopStrategy function initializes a new BoP strategy instance with the default parameters. - - -### func \(\*BopStrategy\) [Compute]() - -```go -func (b *BopStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action -``` - -Compute processes the provided asset snapshots and generates a stream of actionable recommendations. - - -### func \(\*BopStrategy\) [Name]() - -```go -func (*BopStrategy) Name() string -``` - -Name returns the name of the strategy. - - -### func \(\*BopStrategy\) [Report]() - -```go -func (b *BopStrategy) Report(c <-chan *asset.Snapshot) *helper.Report -``` - -Report processes the provided asset snapshots and generates a report annotated with the recommended actions. - ## type [BuyAndHoldStrategy]() @@ -452,214 +221,6 @@ func (b *BuyAndHoldStrategy) Report(c <-chan *asset.Snapshot) *helper.Report Report processes the provided asset snapshots and generates a report annotated with the recommended actions. - -## type [DemaStrategy]() - -DemaStrategy represents the configuration parameters for calculating the DEMA strategy. A bullish cross occurs when DEMA with 5 days period moves above DEMA with 35 days period. A bearish cross occurs when DEMA with 35 days period moves above DEMA With 5 days period. - -```go -type DemaStrategy struct { - Strategy - - // Dema1 represents the configuration parameters for - // calculating the first DEMA. - Dema1 *trend.Dema[float64] - - // Dema2 represents the configuration parameters for - // calculating the second DEMA. - Dema2 *trend.Dema[float64] -} -``` - - -### func [NewDemaStrategy]() - -```go -func NewDemaStrategy() *DemaStrategy -``` - -NewDemaStrategy function initializes a new DEMA strategy instance with the default parameters. - - -### func \(\*DemaStrategy\) [Compute]() - -```go -func (d *DemaStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action -``` - -Compute processes the provided asset snapshots and generates a stream of actionable recommendations. - - -### func \(\*DemaStrategy\) [Name]() - -```go -func (*DemaStrategy) Name() string -``` - -Name returns the name of the strategy. - - -### func \(\*DemaStrategy\) [Report]() - -```go -func (d *DemaStrategy) Report(c <-chan *asset.Snapshot) *helper.Report -``` - -Report processes the provided asset snapshots and generates a report annotated with the recommended actions. - - -## type [KdjStrategy]() - -KdjStrategy represents the configuration parameters for calculating the KDJ strategy. Generates BUY action when j value crosses above both k and d values. Generates SELL action when j value crosses below both k and d values. - -```go -type KdjStrategy struct { - Strategy - - // Kdj represents the configuration parameters for calculating the KDJ. - Kdj *trend.Kdj[float64] -} -``` - - -### func [NewKdjStrategy]() - -```go -func NewKdjStrategy() *KdjStrategy -``` - -NewKdjStrategy function initializes a new KDJ strategy instance. - - -### func \(\*KdjStrategy\) [Compute]() - -```go -func (kdj *KdjStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action -``` - -Compute processes the provided asset snapshots and generates a stream of actionable recommendations. - - -### func \(\*KdjStrategy\) [Name]() - -```go -func (*KdjStrategy) Name() string -``` - -Name returns the name of the strategy. - - -### func \(\*KdjStrategy\) [Report]() - -```go -func (kdj *KdjStrategy) Report(c <-chan *asset.Snapshot) *helper.Report -``` - -Report processes the provided asset snapshots and generates a report annotated with the recommended actions. - - -## type [MacdStrategy]() - -MacdStrategy represents the configuration parameters for calculating the MACD strategy. A MACD value crossing above the signal line suggests a bullish trend, while crossing below the signal line indicates a bearish trend. - -```go -type MacdStrategy struct { - Strategy - - // Macd represents the configuration parameters for calculating the - // Moving Average Convergence Divergence (MACD). - Macd *trend.Macd[float64] -} -``` - - -### func [NewMacdStrategy]() - -```go -func NewMacdStrategy() *MacdStrategy -``` - -NewMacdStrategy function initializes a new MACD strategy instance. - - -### func \(\*MacdStrategy\) [Compute]() - -```go -func (m *MacdStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action -``` - -Compute processes the provided asset snapshots and generates a stream of actionable recommendations. - - -### func \(\*MacdStrategy\) [Name]() - -```go -func (*MacdStrategy) Name() string -``` - -Name returns the name of the strategy. - - -### func \(\*MacdStrategy\) [Report]() - -```go -func (m *MacdStrategy) Report(c <-chan *asset.Snapshot) *helper.Report -``` - -Report processes the provided asset snapshots and generates a report annotated with the recommended actions. - - -## type [QstickStrategy]() - -QstickStrategy represents the configuration parameters for calculating the Qstick strategy. Qstick is a momentum indicator used to identify an asset's trend by looking at the SMA of the difference between its closing and opening. - -A Qstick above zero indicates increasing buying pressure, while a Qstick below zero indicates increasing selling pressure. - -```go -type QstickStrategy struct { - Strategy - - // Qstick represents the configuration parameters for calculating the Qstick. - Qstick *momentum.Qstick[float64] -} -``` - - -### func [NewQstickStrategy]() - -```go -func NewQstickStrategy() *QstickStrategy -``` - -NewQstickStrategy function initializes a new Qstick strategy instance. - - -### func \(\*QstickStrategy\) [Compute]() - -```go -func (q *QstickStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action -``` - -Compute processes the provided asset snapshots and generates a stream of actionable recommendations. - - -### func \(\*QstickStrategy\) [Name]() - -```go -func (*QstickStrategy) Name() string -``` - -Name returns the name of the strategy. - - -### func \(\*QstickStrategy\) [Report]() - -```go -func (q *QstickStrategy) Report(c <-chan *asset.Snapshot) *helper.Report -``` - -Report processes the provided asset snapshots and generates a report annotated with the recommended actions. - ## type [Result]() @@ -692,110 +253,4 @@ type Strategy interface { } ``` - -## type [TrimaStrategy]() - -TrimaStrategy represents the configuration parameters for calculating the TRIMA strategy. A bullish cross occurs when the short TRIMA moves above the long TRIMA. A bearish cross occurs when the short TRIMA moves below the long TRIME. - -```go -type TrimaStrategy struct { - Strategy - - // Trima1 represents the configuration parameters for calculating the short TRIMA. - Short *trend.Trima[float64] - - // Trima2 represents the configuration parameters for calculating the long TRIMA. - Long *trend.Trima[float64] -} -``` - - -### func [NewTrimaStrategy]() - -```go -func NewTrimaStrategy() *TrimaStrategy -``` - -NewTrimaStrategy function initializes a new TRIMA strategy instance with the default parameters. - - -### func \(\*TrimaStrategy\) [Compute]() - -```go -func (t *TrimaStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action -``` - -Compute processes the provided asset snapshots and generates a stream of actionable recommendations. - - -### func \(\*TrimaStrategy\) [Name]() - -```go -func (*TrimaStrategy) Name() string -``` - -Name returns the name of the strategy. - - -### func \(\*TrimaStrategy\) [Report]() - -```go -func (t *TrimaStrategy) Report(c <-chan *asset.Snapshot) *helper.Report -``` - -Report processes the provided asset snapshots and generates a report annotated with the recommended actions. - - -## type [VwmaStrategy]() - -VwmaStrategy represents the configuration parameters for calculating the VWMA strategy. 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 otherwse. - -```go -type VwmaStrategy struct { - Strategy - - // VWMA indicator. - Vwma *trend.Vwma[float64] - - // SMA indicator. - Sma *trend.Sma[float64] -} -``` - - -### func [NewVwmaStrategy]() - -```go -func NewVwmaStrategy() *VwmaStrategy -``` - -NewVwmaStrategy function initializes a new VWMA strategy instance with the default parameters. - - -### func \(\*VwmaStrategy\) [Compute]() - -```go -func (v *VwmaStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action -``` - -Compute processes the provided asset snapshots and generates a stream of actionable recommendations. - - -### func \(\*VwmaStrategy\) [Name]() - -```go -func (*VwmaStrategy) Name() string -``` - -Name returns the name of the strategy. - - -### func \(\*VwmaStrategy\) [Report]() - -```go -func (v *VwmaStrategy) Report(c <-chan *asset.Snapshot) *helper.Report -``` - -Report processes the provided asset snapshots and generates a report annotated with the recommended actions. - Generated by [gomarkdoc]() diff --git a/strategy/backtest.go b/strategy/backtest.go index ccb293e..b29c11e 100644 --- a/strategy/backtest.go +++ b/strategy/backtest.go @@ -109,17 +109,7 @@ func (b *Backtest) Run() error { // allStrategies returns a slice containing references to all available strategies. func (*Backtest) allStrategies() []Strategy { return []Strategy{ - NewApoStrategy(), - NewAroonStrategy(), - NewBopStrategy(), NewBuyAndHoldStrategy(), - NewDemaStrategy(), - NewKdjStrategy(), - NewMacdStrategy(), - NewQstickStrategy(), - NewTrimaStrategy(), - NewTrimaStrategy(), - NewVwmaStrategy(), } } diff --git a/strategy/backtest_test.go b/strategy/backtest_test.go index b7700a6..df2b813 100644 --- a/strategy/backtest_test.go +++ b/strategy/backtest_test.go @@ -10,6 +10,7 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func TestBacktest(t *testing.T) { @@ -24,7 +25,7 @@ func TestBacktest(t *testing.T) { backtest := strategy.NewBacktest(repository, outputDir) backtest.Names = append(backtest.Names, "brk-b") - backtest.Strategies = append(backtest.Strategies, strategy.NewApoStrategy()) + backtest.Strategies = append(backtest.Strategies, trend.NewApoStrategy()) err = backtest.Run() if err != nil { diff --git a/strategy/trend/README.md b/strategy/trend/README.md new file mode 100644 index 0000000..d228b3d --- /dev/null +++ b/strategy/trend/README.md @@ -0,0 +1,641 @@ + + +# trend + +```go +import "github.com/cinar/indicator/strategy/trend" +``` + +Package trend contains the trend strategy functions. + +This package belongs to the Indicator project. Indicator is a Golang module that supplies a variety of technical indicators, strategies, and a backtesting framework for analysis. + +### License + +``` +Copyright (c) 2021-2023 Onur Cinar. +The source code is provided under GNU AGPLv3 License. +https://github.com/cinar/indicator +``` + +### 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. + +## Index + +- [Constants](<#constants>) +- [func AllStrategies\(\) \[\]strategy.Strategy](<#AllStrategies>) +- [type ApoStrategy](<#ApoStrategy>) + - [func NewApoStrategy\(\) \*ApoStrategy](<#NewApoStrategy>) + - [func \(a \*ApoStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#ApoStrategy.Compute>) + - [func \(\*ApoStrategy\) Name\(\) string](<#ApoStrategy.Name>) + - [func \(a \*ApoStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#ApoStrategy.Report>) +- [type AroonStrategy](<#AroonStrategy>) + - [func NewAroonStrategy\(\) \*AroonStrategy](<#NewAroonStrategy>) + - [func \(a \*AroonStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#AroonStrategy.Compute>) + - [func \(\*AroonStrategy\) Name\(\) string](<#AroonStrategy.Name>) + - [func \(a \*AroonStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#AroonStrategy.Report>) +- [type BopStrategy](<#BopStrategy>) + - [func NewBopStrategy\(\) \*BopStrategy](<#NewBopStrategy>) + - [func \(b \*BopStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#BopStrategy.Compute>) + - [func \(\*BopStrategy\) Name\(\) string](<#BopStrategy.Name>) + - [func \(b \*BopStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#BopStrategy.Report>) +- [type DemaStrategy](<#DemaStrategy>) + - [func NewDemaStrategy\(\) \*DemaStrategy](<#NewDemaStrategy>) + - [func \(d \*DemaStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#DemaStrategy.Compute>) + - [func \(\*DemaStrategy\) Name\(\) string](<#DemaStrategy.Name>) + - [func \(d \*DemaStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#DemaStrategy.Report>) +- [type KdjStrategy](<#KdjStrategy>) + - [func NewKdjStrategy\(\) \*KdjStrategy](<#NewKdjStrategy>) + - [func \(kdj \*KdjStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#KdjStrategy.Compute>) + - [func \(\*KdjStrategy\) Name\(\) string](<#KdjStrategy.Name>) + - [func \(kdj \*KdjStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#KdjStrategy.Report>) +- [type MacdStrategy](<#MacdStrategy>) + - [func NewMacdStrategy\(\) \*MacdStrategy](<#NewMacdStrategy>) + - [func \(m \*MacdStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#MacdStrategy.Compute>) + - [func \(\*MacdStrategy\) Name\(\) string](<#MacdStrategy.Name>) + - [func \(m \*MacdStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#MacdStrategy.Report>) +- [type QstickStrategy](<#QstickStrategy>) + - [func NewQstickStrategy\(\) \*QstickStrategy](<#NewQstickStrategy>) + - [func \(q \*QstickStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#QstickStrategy.Compute>) + - [func \(\*QstickStrategy\) Name\(\) string](<#QstickStrategy.Name>) + - [func \(q \*QstickStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#QstickStrategy.Report>) +- [type TrimaStrategy](<#TrimaStrategy>) + - [func NewTrimaStrategy\(\) \*TrimaStrategy](<#NewTrimaStrategy>) + - [func \(t \*TrimaStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#TrimaStrategy.Compute>) + - [func \(\*TrimaStrategy\) Name\(\) string](<#TrimaStrategy.Name>) + - [func \(t \*TrimaStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#TrimaStrategy.Report>) +- [type TrixStrategy](<#TrixStrategy>) + - [func NewTrixStrategy\(\) \*TrixStrategy](<#NewTrixStrategy>) + - [func \(t \*TrixStrategy\) Compute\(snapshots \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#TrixStrategy.Compute>) + - [func \(\*TrixStrategy\) Name\(\) string](<#TrixStrategy.Name>) + - [func \(t \*TrixStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#TrixStrategy.Report>) +- [type VwmaStrategy](<#VwmaStrategy>) + - [func NewVwmaStrategy\(\) \*VwmaStrategy](<#NewVwmaStrategy>) + - [func \(v \*VwmaStrategy\) Compute\(c \<\-chan \*asset.Snapshot\) \<\-chan strategy.Action](<#VwmaStrategy.Compute>) + - [func \(\*VwmaStrategy\) Name\(\) string](<#VwmaStrategy.Name>) + - [func \(v \*VwmaStrategy\) Report\(c \<\-chan \*asset.Snapshot\) \*helper.Report](<#VwmaStrategy.Report>) + + +## Constants + + + +```go +const ( + // DefaultDemaStrategyPeriod1 is the first DEMA period. + DefaultDemaStrategyPeriod1 = 5 + + // DefaultDemaStrategyPeriod2 is the second DEMA period. + DefaultDemaStrategyPeriod2 = 35 +) +``` + + + +```go +const ( + // DefaultTrimaStrategyShortPeriod is the first TRIMA period. + DefaultTrimaStrategyShortPeriod = 20 + + // DefaultTrimaStrategyLongPeriod is the second TRIMA period. + DefaultTrimaStrategyLongPeriod = 50 +) +``` + + + +```go +const ( + // DefaultVwmaStrategyPeriod is the default VWMA period. + DefaultVwmaStrategyPeriod = 20 +) +``` + + +## func [AllStrategies]() + +```go +func AllStrategies() []strategy.Strategy +``` + +AllStrategies returns a slice containing references to all available trend strategies. + + +## type [ApoStrategy]() + +ApoStrategy represents the configuration parameters for calculating the APO strategy. 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. + +```go +type ApoStrategy struct { + strategy.Strategy + + // Apo represents the configuration parameters for calculating the + // Absolute Price Oscillator (APO). + Apo *trend.Apo[float64] +} +``` + + +### func [NewApoStrategy]() + +```go +func NewApoStrategy() *ApoStrategy +``` + +NewApoStrategy function initializes a new APO strategy instance with the default parameters. + + +### func \(\*ApoStrategy\) [Compute]() + +```go +func (a *ApoStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*ApoStrategy\) [Name]() + +```go +func (*ApoStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*ApoStrategy\) [Report]() + +```go +func (a *ApoStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + + +## type [AroonStrategy]() + +AroonStrategy represents the configuration parameters for calculating the Aroon strategy. Aroon 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. + +```go +type AroonStrategy struct { + strategy.Strategy + + // Aroon represent the configuration for calculating the Aroon indicator. + Aroon *trend.Aroon[float64] +} +``` + + +### func [NewAroonStrategy]() + +```go +func NewAroonStrategy() *AroonStrategy +``` + +NewAroonStrategy function initializes a new Aroon strategy instance with the default parameters. + + +### func \(\*AroonStrategy\) [Compute]() + +```go +func (a *AroonStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*AroonStrategy\) [Name]() + +```go +func (*AroonStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*AroonStrategy\) [Report]() + +```go +func (a *AroonStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + + +## type [BopStrategy]() + +BopStrategy 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. + +```go +type BopStrategy struct { + strategy.Strategy + + // Bop represents the configuration parameters for calculating the + // Balance of Power (BoP). + Bop *trend.Bop[float64] +} +``` + + +### func [NewBopStrategy]() + +```go +func NewBopStrategy() *BopStrategy +``` + +NewBopStrategy function initializes a new BoP strategy instance with the default parameters. + + +### func \(\*BopStrategy\) [Compute]() + +```go +func (b *BopStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*BopStrategy\) [Name]() + +```go +func (*BopStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*BopStrategy\) [Report]() + +```go +func (b *BopStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + + +## type [DemaStrategy]() + +DemaStrategy represents the configuration parameters for calculating the DEMA strategy. A bullish cross occurs when DEMA with 5 days period moves above DEMA with 35 days period. A bearish cross occurs when DEMA with 35 days period moves above DEMA With 5 days period. + +```go +type DemaStrategy struct { + strategy.Strategy + + // Dema1 represents the configuration parameters for + // calculating the first DEMA. + Dema1 *trend.Dema[float64] + + // Dema2 represents the configuration parameters for + // calculating the second DEMA. + Dema2 *trend.Dema[float64] +} +``` + + +### func [NewDemaStrategy]() + +```go +func NewDemaStrategy() *DemaStrategy +``` + +NewDemaStrategy function initializes a new DEMA strategy instance with the default parameters. + + +### func \(\*DemaStrategy\) [Compute]() + +```go +func (d *DemaStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*DemaStrategy\) [Name]() + +```go +func (*DemaStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*DemaStrategy\) [Report]() + +```go +func (d *DemaStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + + +## type [KdjStrategy]() + +KdjStrategy represents the configuration parameters for calculating the KDJ strategy. Generates BUY action when j value crosses above both k and d values. Generates SELL action when j value crosses below both k and d values. + +```go +type KdjStrategy struct { + strategy.Strategy + + // Kdj represents the configuration parameters for calculating the KDJ. + Kdj *trend.Kdj[float64] +} +``` + + +### func [NewKdjStrategy]() + +```go +func NewKdjStrategy() *KdjStrategy +``` + +NewKdjStrategy function initializes a new KDJ strategy instance. + + +### func \(\*KdjStrategy\) [Compute]() + +```go +func (kdj *KdjStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*KdjStrategy\) [Name]() + +```go +func (*KdjStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*KdjStrategy\) [Report]() + +```go +func (kdj *KdjStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + + +## type [MacdStrategy]() + +MacdStrategy represents the configuration parameters for calculating the MACD strategy. A MACD value crossing above the signal line suggests a bullish trend, while crossing below the signal line indicates a bearish trend. + +```go +type MacdStrategy struct { + strategy.Strategy + + // Macd represents the configuration parameters for calculating the + // Moving Average Convergence Divergence (MACD). + Macd *trend.Macd[float64] +} +``` + + +### func [NewMacdStrategy]() + +```go +func NewMacdStrategy() *MacdStrategy +``` + +NewMacdStrategy function initializes a new MACD strategy instance. + + +### func \(\*MacdStrategy\) [Compute]() + +```go +func (m *MacdStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*MacdStrategy\) [Name]() + +```go +func (*MacdStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*MacdStrategy\) [Report]() + +```go +func (m *MacdStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + + +## type [QstickStrategy]() + +QstickStrategy represents the configuration parameters for calculating the Qstick strategy. Qstick is a momentum indicator used to identify an asset's trend by looking at the SMA of the difference between its closing and opening. + +A Qstick above zero indicates increasing buying pressure, while a Qstick below zero indicates increasing selling pressure. + +```go +type QstickStrategy struct { + strategy.Strategy + + // Qstick represents the configuration parameters for calculating the Qstick. + Qstick *momentum.Qstick[float64] +} +``` + + +### func [NewQstickStrategy]() + +```go +func NewQstickStrategy() *QstickStrategy +``` + +NewQstickStrategy function initializes a new Qstick strategy instance. + + +### func \(\*QstickStrategy\) [Compute]() + +```go +func (q *QstickStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*QstickStrategy\) [Name]() + +```go +func (*QstickStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*QstickStrategy\) [Report]() + +```go +func (q *QstickStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + + +## type [TrimaStrategy]() + +TrimaStrategy represents the configuration parameters for calculating the TRIMA strategy. A bullish cross occurs when the short TRIMA moves above the long TRIMA. A bearish cross occurs when the short TRIMA moves below the long TRIME. + +```go +type TrimaStrategy struct { + strategy.Strategy + + // Trima1 represents the configuration parameters for calculating the short TRIMA. + Short *trend.Trima[float64] + + // Trima2 represents the configuration parameters for calculating the long TRIMA. + Long *trend.Trima[float64] +} +``` + + +### func [NewTrimaStrategy]() + +```go +func NewTrimaStrategy() *TrimaStrategy +``` + +NewTrimaStrategy function initializes a new TRIMA strategy instance with the default parameters. + + +### func \(\*TrimaStrategy\) [Compute]() + +```go +func (t *TrimaStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*TrimaStrategy\) [Name]() + +```go +func (*TrimaStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*TrimaStrategy\) [Report]() + +```go +func (t *TrimaStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + + +## type [TrixStrategy]() + +TrixStrategy represents the configuration parameters for calculating the TRIX strategy. A TRIX value crossing above the zero line suggests a bullish trend, while crossing below the zero line indicates a bearish trend. + +```go +type TrixStrategy struct { + strategy.Strategy + + // Trix represents the configuration parameters for calculating the TRIX. + Trix *trend.Trix[float64] +} +``` + + +### func [NewTrixStrategy]() + +```go +func NewTrixStrategy() *TrixStrategy +``` + +NewTrixStrategy function initializes a new TRIX strategy instance. + + +### func \(\*TrixStrategy\) [Compute]() + +```go +func (t *TrixStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*TrixStrategy\) [Name]() + +```go +func (*TrixStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*TrixStrategy\) [Report]() + +```go +func (t *TrixStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + + +## type [VwmaStrategy]() + +VwmaStrategy represents the configuration parameters for calculating the VWMA strategy. 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 otherwse. + +```go +type VwmaStrategy struct { + strategy.Strategy + + // VWMA indicator. + Vwma *trend.Vwma[float64] + + // SMA indicator. + Sma *trend.Sma[float64] +} +``` + + +### func [NewVwmaStrategy]() + +```go +func NewVwmaStrategy() *VwmaStrategy +``` + +NewVwmaStrategy function initializes a new VWMA strategy instance with the default parameters. + + +### func \(\*VwmaStrategy\) [Compute]() + +```go +func (v *VwmaStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action +``` + +Compute processes the provided asset snapshots and generates a stream of actionable recommendations. + + +### func \(\*VwmaStrategy\) [Name]() + +```go +func (*VwmaStrategy) Name() string +``` + +Name returns the name of the strategy. + + +### func \(\*VwmaStrategy\) [Report]() + +```go +func (v *VwmaStrategy) Report(c <-chan *asset.Snapshot) *helper.Report +``` + +Report processes the provided asset snapshots and generates a report annotated with the recommended actions. + +Generated by [gomarkdoc]() diff --git a/strategy/apo_strategy.go b/strategy/trend/apo_strategy.go similarity index 87% rename from strategy/apo_strategy.go rename to strategy/trend/apo_strategy.go index b91ceb6..5128273 100644 --- a/strategy/apo_strategy.go +++ b/strategy/trend/apo_strategy.go @@ -2,11 +2,12 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy +package trend import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/strategy" "github.com/cinar/indicator/trend" ) @@ -15,6 +16,8 @@ import ( // indicates a bearish trend. Positive APO values signify an upward trend, while // negative values signify a downward trend. type ApoStrategy struct { + strategy.Strategy + // Apo represents the configuration parameters for calculating the // Absolute Price Oscillator (APO). Apo *trend.Apo[float64] @@ -34,7 +37,7 @@ func (*ApoStrategy) Name() string { // Compute processes the provided asset snapshots and generates a // stream of actionable recommendations. -func (a *ApoStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action { +func (a *ApoStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action { closings := asset.SnapshotsAsClosings(snapshots) apo := a.Apo.Compute(closings) @@ -45,22 +48,22 @@ func (a *ApoStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action { // Skip the first value inputs[1] = helper.Skip(inputs[1], 1) - actions := helper.Operate(inputs[0], inputs[1], func(b, c float64) Action { + actions := helper.Operate(inputs[0], inputs[1], func(b, c float64) strategy.Action { // An APO value crossing above zero suggests a bullish trend. if c >= 0 && b < 0 { - return Buy + return strategy.Buy } // An APO value crossing below zero indicates a bearish trend. if c <= 0 && b > 0 { - return Sell + return strategy.Sell } - return Hold + return strategy.Hold }) // APO starts only after the slow period. - actions = helper.Shift(actions, a.Apo.SlowPeriod, Hold) + actions = helper.Shift(actions, a.Apo.SlowPeriod, strategy.Hold) return actions } @@ -80,8 +83,8 @@ func (a *ApoStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { closings := helper.Duplicate(asset.SnapshotsAsClosings(snapshots[2]), 2) apo := helper.Shift(a.Apo.Compute(closings[1]), a.Apo.SlowPeriod, 0) - actions, outcomes := ComputeWithOutcome(a, snapshots[1]) - annotations := ActionsToAnnotations(actions) + actions, outcomes := strategy.ComputeWithOutcome(a, snapshots[1]) + annotations := strategy.ActionsToAnnotations(actions) outcomes = helper.MultiplyBy(outcomes, 100) report := helper.NewReport(a.Name(), dates) diff --git a/strategy/apo_strategy_test.go b/strategy/trend/apo_strategy_test.go similarity index 86% rename from strategy/apo_strategy_test.go rename to strategy/trend/apo_strategy_test.go index 0de7748..341a37a 100644 --- a/strategy/apo_strategy_test.go +++ b/strategy/trend/apo_strategy_test.go @@ -2,7 +2,7 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy_test +package trend_test import ( "os" @@ -11,10 +11,11 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func TestApoStrategy(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } @@ -24,7 +25,7 @@ func TestApoStrategy(t *testing.T) { t.Fatal(err) } - apo := strategy.NewApoStrategy() + apo := trend.NewApoStrategy() actions, outcomes := strategy.ComputeWithOutcome(apo, snapshots) outcomes = helper.RoundDigits(outcomes, 2) @@ -36,12 +37,12 @@ func TestApoStrategy(t *testing.T) { } func TestApoStrategyReport(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } - apo := strategy.NewApoStrategy() + apo := trend.NewApoStrategy() report := apo.Report(snapshots) diff --git a/strategy/aroon_strategy.go b/strategy/trend/aroon_strategy.go similarity index 85% rename from strategy/aroon_strategy.go rename to strategy/trend/aroon_strategy.go index 5e349f3..93739d2 100644 --- a/strategy/aroon_strategy.go +++ b/strategy/trend/aroon_strategy.go @@ -2,11 +2,12 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy +package trend import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/strategy" "github.com/cinar/indicator/trend" ) @@ -17,7 +18,7 @@ import ( // Aroon Down, it suggests a bullish trend; when Aroon Down surpasses Aroon Up, // it indicates a bearish trend. type AroonStrategy struct { - Strategy + strategy.Strategy // Aroon represent the configuration for calculating the Aroon indicator. Aroon *trend.Aroon[float64] @@ -38,7 +39,7 @@ func (*AroonStrategy) Name() string { // Compute processes the provided asset snapshots and generates a // stream of actionable recommendations. -func (a *AroonStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { +func (a *AroonStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action { snapshots := helper.Duplicate(c, 2) highs := asset.SnapshotsAsHighs(snapshots[0]) @@ -46,20 +47,22 @@ func (a *AroonStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { ups, downs := a.Aroon.Compute(highs, lows) - actions := NormalizeActions(helper.Operate(ups, downs, func(up, down float64) Action { + actions := helper.Operate(ups, downs, func(up, down float64) strategy.Action { if up > down { - return Buy + return strategy.Buy } if down > up { - return Sell + return strategy.Sell } - return Hold - })) + return strategy.Hold + }) + + actions = strategy.NormalizeActions(actions) // Aroon starts only after the a full period. - actions = helper.Shift(actions, a.Aroon.Period-1, Hold) + actions = helper.Shift(actions, a.Aroon.Period-1, strategy.Hold) return actions } @@ -86,8 +89,8 @@ func (a *AroonStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { ups = helper.Shift(ups, a.Aroon.Period-1, 0) downs = helper.Shift(downs, a.Aroon.Period-1, 0) - actions, outcomes := ComputeWithOutcome(a, snapshots[4]) - annotations := ActionsToAnnotations(actions) + actions, outcomes := strategy.ComputeWithOutcome(a, snapshots[4]) + annotations := strategy.ActionsToAnnotations(actions) outcomes = helper.MultiplyBy(outcomes, 100) report := helper.NewReport(a.Name(), dates) diff --git a/strategy/aroon_strategy_test.go b/strategy/trend/aroon_strategy_test.go similarity index 85% rename from strategy/aroon_strategy_test.go rename to strategy/trend/aroon_strategy_test.go index c656a7e..89ea885 100644 --- a/strategy/aroon_strategy_test.go +++ b/strategy/trend/aroon_strategy_test.go @@ -2,7 +2,7 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy_test +package trend_test import ( "os" @@ -11,10 +11,11 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func TestAroonStrategy(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } @@ -24,7 +25,7 @@ func TestAroonStrategy(t *testing.T) { t.Fatal(err) } - aroon := strategy.NewAroonStrategy() + aroon := trend.NewAroonStrategy() actions, outcomes := strategy.ComputeWithOutcome(aroon, snapshots) outcomes = helper.RoundDigits(outcomes, 2) @@ -36,12 +37,12 @@ func TestAroonStrategy(t *testing.T) { } func TestAroonStrategyReport(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } - aroon := strategy.NewAroonStrategy() + aroon := trend.NewAroonStrategy() report := aroon.Report(snapshots) diff --git a/strategy/bop_strategy.go b/strategy/trend/bop_strategy.go similarity index 86% rename from strategy/bop_strategy.go rename to strategy/trend/bop_strategy.go index e77e2ef..f4446b3 100644 --- a/strategy/bop_strategy.go +++ b/strategy/trend/bop_strategy.go @@ -2,11 +2,12 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy +package trend import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/strategy" "github.com/cinar/indicator/trend" ) @@ -15,7 +16,7 @@ import ( // upward trend, while a negative value indicates a downward trend. A // BoP value of zero implies equilibrium between the two forces. type BopStrategy struct { - Strategy + strategy.Strategy // Bop represents the configuration parameters for calculating the // Balance of Power (BoP). @@ -36,7 +37,7 @@ func (*BopStrategy) Name() string { // Compute processes the provided asset snapshots and generates a // stream of actionable recommendations. -func (b *BopStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { +func (b *BopStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action { snapshots := helper.Duplicate(c, 4) openings := asset.SnapshotsAsOpenings(snapshots[0]) @@ -46,16 +47,16 @@ func (b *BopStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { bops := b.Bop.Compute(openings, highs, lows, closings) - return NormalizeActions(helper.Map(bops, func(bop float64) Action { + return strategy.NormalizeActions(helper.Map(bops, func(bop float64) strategy.Action { if bop > 0 { - return Buy + return strategy.Buy } if bop < 0 { - return Sell + return strategy.Sell } - return Hold + return strategy.Hold })) } @@ -82,8 +83,8 @@ func (b *BopStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { bop := b.Bop.Compute(openings, highs, lows, closings[1]) - actions, outcomes := ComputeWithOutcome(b, snapshots[5]) - annotations := ActionsToAnnotations(actions) + actions, outcomes := strategy.ComputeWithOutcome(b, snapshots[5]) + annotations := strategy.ActionsToAnnotations(actions) outcomes = helper.MultiplyBy(outcomes, 100) report := helper.NewReport(b.Name(), dates) diff --git a/strategy/bop_strategy_test.go b/strategy/trend/bop_strategy_test.go similarity index 86% rename from strategy/bop_strategy_test.go rename to strategy/trend/bop_strategy_test.go index 291ca8a..65fd8c4 100644 --- a/strategy/bop_strategy_test.go +++ b/strategy/trend/bop_strategy_test.go @@ -2,7 +2,7 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy_test +package trend_test import ( "os" @@ -11,10 +11,11 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func TestBopStrategy(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } @@ -24,7 +25,7 @@ func TestBopStrategy(t *testing.T) { t.Fatal(err) } - bop := strategy.NewBopStrategy() + bop := trend.NewBopStrategy() actions, outcomes := strategy.ComputeWithOutcome(bop, snapshots) outcomes = helper.RoundDigits(outcomes, 2) @@ -36,12 +37,12 @@ func TestBopStrategy(t *testing.T) { } func TestBopStrategyReport(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } - bop := strategy.NewBopStrategy() + bop := trend.NewBopStrategy() report := bop.Report(snapshots) diff --git a/strategy/dema_strategy.go b/strategy/trend/dema_strategy.go similarity index 87% rename from strategy/dema_strategy.go rename to strategy/trend/dema_strategy.go index 7d91d75..0afe189 100644 --- a/strategy/dema_strategy.go +++ b/strategy/trend/dema_strategy.go @@ -2,13 +2,14 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy +package trend import ( "fmt" "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/strategy" "github.com/cinar/indicator/trend" ) @@ -24,7 +25,7 @@ const ( // A bullish cross occurs when DEMA with 5 days period moves above DEMA with 35 days period. // A bearish cross occurs when DEMA with 35 days period moves above DEMA With 5 days period. type DemaStrategy struct { - Strategy + strategy.Strategy // Dema1 represents the configuration parameters for // calculating the first DEMA. @@ -59,7 +60,7 @@ func (*DemaStrategy) Name() string { // Compute processes the provided asset snapshots and generates a // stream of actionable recommendations. -func (d *DemaStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { +func (d *DemaStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action { closings := helper.Duplicate(asset.SnapshotsAsClosings(c), 2) demas1 := d.Dema1.Compute(closings[0]) @@ -68,21 +69,23 @@ func (d *DemaStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { demas2 := d.Dema2.Compute(closings[1]) demas2 = helper.Shift(demas2, d.Dema2.IdlePeriod(), 0) - actions := NormalizeActions(helper.Operate(demas1, demas2, func(dema1, dema2 float64) Action { + actions := helper.Operate(demas1, demas2, func(dema1, dema2 float64) strategy.Action { if dema1 > dema2 { - return Buy + return strategy.Buy } if dema2 > dema1 { - return Sell + return strategy.Sell } - return Hold - })) + return strategy.Hold + }) + + actions = strategy.NormalizeActions(actions) // DEMA starts only after the a full periods for each EMA used. actions = helper.Skip(actions, d.Dema2.IdlePeriod()) - actions = helper.Shift(actions, d.Dema2.IdlePeriod(), Hold) + actions = helper.Shift(actions, d.Dema2.IdlePeriod(), strategy.Hold) return actions } @@ -109,8 +112,8 @@ func (d *DemaStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { demas2 := d.Dema2.Compute(closings[1]) demas2 = helper.Shift(demas2, d.Dema2.IdlePeriod(), 0) - actions, outcomes := ComputeWithOutcome(d, snapshots[2]) - annotations := ActionsToAnnotations(actions) + actions, outcomes := strategy.ComputeWithOutcome(d, snapshots[2]) + annotations := strategy.ActionsToAnnotations(actions) outcomes = helper.MultiplyBy(outcomes, 100) report := helper.NewReport(d.Name(), dates) diff --git a/strategy/dema_strategy_test.go b/strategy/trend/dema_strategy_test.go similarity index 85% rename from strategy/dema_strategy_test.go rename to strategy/trend/dema_strategy_test.go index f5b5220..162e8b9 100644 --- a/strategy/dema_strategy_test.go +++ b/strategy/trend/dema_strategy_test.go @@ -2,7 +2,7 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy_test +package trend_test import ( "os" @@ -11,10 +11,11 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func TestDemaStrategy(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } @@ -24,7 +25,7 @@ func TestDemaStrategy(t *testing.T) { t.Fatal(err) } - dema := strategy.NewDemaStrategy() + dema := trend.NewDemaStrategy() actions, outcomes := strategy.ComputeWithOutcome(dema, snapshots) outcomes = helper.RoundDigits(outcomes, 2) @@ -36,12 +37,12 @@ func TestDemaStrategy(t *testing.T) { } func TestDemaStrategyReport(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } - dema := strategy.NewDemaStrategy() + dema := trend.NewDemaStrategy() report := dema.Report(snapshots) diff --git a/strategy/kdj_strategy.go b/strategy/trend/kdj_strategy.go similarity index 86% rename from strategy/kdj_strategy.go rename to strategy/trend/kdj_strategy.go index afae1ac..3855e19 100644 --- a/strategy/kdj_strategy.go +++ b/strategy/trend/kdj_strategy.go @@ -2,11 +2,12 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy +package trend import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/strategy" "github.com/cinar/indicator/trend" ) @@ -14,7 +15,7 @@ import ( // Generates BUY action when j value crosses above both k and d values. // Generates SELL action when j value crosses below both k and d values. type KdjStrategy struct { - Strategy + strategy.Strategy // Kdj represents the configuration parameters for calculating the KDJ. Kdj *trend.Kdj[float64] @@ -34,7 +35,7 @@ func (*KdjStrategy) Name() string { // Compute processes the provided asset snapshots and generates a // stream of actionable recommendations. -func (kdj *KdjStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { +func (kdj *KdjStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action { snapshots := helper.Duplicate(c, 3) highs := asset.SnapshotsAsHighs(snapshots[0]) lows := asset.SnapshotsAsLows(snapshots[1]) @@ -46,22 +47,24 @@ func (kdj *KdjStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { jk := helper.Subtract(js[0], k) jd := helper.Subtract(js[1], d) - actions := NormalizeActions(helper.Operate(jk, jd, func(a, b float64) Action { + actions := helper.Operate(jk, jd, func(a, b float64) strategy.Action { // Generates BUY action when j value crosses above both k and d values. if a > 0 && b > 0 { - return Buy + return strategy.Buy } // Generates SELL action when j value crosses below both k and d values. if a < 0 && b < 0 { - return Sell + return strategy.Sell } - return Hold - })) + return strategy.Hold + }) + + actions = strategy.NormalizeActions(actions) // KDJ starts only after a full period. - actions = helper.Shift(actions, kdj.Kdj.IdlePeriod(), Hold) + actions = helper.Shift(actions, kdj.Kdj.IdlePeriod(), strategy.Hold) return actions } @@ -90,8 +93,8 @@ func (kdj *KdjStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { d = helper.Shift(d, kdj.Kdj.IdlePeriod(), 0) j = helper.Shift(j, kdj.Kdj.IdlePeriod(), 0) - actions, outcomes := ComputeWithOutcome(kdj, snapshots[4]) - annotations := ActionsToAnnotations(actions) + actions, outcomes := strategy.ComputeWithOutcome(kdj, snapshots[4]) + annotations := strategy.ActionsToAnnotations(actions) outcomes = helper.MultiplyBy(outcomes, 100) report := helper.NewReport(kdj.Name(), dates) diff --git a/strategy/kdj_strategy_test.go b/strategy/trend/kdj_strategy_test.go similarity index 86% rename from strategy/kdj_strategy_test.go rename to strategy/trend/kdj_strategy_test.go index cf17b64..bfc087d 100644 --- a/strategy/kdj_strategy_test.go +++ b/strategy/trend/kdj_strategy_test.go @@ -2,7 +2,7 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy_test +package trend_test import ( "os" @@ -11,10 +11,11 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func TestKdjStrategy(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } @@ -24,7 +25,7 @@ func TestKdjStrategy(t *testing.T) { t.Fatal(err) } - kdj := strategy.NewKdjStrategy() + kdj := trend.NewKdjStrategy() actions, outcomes := strategy.ComputeWithOutcome(kdj, snapshots) outcomes = helper.RoundDigits(outcomes, 2) @@ -35,12 +36,12 @@ func TestKdjStrategy(t *testing.T) { } func TestKdjStrategyReport(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } - kdj := strategy.NewKdjStrategy() + kdj := trend.NewKdjStrategy() report := kdj.Report(snapshots) diff --git a/strategy/macd_strategy.go b/strategy/trend/macd_strategy.go similarity index 88% rename from strategy/macd_strategy.go rename to strategy/trend/macd_strategy.go index e804f26..cafdf3d 100644 --- a/strategy/macd_strategy.go +++ b/strategy/trend/macd_strategy.go @@ -2,11 +2,12 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy +package trend import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/strategy" "github.com/cinar/indicator/trend" ) @@ -15,7 +16,7 @@ import ( // bullish trend, while crossing below the signal line indicates a // bearish trend. type MacdStrategy struct { - Strategy + strategy.Strategy // Macd represents the configuration parameters for calculating the // Moving Average Convergence Divergence (MACD). @@ -36,7 +37,7 @@ func (*MacdStrategy) Name() string { // Compute processes the provided asset snapshots and generates a // stream of actionable recommendations. -func (m *MacdStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action { +func (m *MacdStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan strategy.Action { closings := asset.SnapshotsAsClosings(snapshots) macds, signals := m.Macd.Compute(closings) @@ -47,22 +48,22 @@ func (m *MacdStrategy) Compute(snapshots <-chan *asset.Snapshot) <-chan Action { divergences := helper.Duplicate(divergence, 2) divergences[1] = helper.Skip(divergences[1], 1) - actions := helper.Operate(divergences[0], divergences[1], func(b, c float64) Action { + actions := helper.Operate(divergences[0], divergences[1], func(b, c float64) strategy.Action { // A MACD value crossing above signal line suggests a bullish trend. if c >= 0 && b < 0 { - return Buy + return strategy.Buy } // A MACD value crossing below signal line suggests a bearish trend. if c <= 0 && b > 0 { - return Sell + return strategy.Sell } - return Hold + return strategy.Hold }) // MACD starts only after a full period. - actions = helper.Shift(actions, m.Macd.IdlePeriod(), Hold) + actions = helper.Shift(actions, m.Macd.IdlePeriod(), strategy.Hold) return actions } @@ -86,8 +87,8 @@ func (m *MacdStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { macds = helper.Shift(macds, m.Macd.IdlePeriod(), 0) signals = helper.Shift(signals, m.Macd.IdlePeriod(), 0) - actions, outcomes := ComputeWithOutcome(m, snapshots[2]) - annotations := ActionsToAnnotations(actions) + actions, outcomes := strategy.ComputeWithOutcome(m, snapshots[2]) + annotations := strategy.ActionsToAnnotations(actions) outcomes = helper.MultiplyBy(outcomes, 100) report := helper.NewReport(m.Name(), dates) diff --git a/strategy/macd_strategy_test.go b/strategy/trend/macd_strategy_test.go similarity index 85% rename from strategy/macd_strategy_test.go rename to strategy/trend/macd_strategy_test.go index d6ddac8..4e29e51 100644 --- a/strategy/macd_strategy_test.go +++ b/strategy/trend/macd_strategy_test.go @@ -2,7 +2,7 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy_test +package trend_test import ( "os" @@ -11,10 +11,11 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func TestMacdStrategy(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } @@ -24,7 +25,7 @@ func TestMacdStrategy(t *testing.T) { t.Fatal(err) } - macd := strategy.NewMacdStrategy() + macd := trend.NewMacdStrategy() actions, outcomes := strategy.ComputeWithOutcome(macd, snapshots) outcomes = helper.RoundDigits(outcomes, 2) @@ -35,12 +36,12 @@ func TestMacdStrategy(t *testing.T) { } func TestMacdStrategyReport(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } - macd := strategy.NewMacdStrategy() + macd := trend.NewMacdStrategy() report := macd.Report(snapshots) diff --git a/strategy/qstick_strategy.go b/strategy/trend/qstick_strategy.go similarity index 89% rename from strategy/qstick_strategy.go rename to strategy/trend/qstick_strategy.go index 80d0d75..96cf4ed 100644 --- a/strategy/qstick_strategy.go +++ b/strategy/trend/qstick_strategy.go @@ -2,12 +2,13 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy +package trend import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" "github.com/cinar/indicator/momentum" + "github.com/cinar/indicator/strategy" ) // QstickStrategy represents the configuration parameters for calculating the @@ -18,7 +19,7 @@ import ( // A Qstick above zero indicates increasing buying pressure, while // a Qstick below zero indicates increasing selling pressure. type QstickStrategy struct { - Strategy + strategy.Strategy // Qstick represents the configuration parameters for calculating the Qstick. Qstick *momentum.Qstick[float64] @@ -38,7 +39,7 @@ func (*QstickStrategy) Name() string { // Compute processes the provided asset snapshots and generates a // stream of actionable recommendations. -func (q *QstickStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { +func (q *QstickStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action { snapshots := helper.Duplicate(c, 2) openings := asset.SnapshotsAsOpenings(snapshots[0]) closings := asset.SnapshotsAsClosings(snapshots[1]) @@ -49,22 +50,22 @@ func (q *QstickStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { qsticks := helper.Duplicate(qstick, 2) qsticks[1] = helper.Skip(qsticks[1], 1) - actions := helper.Operate(qsticks[0], qsticks[1], func(b, c float64) Action { + actions := helper.Operate(qsticks[0], qsticks[1], func(b, c float64) strategy.Action { // A Qstick above zero indicates increasing buying pressure. if c >= 0 && b < 0 { - return Buy + return strategy.Buy } // A Qstick below zero indicates increasing selling pressure. if c <= 0 && b > 0 { - return Sell + return strategy.Sell } - return Hold + return strategy.Hold }) // Qstick starts only after a full period. - actions = helper.Shift(actions, q.Qstick.Sma.Period, Hold) + actions = helper.Shift(actions, q.Qstick.Sma.Period, strategy.Hold) return actions } @@ -90,8 +91,8 @@ func (q *QstickStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { qstick := q.Qstick.Compute(openings[0], closings[0]) qstick = helper.Shift(qstick, q.Qstick.Sma.Period-1, 0) - actions, outcomes := ComputeWithOutcome(q, snapshots[3]) - annotations := ActionsToAnnotations(actions) + actions, outcomes := strategy.ComputeWithOutcome(q, snapshots[3]) + annotations := strategy.ActionsToAnnotations(actions) outcomes = helper.MultiplyBy(outcomes, 100) report := helper.NewReport(q.Name(), dates) diff --git a/strategy/qstick_strategy_test.go b/strategy/trend/qstick_strategy_test.go similarity index 85% rename from strategy/qstick_strategy_test.go rename to strategy/trend/qstick_strategy_test.go index b708110..7d70412 100644 --- a/strategy/qstick_strategy_test.go +++ b/strategy/trend/qstick_strategy_test.go @@ -2,7 +2,7 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy_test +package trend_test import ( "os" @@ -11,10 +11,11 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func TestQstickStrategy(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } @@ -24,7 +25,7 @@ func TestQstickStrategy(t *testing.T) { t.Fatal(err) } - qstick := strategy.NewQstickStrategy() + qstick := trend.NewQstickStrategy() actions, outcomes := strategy.ComputeWithOutcome(qstick, snapshots) outcomes = helper.RoundDigits(outcomes, 2) @@ -35,12 +36,12 @@ func TestQstickStrategy(t *testing.T) { } func TestQstickStrategyReport(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } - qstick := strategy.NewQstickStrategy() + qstick := trend.NewQstickStrategy() report := qstick.Report(snapshots) diff --git a/strategy/testdata/apo_strategy.csv b/strategy/trend/testdata/apo_strategy.csv similarity index 100% rename from strategy/testdata/apo_strategy.csv rename to strategy/trend/testdata/apo_strategy.csv diff --git a/strategy/testdata/aroon_strategy.csv b/strategy/trend/testdata/aroon_strategy.csv similarity index 100% rename from strategy/testdata/aroon_strategy.csv rename to strategy/trend/testdata/aroon_strategy.csv diff --git a/strategy/testdata/bop_strategy.csv b/strategy/trend/testdata/bop_strategy.csv similarity index 100% rename from strategy/testdata/bop_strategy.csv rename to strategy/trend/testdata/bop_strategy.csv diff --git a/strategy/testdata/dema_strategy.csv b/strategy/trend/testdata/dema_strategy.csv similarity index 100% rename from strategy/testdata/dema_strategy.csv rename to strategy/trend/testdata/dema_strategy.csv diff --git a/strategy/testdata/kdj_strategy.csv b/strategy/trend/testdata/kdj_strategy.csv similarity index 100% rename from strategy/testdata/kdj_strategy.csv rename to strategy/trend/testdata/kdj_strategy.csv diff --git a/strategy/testdata/macd_strategy.csv b/strategy/trend/testdata/macd_strategy.csv similarity index 100% rename from strategy/testdata/macd_strategy.csv rename to strategy/trend/testdata/macd_strategy.csv diff --git a/strategy/testdata/qstick_strategy.csv b/strategy/trend/testdata/qstick_strategy.csv similarity index 100% rename from strategy/testdata/qstick_strategy.csv rename to strategy/trend/testdata/qstick_strategy.csv diff --git a/strategy/testdata/trima_strategy.csv b/strategy/trend/testdata/trima_strategy.csv similarity index 100% rename from strategy/testdata/trima_strategy.csv rename to strategy/trend/testdata/trima_strategy.csv diff --git a/strategy/testdata/vwma_strategy.csv b/strategy/trend/testdata/vwma_strategy.csv similarity index 100% rename from strategy/testdata/vwma_strategy.csv rename to strategy/trend/testdata/vwma_strategy.csv diff --git a/strategy/trend/trend.go b/strategy/trend/trend.go index 4ad849b..71c63d0 100644 --- a/strategy/trend/trend.go +++ b/strategy/trend/trend.go @@ -17,3 +17,21 @@ // informational purposes and is not to be construed as // advice or solicitation to buy or sell any security. package trend + +import "github.com/cinar/indicator/strategy" + +// AllStrategies returns a slice containing references to all available trend strategies. +func AllStrategies() []strategy.Strategy { + return []strategy.Strategy{ + NewApoStrategy(), + NewAroonStrategy(), + NewBopStrategy(), + NewDemaStrategy(), + NewKdjStrategy(), + NewMacdStrategy(), + NewQstickStrategy(), + NewTrimaStrategy(), + NewTrimaStrategy(), + NewVwmaStrategy(), + } +} diff --git a/strategy/trima_strategy.go b/strategy/trend/trima_strategy.go similarity index 87% rename from strategy/trima_strategy.go rename to strategy/trend/trima_strategy.go index 083e9a6..b050cba 100644 --- a/strategy/trima_strategy.go +++ b/strategy/trend/trima_strategy.go @@ -2,11 +2,12 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy +package trend import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/strategy" "github.com/cinar/indicator/trend" ) @@ -22,7 +23,7 @@ const ( // A bullish cross occurs when the short TRIMA moves above the long TRIMA. // A bearish cross occurs when the short TRIMA moves below the long TRIME. type TrimaStrategy struct { - Strategy + strategy.Strategy // Trima1 represents the configuration parameters for calculating the short TRIMA. Short *trend.Trima[float64] @@ -52,7 +53,7 @@ func (*TrimaStrategy) Name() string { // Compute processes the provided asset snapshots and generates a // stream of actionable recommendations. -func (t *TrimaStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { +func (t *TrimaStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action { closings := helper.Duplicate(asset.SnapshotsAsClosings(c), 2) shorts := t.Short.Compute(closings[0]) @@ -60,20 +61,22 @@ func (t *TrimaStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { shorts = helper.Skip(shorts, t.Long.IdlePeriod()-t.Short.IdlePeriod()) - actions := NormalizeActions(helper.Operate(shorts, longs, func(short, long float64) Action { + actions := helper.Operate(shorts, longs, func(short, long float64) strategy.Action { if short > long { - return Buy + return strategy.Buy } if long > short { - return Sell + return strategy.Sell } - return Hold - })) + return strategy.Hold + }) + + actions = strategy.NormalizeActions(actions) // TRIMA starts only after the a full periods for each EMA used. - actions = helper.Shift(actions, t.Long.IdlePeriod(), Hold) + actions = helper.Shift(actions, t.Long.IdlePeriod(), strategy.Hold) return actions } @@ -101,8 +104,8 @@ func (t *TrimaStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { shorts = helper.Shift(shorts, t.Long.IdlePeriod(), 0) longs = helper.Shift(longs, t.Long.IdlePeriod(), 0) - actions, outcomes := ComputeWithOutcome(t, snapshots[2]) - annotations := ActionsToAnnotations(actions) + actions, outcomes := strategy.ComputeWithOutcome(t, snapshots[2]) + annotations := strategy.ActionsToAnnotations(actions) outcomes = helper.MultiplyBy(outcomes, 100) report := helper.NewReport(t.Name(), dates) diff --git a/strategy/trima_strategy_test.go b/strategy/trend/trima_strategy_test.go similarity index 85% rename from strategy/trima_strategy_test.go rename to strategy/trend/trima_strategy_test.go index edfa5b6..01a61b2 100644 --- a/strategy/trima_strategy_test.go +++ b/strategy/trend/trima_strategy_test.go @@ -2,7 +2,7 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy_test +package trend_test import ( "os" @@ -11,10 +11,11 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func TestTrimaStrategy(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } @@ -24,7 +25,7 @@ func TestTrimaStrategy(t *testing.T) { t.Fatal(err) } - trima := strategy.NewTrimaStrategy() + trima := trend.NewTrimaStrategy() actions, outcomes := strategy.ComputeWithOutcome(trima, snapshots) outcomes = helper.RoundDigits(outcomes, 2) @@ -36,12 +37,12 @@ func TestTrimaStrategy(t *testing.T) { } func TestTrimaStrategyReport(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } - trima := strategy.NewTrimaStrategy() + trima := trend.NewTrimaStrategy() report := trima.Report(snapshots) diff --git a/strategy/vwma_strategy.go b/strategy/trend/vwma_strategy.go similarity index 86% rename from strategy/vwma_strategy.go rename to strategy/trend/vwma_strategy.go index 18b635b..1c49032 100644 --- a/strategy/vwma_strategy.go +++ b/strategy/trend/vwma_strategy.go @@ -2,11 +2,12 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy +package trend import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" + "github.com/cinar/indicator/strategy" "github.com/cinar/indicator/trend" ) @@ -19,7 +20,7 @@ const ( // 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 otherwse. type VwmaStrategy struct { - Strategy + strategy.Strategy // VWMA indicator. Vwma *trend.Vwma[float64] @@ -47,23 +48,25 @@ func (*VwmaStrategy) Name() string { } // Compute processes the provided asset snapshots and generates a stream of actionable recommendations. -func (v *VwmaStrategy) Compute(c <-chan *asset.Snapshot) <-chan Action { +func (v *VwmaStrategy) Compute(c <-chan *asset.Snapshot) <-chan strategy.Action { smas, vwmas := v.calculateSmaAndVwma(c) - actions := NormalizeActions(helper.Operate(smas, vwmas, func(sma, vwma float64) Action { + actions := helper.Operate(smas, vwmas, func(sma, vwma float64) strategy.Action { if vwma > sma { - return Buy + return strategy.Buy } if sma > vwma { - return Sell + return strategy.Sell } - return Hold - })) + return strategy.Hold + }) + + actions = strategy.NormalizeActions(actions) // VWMA starts only after the a full period. - actions = helper.Shift(actions, v.Vwma.Period-1, Hold) + actions = helper.Shift(actions, v.Vwma.Period-1, strategy.Hold) return actions } @@ -88,8 +91,8 @@ func (v *VwmaStrategy) Report(c <-chan *asset.Snapshot) *helper.Report { smas = helper.Shift(smas, v.Vwma.Period-1, 0) vwmas = helper.Shift(vwmas, v.Vwma.Period-1, 0) - actions, outcomes := ComputeWithOutcome(v, snapshots[3]) - annotations := ActionsToAnnotations(actions) + actions, outcomes := strategy.ComputeWithOutcome(v, snapshots[3]) + annotations := strategy.ActionsToAnnotations(actions) outcomes = helper.MultiplyBy(outcomes, 100) report := helper.NewReport(v.Name(), dates) diff --git a/strategy/vwma_strategy_test.go b/strategy/trend/vwma_strategy_test.go similarity index 85% rename from strategy/vwma_strategy_test.go rename to strategy/trend/vwma_strategy_test.go index beec300..6fc4afd 100644 --- a/strategy/vwma_strategy_test.go +++ b/strategy/trend/vwma_strategy_test.go @@ -2,7 +2,7 @@ // The source code is provided under GNU AGPLv3 License. // https://github.com/cinar/indicator -package strategy_test +package trend_test import ( "os" @@ -11,10 +11,11 @@ import ( "github.com/cinar/indicator/asset" "github.com/cinar/indicator/helper" "github.com/cinar/indicator/strategy" + "github.com/cinar/indicator/strategy/trend" ) func TestVwmaStrategy(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } @@ -24,7 +25,7 @@ func TestVwmaStrategy(t *testing.T) { t.Fatal(err) } - vwma := strategy.NewVwmaStrategy() + vwma := trend.NewVwmaStrategy() actions, outcomes := strategy.ComputeWithOutcome(vwma, snapshots) outcomes = helper.RoundDigits(outcomes, 2) @@ -36,12 +37,12 @@ func TestVwmaStrategy(t *testing.T) { } func TestVwmaStrategyReport(t *testing.T) { - snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/repository/brk-b.csv", true) + snapshots, err := helper.ReadFromCsvFile[asset.Snapshot]("testdata/brk-b.csv", true) if err != nil { t.Fatal(err) } - vwma := strategy.NewVwmaStrategy() + vwma := trend.NewVwmaStrategy() report := vwma.Report(snapshots)