An important aspect of trading is knowing when to buy and sell in the market. There are many ways to do this. The first is by back testing. By looking at past data, analysts can predict how a company will perform in the future. This will dictate when they should buy and sell that stock. Another is through the Direction Volume Index (DVI). This is a predictor for S&P 500 over the past 30 years. DVI takes the magnitude of returns over different time windows smoothed and the relative number of up versus down days into account over different time windows. From this it can be used as a trend indicator. Finally, traders need to be aware of signaling. Signaling is the trigger for when to buy or sell stocks in the market. Trade signaling also helps modify portfolios by determining when it is a good time to buy more of a particular sector or offload more of another sector.
In the following code and analysis the team will develop 3 functions that only need inputs of ticker name, begin date and end date for the back testing, and threshold for DVI. The first function has a specific default constant DVI of 0.5. This function will be the baseline for the remaining function as it establishes an output summary that includes: total number of long trades, total number of short trades, percentage of time that the portfolio is long, the percentage of time that the portfolio is short, and the cumulative return from the strategy. We identify a long portfolio versus a short by applying the rule of DVI less than 0.5 as long, and a DVI greater than 0.5 as short. The second function applies the first function, but is able to simulate the back testing for multiple back test periods, given an input number of testing periods and date range. This second function will provide the average cumulative return across each iteration, while still using a constant DVI of 0.5. The third final function will allow us to simulate multiple DVI thresholds using the inputs of the low threshold, high threshold, and increment step size.
Performance Analytics Package - “Collection of econometric functions for performance and risk analysis. In addition to standard risk and performance metrics, this package aims to aid practitioners and researchers in utilizing the latest research in analysis of non-normal return streams”
https://cran.r-project.org/web/packages/PerformanceAnalytics/index.html
library(quantmod) # for return calculations
library(TTR) # for signals
library(dplyr) # for data management
library(PerformanceAnalytics) # for return metrics
library(lubridate)
library(ggplot2)
Trading strategies are important to obtain profits.A simple understanding of the concept is that if a strategy has done well in the past it is most likely going to do well in the future and vice verse. When a back testing is conducted correctly it yeild the user or trader a strategy that is going to yeild a positive output or profit.
main <- function(ticker, fromDate, toDate, defaultDvi) {
fromDate <- as.Date(fromDate , format = '%Y%m%d');
toDate <- as.Date(toDate , format = '%Y%m%d');
Price <- getSymbols(ticker, auto.assign = F, from = fromDate, to = toDate ,periodicity = "daily")
Price = Price[,4]
Retn <- periodReturn(Price, period = 'daily', type = 'arithmetic')
Retn <- na.omit(Retn)
dvi <- DVI(Price)
dvi <- na.omit(dvi)
Position <- ifelse(dvi$dvi < defaultDvi, 1, -1)
lagPosition <- Lag(Position)
lagPosition <- na.omit(lagPosition)
dfxts <- merge(Price, Retn, dvi$dvi,Position, lagPosition)
dfxts <- na.omit(dfxts)
colnames(dfxts) <- c("Price", "Retn", "dvi", "Position", "lagPosition")
df1 <- data.frame(Date = index(dfxts), coredata(dfxts))
df1 <- na.omit(df1)
df3 <- df1 %>% mutate( LongOrShort = ifelse( df1$Position == -1 & df1$lagPosition == 1 , "Long" , ifelse(
df1$Position == 1 & df1$lagPosition == -1 ,"Short" ,0) ))
totalNoOfLongTrades <- length(which(df3$LongOrShort=='Long'))
totalNoOfShortTrades <- length(which(df3$LongOrShort=='Short'))
percntTimeLong <- (length(which(df3$Position=='1'))/ length(df3$Position))*100
percntTimeShort <- (length(which(df3$Position=='-1'))/ length(df3$Position))*100
return_strategy = Retn*lagPosition
df <- data.frame(return_strategy)
df <- na.omit(df)
df$retplus = df$daily.returns + 1
cumulativeReturn <- tail(cumprod(df$retplus),1) -1
finalDataFrame <- data.frame(totalNoOfLongTrades,totalNoOfShortTrades,percntTimeLong,percntTimeShort,cumulativeReturn)
return(finalDataFrame)
}
finalData <- main("JNJ", "20140101", "20171231", 0.5)
finalData
## totalNoOfLongTrades totalNoOfShortTrades percntTimeLong percntTimeShort
## 1 30 30 48 52
## cumulativeReturn
## 1 -0.1670116
Applying the rule of long when DVI < 0.5 and short otherwise. The dataframe obtained shows the total number of long trades and short trades at 30 each. While the cumulative return is at -0.167. The required output was for the company Jonson & Jonson, with a ticker of JNJ, for the time period of Jan 1st 2014 to December 31st 2017.
multipleBackTest <- function(ticker , testPeriods , years , dvi ){
counter <- 0
y <- years
x <- as.numeric(y[-1]) - as.numeric(y[1]) +1
for ( i in 1: x ){
from <- make_date((as.numeric(years[1] )+ counter ),01,01)
to <- make_date((as.numeric(years[1] ) + counter + testPeriods -1))
if(to > make_date((as.numeric(y[-1]) ),12,31)){
break
}
df1 <- main(ticker , from , to , 0.5 )
df1 <- df1 %>% mutate(fromDate = from ) %>% mutate(toDate = to)
if(counter == 0){
df <- df1
}
else{
df<- rbind(df,df1)
}
counter<- counter +1
}
df$Period <- paste(format(as.Date(df$fromDate, format="%d/%m/%Y"),"%Y"),format(as.Date(df$toDate, format="%d/%m/%Y"),"%Y"),sep ="-")
return(df)
}
df4<-multipleBackTest("JNJ",3, c("2010","2016"),0.5)
df4
## totalNoOfLongTrades totalNoOfShortTrades percntTimeLong percntTimeShort
## 1 10 10 53.06122 46.93878
## 2 5 5 44.13793 55.86207
## 3 5 5 60.00000 40.00000
## 4 6 7 55.10204 44.89796
## 5 8 7 44.89796 55.10204
## cumulativeReturn fromDate toDate Period
## 1 0.22180437 2010-01-01 2012-01-01 2010-2012
## 2 0.04567274 2011-01-01 2013-01-01 2011-2013
## 3 -0.01543197 2012-01-01 2014-01-01 2012-2014
## 4 -0.14402074 2013-01-01 2015-01-01 2013-2015
## 5 -0.00367772 2014-01-01 2016-01-01 2014-2016
df4 %>% mutate(Color = ifelse(cumulativeReturn <0, "red","blue")) %>%
ggplot(aes(x = Period, y = cumulativeReturn, fill = Color))+
geom_col()+
geom_text(aes(label=cumulativeReturn), vjust=1.5, color="black", size=3)+
ggtitle("Average Cumulative Return by Period")+
scale_fill_identity(guide = FALSE)
Simulating multiple back testing periods, considering all the possible
period intervals, with a default DVI of 0.5. We obtain the bar graph and
the data frame shown above.In the data frame, we can see the total
number of Short and Long trades along with their periods and their
Cumulative Return. The same is visualized in the bargraph for a better
understanding.
multipleBackTestWithDvi <- function(ticker,fromDate, toDate , intitailDvi ,finalDvi, steps){
for(i in seq(intitailDvi, finalDvi, steps)){
df1 <- main(ticker,fromDate,toDate,i)
df1$dviThreshold <- i
if(i == intitailDvi){df <- df1}
else{df <- rbind(df,df1)}
}
return(df)
}
df <- multipleBackTestWithDvi("JNJ", "20140101", "20171231", 0.4, 0.6, 0.01)
df
## totalNoOfLongTrades totalNoOfShortTrades percntTimeLong percntTimeShort
## 1 30 30 38.46154 61.53846
## 2 29 29 39.38462 60.61538
## 3 29 29 40.46154 59.53846
## 4 28 28 41.38462 58.61538
## 5 28 28 42.15385 57.84615
## 6 29 29 43.23077 56.76923
## 7 29 29 44.00000 56.00000
## 8 28 28 44.92308 55.07692
## 9 29 29 46.30769 53.69231
## 10 29 29 47.07692 52.92308
## 11 30 30 48.00000 52.00000
## 12 30 30 49.23077 50.76923
## 13 31 31 49.84615 50.15385
## 14 32 32 51.69231 48.30769
## 15 33 33 53.38462 46.61538
## 16 33 33 54.46154 45.53846
## 17 31 31 55.38462 44.61538
## 18 30 30 56.30769 43.69231
## 19 31 31 57.38462 42.61538
## 20 34 34 58.30769 41.69231
## 21 32 32 59.84615 40.15385
## cumulativeReturn dviThreshold
## 1 0.029467250 0.40
## 2 -0.014840823 0.41
## 3 0.013626310 0.42
## 4 -0.032340622 0.43
## 5 -0.036423881 0.44
## 6 -0.016490685 0.45
## 7 -0.046668649 0.46
## 8 -0.101722105 0.47
## 9 -0.114357188 0.48
## 10 -0.162615995 0.49
## 11 -0.167011579 0.50
## 12 -0.147489727 0.51
## 13 -0.111696884 0.52
## 14 -0.040239974 0.53
## 15 -0.014640373 0.54
## 16 0.008646664 0.55
## 17 -0.004821547 0.56
## 18 -0.002753345 0.57
## 19 0.057067132 0.58
## 20 0.168874665 0.59
## 21 0.163403285 0.60
df %>% mutate(Color = ifelse(cumulativeReturn <0, "red","blue")) %>%
ggplot(aes(x = dviThreshold, y = cumulativeReturn, fill = Color))+
geom_col()+
scale_fill_identity(guide = FALSE)
When developing future financial functions for trading rules, we will want to carefully consider and address the response time to execute a trade following a signal indicator. Once we receive the signal to perform a trade, the scaling of that trade can also be important to understand. If the ticker has a high liquidity of the market, a large trade will not have as much of a ripple effect and upset the market then if the company is very illiquid. A penalty to not trading a high enough quantity can be relative to how high the transaction costs are for each sale. It could be useful to incorporate multiple signals to help the algorithm see a larger perspective of data that could lead to a more accurate portfolio prediction. Similarly, an analyst can ensure lower risk across the board by implementing a large diversification classification of companies. It is beneficial to stay away from large amounts of trading on one specific portfolio and to attempt to hedge your funds by incorporating many different firms at a smaller scale. Finally, when it comes to automating the finalized trading rules, one should be careful to have some sort of human intervention if the dollar amount of the transaction reaches a certain level.