[This article was first published on Posts on Matthew Smith R Shenanigans, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

# Strategies

## Introduction:

Note: This post is unfinished.

This post goes through a number of the technical trading functions in the TTR package here. I add definitions and show examples of how the functions work.

library(dplyr)
library(quantmod)
library(tidyquant)
library(TTR)
library(timetk)
library(tidyr)
library(ggplot2)
library(directlabels)
library(data.table)
library(quantstrat)
library(purrr)
library(quantstrat)

We can download some data using the quantmod package which stores the different assets into our Global Environment.

start_date <- "2013-01-01"
end_date <- "2017-08-31"
symbols <- c("AAPL", "AMD", "ADI",  "ABBV", "A",  "APD", "AA", "CF", "NVDA", "HOG", "WMT", "AMZN"
#,"MSFT", "F", "INTC", "ADBE", "AMG", "AKAM", "ALB", "ALK"
)

getSymbols(symbols,
from = start_date,
to = end_date)
##  [1] "AAPL" "AMD"  "ADI"  "ABBV" "A"    "APD"  "AA"   "CF"   "NVDA" "HOG"
## [11] "WMT"  "AMZN"

The first few observations for AAPL looks like:

AAPL.Open AAPL.High AAPL.Low AAPL.Close AAPL.Volume AAPL.Adjusted
2013-01-02 79.11714 79.28571 77.37572 78.43285 140129500 68.85055
2013-01-03 78.26857 78.52428 77.28571 77.44286 88241300 67.98149
2013-01-04 76.71000 76.94714 75.11857 75.28571 148583400 66.08789
2013-01-07 74.57143 75.61429 73.60000 74.84286 121039100 65.69916
2013-01-08 75.60143 75.98428 74.46429 75.04429 114676800 65.87595
2013-01-09 74.64286 75.00143 73.71286 73.87143 101901100 64.84639

## Bollinger Bands Strategy

The idea of this strategy is

Definition: A Bollinger Band consists of 3 lines. A simple moving average (SMA) and two additional lines plotted 2 standard deviations above and below the SMA. The standard deviation measures a stocks volatility and so when the markers are more volatile then the Bollinger Bands become wider. When the market is flat the Bollinger Banks contract.

chartSeries(NVDA,
theme = chartTheme("white"),
TA = c(addBBands(n = 20, sd = 2, ma = "SMA", draw = 'bands', on = -1))) 

chartSeries(AMZN,
theme = chartTheme("white"),
TA = c(addBBands(n = 20, sd = 2, ma = "SMA", draw = 'bands', on = -1))) 

We set the initial quantstrat parameters and initialise the strategy.

rm.strat("BollingerBandsStrat")
currency('USD')
## [1] "USD"
stock(symbols, currency = 'USD', multiplier = 1)
##  [1] "AAPL" "AMD"  "ADI"  "ABBV" "A"    "APD"  "AA"   "CF"   "NVDA" "HOG"
## [11] "WMT"  "AMZN"
init_date = as.Date(start_date) - 1
init_equity = 1000
portfolio.st <- account.st <- 'BollingerBandsStrat'
initPortf(portfolio.st,
symbols = symbols,
initDate = init_date)
## [1] "BollingerBandsStrat"
initAcct(account.st,
portfolios = 'BollingerBandsStrat',
initDate = init_date)
## [1] "BollingerBandsStrat"
initOrders(portfolio = portfolio.st,
initDate = init_date)
BBands_Strategy <- strategy("BollingerBandsStrat")

We add the Bollinger Bands indicator from the TTR package to the strategy. It takes a Simple Moving Average (SMA) and the High Low Close data from each of our stocks, add the indicator to the defined BBands_Strategy and creates a new column in our data alled BollingerBands_Label.

# Add indicators
strategy = BBands_Strategy,
name = "BBands",
arguments = list(
HLC = quote(HLC(mktdata)),
maType = 'SMA'),
label = 'BollingerBands_Label')

• The first signal states that when the close is greater than the upper Bollinger Band, add a value of 1 into a column called Close.gt.LowerBBand. The sigCrossover function only returns the first occurence of the relationship switching from false to true. Replacing with sigComparison here would return all occurences of the relationship being greater than the upper band.

• The second signal states that when the close is less than the lower Bollinger Band add a value of 1 into a column called Close.lt.LowerBBand. Again using the sigCrossover function to only return the first occurence.

• The third signal

The below code creates 3 new columns in our mktdata. dn.BollingerBands_Label, mavg.BollingerBands_Label, up.BollingerBands_Label. Which are the lower, middle and upper Bollinger Band values. It also creates the following: Close.gt.UpperBBand, Close.lt.LowerBBand, Cross.MiddleBBand. Which takes on the values according to whether the sigCrossover’ has occured.

# Add Signals

BBands_Strategy,
name = "sigCrossover",
arguments = list(
columns = c("Close","up"),
relationship = "gt"),
label = "Close.gt.UpperBBand")

BBands_Strategy,
name = "sigCrossover",
arguments = list(
columns = c("Close","dn"),
relationship = "lt"),
label = "Close.lt.LowerBBand")

BBands_Strategy,
name = "sigCrossover",
arguments = list(
columns = c("High","Low","mavg"),
relationship = "op"),
label = "Cross.MiddleBBand")

The mktdata (which gets presented after running applyStrategy and updatePortf) would look like:

index WMT.Open WMT.High WMT.Low WMT.Close WMT.Volume WMT.Adjusted dn.BollingerBands_Label mavg.BollingerBands_Label up.BollingerBands_Label pctB.BollingerBands_Label Close.gt.UpperBBand Close.lt.LowerBBand Cross.MiddleBBand
2013-02-13 71.29 71.70 71.21 71.39 3968600 59.98333 68.71632 70.09250 71.46868 0.9871567 NA NA NA
2013-02-14 71.10 71.23 70.75 70.82 6821000 59.50441 68.82043 70.18133 71.54224 0.7762866 NA NA NA
2013-02-15 69.54 70.00 68.13 69.30 25683700 58.22726 68.84929 70.19050 71.53171 0.1096177 NA NA 1
2013-02-19 69.19 69.45 68.54 68.76 14682300 57.77356 68.82222 70.18217 71.54211 0.0347239 NA 1 NA
2013-02-20 68.72 69.85 68.30 69.21 11973600 58.15165 68.78472 70.16833 71.55195 0.1211618 NA NA NA
2013-02-21 70.00 71.47 69.72 70.26 20413100 59.03389 68.86119 70.22117 71.58114 0.5963869 NA NA 1
2013-02-22 70.22 70.54 69.89 70.40 9165200 59.15152 68.90249 70.24950 71.59651 0.5100843 NA NA NA
2013-02-25 70.50 71.34 70.44 70.44 11817600 59.18514 69.00983 70.32100 71.63217 0.6597812 NA NA NA
2013-02-26 70.69 71.39 70.61 71.11 10557600 59.74808 69.14416 70.41200 71.67984 0.7463509 NA NA NA
2013-02-27 70.92 71.96 70.58 71.66 8819000 60.21020 69.20490 70.49383 71.78277 0.8515172 NA NA NA
2013-02-28 71.59 71.88 70.78 70.78 18883600 59.47081 69.28076 70.56167 71.84258 0.7283528 NA NA NA
2013-03-01 70.78 71.90 70.78 71.74 8902300 60.27741 69.33128 70.63400 71.93672 0.8221462 NA NA NA
2013-03-04 71.52 73.26 71.51 73.26 10567200 61.55456 69.27376 70.75150 72.22924 1.1513879 1 NA NA
2013-03-05 73.47 74.04 72.99 73.72 9142000 61.94105 69.24347 70.95300 72.66253 1.2693145 NA NA NA
2013-03-06 73.75 74.13 73.25 73.38 7149600 61.65540 69.17483 71.10567 73.03650 1.1424682 NA NA NA
2013-03-07 73.49 73.61 73.20 73.32 6680000 61.60497 69.14018 71.22567 73.31116 1.0157056 NA NA NA

In row 4 of the below table, the close is 68.76 which is less than the lower Bollinger Band dn.BollingerBands_Label column 68.822 and a 1 is indicated in the Close.lt.LowerBBand column. In row 13 of the same data, there is a 1 in the column Close.gt.UpperBBand. The close is 73.26 and the up.BollingerBands_Label is 72.23 so the close is gt then Upper Bollinger Band.

• When the close is greater than the upper Bollinger Band we go short by a quantity of 100 at the market price
• When the close is lower than the lower Bollinger Band we go long by a quantity of 100 at the market price.
• When the Close crosses the middle Bollinger Band we exit all our positions.
# Add rules

BBands_Strategy,
name = 'ruleSignal',
arguments = list(
sigcol = "Close.gt.UpperBBand",
sigval = TRUE,
orderqty = -100,
ordertype = 'market',
orderside = NULL,
threshold = NULL),
type = 'enter')

BBands_Strategy,
name = 'ruleSignal',
arguments = list(
sigcol = "Close.lt.LowerBBand",
sigval = TRUE,
orderqty = 100,
ordertype = 'market',
orderside = NULL,
threshold = NULL),
type = 'enter')

BBands_Strategy,
name = 'ruleSignal',
arguments = list(
sigcol = "Cross.MiddleBBand",
sigval = TRUE,
orderqty = 'all',
ordertype = 'market',
orderside = NULL,
threshold = NULL),
type = 'exit')

Apply the strategy and update the portfolio

out <- applyStrategy(
strategy = BBands_Strategy,
portfolios = 'BollingerBandsStrat',
parameters = list(
sd = 1.6, # number of standard deviations
n = 20) # MA periods
)

updatePortf(Portfolio = 'BollingerBandsStrat', Dates = paste('::',as.Date(Sys.time()),sep=''))

Find the best performance stocks:

tradeStats(portfolio.st) %>%
tibble::rownames_to_column("ticker") %>%
t() %>%
kable() %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))

for(sym in symbols){
chart.Posn(Portfolio = 'BollingerBandsStrat', Symbol = sym)
plot(add_BBands(on = 1, sd = 1.6, n = 20))

tibble::rownames_to_column("ticker") %>%
filter(ticker == sym) %>%
t() %>%
kable() %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
}

Zooming in one some of the charts, it’s possible to see when we entered and exited each trade.

Here - everything works. chart.posn plots each plot in sequence find a way to plot it in one instance.

chart.Posn(Portfolio = 'BollingerBandsStrat', Symbol = "AMD")

plot(add_BBands(on = 1, sd = 1.6, n = 20))

zoom_Chart('2017-01::2017-03')

chart.Posn(Portfolio = 'BollingerBandsStrat', Symbol = "WMT")

plot(add_BBands(on = 1, sd = 1.6, n = 20))

zoom_Chart('2017-01::2017-03')

chart.Posn(Portfolio = 'BollingerBandsStrat', Symbol = "AMZN")

plot(add_BBands(on = 1, sd = 1.6, n = 20))

zoom_Chart('2017-01::2017-03')

The Directional Movement Index (DMI) Wilder (1978) tries to identify the direction an asset is moving towards through comparing previous highs and lows. It trys to determine if an asset is trending and to measure the strength of that trend. It has four components:

• Positive Direction Indicator (+DI): Computes the difference between todays high and the previous days high. - Helps identify the presence of an uptrend.

• Negative Direction Indicator (-DI): Computes the difference between todays low and the previous days low. - Helps identify the presence of a downtrend.

• Average Directional Index (ADX): A smoothed average of the difference of the +DI and -DI.

• Average Directional Rating (ADXR): Simple average of todays ADX and the ADX from 14 or n periods previously.

The ADX is relative to its own asset price. An ADX of 60 for $$Asset_{i}$$ is different to an ADX for $$Asset_{j}$$.

Using the TTR package along with the tidyquant package in R we can calculate the ADX using:

# adx_calc <- asset_prices %>%
#   group_by(symbol) %>%
#   tq_mutate(
#     select = c("high", "low", "close"),
#     n = 14,
#     maType = "SMA"
#     )

#   group_by(symbol) %>%
#   drop_na() %>%
#   slice(1:5)

The first few observations of each of the assets looks like:

Where the newely created columns are defined as:

• DIp: Directional Index positive
• DIn: Directional Index negative
• DX: Directional Index
• ADX: Average Directional Index which takes on values between 0 and 100

As defined previously.

0-25 Weak Trend
26-50 Strong Trend
51-75 Very Strong
76-100 Strongestng

We can plot the ADX using the following, where I first put the Directional Index columns to longer format using the pivot_longer function and then take a random sample of the grouped data using a combination of group_by, nest and sample_n. Finally I filter the data between a period of 3 months and use ggplot to plot the data.

# adx_calc %>%
#   pivot_longer(
#     cols = c("DIp", "DIn", "ADX"),
#     names_to = "Indicator",
#     values_to = "Indicator_value"
#     ) %>%
#   group_by(symbol) %>%
#   nest() %>%
#   ungroup() %>%
#   sample_n(2) %>%
#   unnest() %>%
#   filter(date > "2014-09-01" & date < "2015-01-01") %>%
#   ggplot(aes(x = date, y = Indicator_value, color = Indicator)) +
#   geom_line() +
#   geom_dl(aes(label = Indicator), method = list(dl.combine("first.points", "last.points"))) +
#   facet_wrap(~symbol, scales = "free") +
#   theme_tq()

We can create a simple trading strategy based on the ADX by:

• Adding a buy signal when the DIp is greater than the DIn and also when the ADX is greater than 30.
• We will hold the asset until the DIp is less than the DIn.
• This creates multiple consecutive rows of 1’s followed by consecutive rows of -1’s. Since we cannot “buy”, “buy” and “buy” and then followed by “sell”, “sell” and “sell” we will only take the first occurrence of the first “buy” and the first “sell”. I call this buy_here and sell_here.
# adx_signals <- adx_calc %>%
#   mutate(
#       DIp > DIn & ADX > 30 ~ 1,
#       TRUE ~ 0
#       ),
#       DIp < DIn ~ -1,
#       TRUE ~ 0
#       )
#     ) %>%
#   ungroup %>%
#   mutate(sell_here = -1 *(all(adx_signal_buy_hold_until == -1) * row_number() == 1)) %>%
#   ungroup %>%
#   select(-grp)`

Wilder, J Welles. 1978. New Concepts in Technical Trading Systems. Trend Research.