[R-SIG-Finance] rule delays

Stephen Choularton stephen at organicfoodmarkets.com.au
Fri May 27 08:43:41 CEST 2016


Hi

I am trying to implement Harry's Connor's RSI from his book Quantative 
Trading ...

I have it working as is and have varied it to some shares in the ASX.

However, I want to pursue the idea of a fixed time (8 day) sell rule 
that takes place if the normal rule doesn't produce a sale before.

I added this rule at line 127 of the file 3. strategy.R:

add.rule(strategy.st, name = "ruleSignal",
     arguments = list(sigcol = "longExit",
     sigval = TRUE,
     orderqty = "all", delay = 691200,
     ordertype = "market",
     orderside = "long", TxnFees = txnFees,
     replace = FALSE,
     prefer = "Open"), type= "exit", path.dep = TRUE)

but it seems to have no effect.

I attach all the code. You run it in the numbered order of the files.

I wonder if anyone can help me make this sort of rule work.

----------------------------------------------------------------------------------------------------------------------------------- 

Stephen Choularton PhD, FIoD
0413 545 182
-------------- next part --------------
######### FUNCTIONS #########################

######### CONNOR SRI FUNCTIONS ##########################################

library(TTR)

# compute Connor's RSI, depends on the RSI TTR function

connorsRSI <- function(price, nRSI = 3, nStreak = 2, nPercentLookBack = 100){

	priceRSI <- RSI(price, nRSI)
	streakRSI <- RSI(computeStreak(price), nStreak)
	percents <- round(runPercentRank(x = diff(log(price)), n = 100, cumulative = FALSE, exact.multiplier = 1) * 100)
	ret <- (priceRSI + streakRSI + percents) /3
	colnames(ret) <- "connorsRSI"
	return(ret)

}

# computes a running streak of positives and negatives of price changes

computeStreak <- function(priceSeries){

	signs <- sign(diff(priceSeries))
	posDiffs <- negDiffs <- rep(0, length(signs))
	posDiffs[signs == 1] <- 1
	posDiffs[signs == -1] <- -1

	# create vector of cumulatives sums and cumulative sums not incremented during streaks. Zero out any leading NAs after na.locf
	posCum <- cumsum(posDiffs)
	posNAcum <- posCum
	posNAcum[posDiffs == 1] <- NA
	posNAcum <- na.locf(posNAcum, na.rm = FALSE)
	posNAcum[is.na(posNAcum)] <- 0
	posStreak <- posCum - posNAcum

	# repeat for negative cumulative sums
	negCum <- cumsum(negDiffs)
	negNAcum <- negCum
	negNAcum[negDiffs == -1] <- NA
	negNAcum <- na.locf(negNAcum, na.rm = FALSE)
	negNAcum[is.na(negNAcum)] <- 0
	negStreak <- negCum - negNAcum

	streak <- posStreak + negStreak
	streak <- xts(streak, order.by = index(priceSeries))

}

sigAND <- function(label, data=mktdata, columns, cross = FALSE){

	ret_sig = NULL
	colNums <- rep(0, length(columns))
	for(i in 1:length(columns)){
		colNums[i] <- match.names(columns[i], colnames(data))
	}
	ret_sig <- data[, colNums[1]]
	if(isTRUE(cross)){
		ret_sig <- diff(ret_sig) == 1
	}
	colnames(ret_sig) <- label
	return(ret_sig)
}

cumCRSI <- function(price, nCum = 2, ...) {

	CSRI <- connorsRSI(price, ...)
	out <- runSum(CSRI, nCum)
	colnames(out) <- "cumCRSI"
	out

}






###########################################################################

######### ATR ORDER SIZING FUNCTIONS #######################################

lagATR <- function(HLC, n = 14, maType, lag = 1, ...) {
	ATR <- ATR(HLC, n = n, maType = maType, ...)
	ATR <- lag(ATR, lag)
	out <- ATR$atr
	colnames(out) <- "atr"
	return(out)
}

osDollarATR <- function(orderside, tradeSize, pctATR, maxPctATR = pctATR, data, timestamp, symbol, prefer = "Open", portfolio, integerQty = TRUE, strMod = "", rebal = FALSE, ...) {

	if(tradeSize > 0 & orderside == "short") {
		tradeSize <- tradeSize * -1
	}

	pos <- getPosQty(portfolio, symbol, timestamp)
	atrString <- paste0("atr", strMod)
	atrCol <- grep(atrString, colnames(mktdata))

	if(length(atrCol) == 0) {
		stop(paste("Term", atrString, "not found in mktdata column names."))
	}

	atrTimeStamp <- mktdata[timestamp, atrCol]
	if(is.na(atrTimeStamp) | atrTimeStamp == 0){
		stop(paste("ATR corresponding to ", atrString, " is invalid at this point in time.  Add a logical operator to account for this."))
	}

	dollarATR <- pos * atrTimeStamp
	desiredDollarATR <- pctATR * tradeSize
	remainingRiskCapacity <- tradeSize * maxPctATR - dollarATR

	if(orderside == "long") {
		qty <- min(tradeSize * pctATR / atrTimeStamp, remainingRiskCapacity / atrTimeStamp)
	} else {
		qty <- max(tradeSize * pctATR / atrTimeStamp, remainingRiskCapacity / atrTimeStamp)
	}

	if(integerQty) {
		qty <- trunc(qty)
	}

	if(!rebal){
		if(orderside == "long" & qty < 0) {
			qty <- 0
		} 
		if(orderside == "short" & qty > 0){
			qty <- 0
		}
	}

	if(rebal) {
		if(pos == 0) {
			qty <- 0
		}
	}

	return (qty)
}


-------------- next part --------------
# do I need these

library(quantstrat)
library(PerformanceAnalytics)
library(blotter)
library(TTR)

# trade size and initial equity settings ???????
#tradeSize <- 10000
#initEq <- tradeSize * length(symbols)


#rm(list = ls(.blotter), envir = .blotter)
initDate = "1990-01-01"
from = "2003-01-01"
to = "2013-12-31"
#initEq = 10000

#currency("USD")
#Sys.setenv(TZ="UTC")

# suppress warnings
options("getSymbols.warning4.0" = FALSE)

# do some house cleaning
rm(list = ls(.blotter), envir = .blotter)

# set the current and time zone
#currency(primary_id = 'USD') # must be first thing specified
#Sys.setenv(TZ = "UTC")

# define the symbols of interest
symbols <- c("AMP.AX",
		"ANZ.AX",
		"BHP.AX",
		"BXB.AX",
		"CBA.AX",
		"CSL.AX",
		"IAG.AX",
		"MQG.AX",
		"NAB.AX",
		"QBE.AX",
		"RIO.AX",
		"SCG.AX",
		"SUN.AX",
		"TCL.AX",
		"TLS.AX",
		"WBC.AX",
		"WES.AX",
		"WFD.AX",
		"WOW.AX",
		"WPL.AX"
		)


# SPDR EFTs first, iShares EFT's afterwards
#if(!"XLB" %in% ls()){
	#if data is not present get it from yahoo
	suppressMessages(getSymbols(symbols, from = from, to = to, src = "yahoo", adjust = TRUE))
#}

#  define the instrument type
stock(symbols, currency = "AUD", multiplier = 1)








-------------- next part --------------
# NEED TO ADD BROKERAGE AND SLIPPAGE?

########## SETUP AND BACKTEST ###############

########### SETUP #############################

#library(quantstrat)
#library(PerformanceAnalytics)
#library(blotter)
#library(TTR)

# trade size and initial equity settings ???????
tradeSize <- 10000
initEq <- tradeSize * length(symbols)
txnFees <- tradeSize * .0008 * -1

rm(list = ls(.blotter), envir = .blotter)
#initDate = "1990-01-01"
#from = "2003-01-01"
#to = "2013-12-31"
#initEq = 10000

currency("AUD")
Sys.setenv(TZ="UTC")

######## DATA SOURCE #########################################
#
# Just pasted in demoData.R at this point
#
###############################################################

#rm(list = ls("portfolio.st", "strategy.st", "account.st"))

strategy.st <- "CRSIcumStrat"
portfolio.st <- "CRSIcumStrat"
account.st <- "CRSIcumStrat"

rm.strat(portfolio.st)
rm.strat(strategy.st)


.blotter <- new.env()
.strategy <- new.env()



initPortf(portfolio.st, symbols=symbols, initDate = initDate, currency = 'USD')

initAcct(account.st, portfolios = portfolio.st, initDate = initDate, currency = 'USD', initEq = initEq)

initOrders(portfolio.st, initDate = initDate)

strategy(strategy.st, store = TRUE)



#### PARAMETERS #########################

cumThresh <- 40
exitThresh <- 75
nCum <- 2
nRSI <- 3
nStreak <- 2
nPercentLookBack <- 100
nSMA <- 200
pctATR <- .02
period <- 10

#### INDICATORS #########

add.indicator(strategy.st, name = "cumCRSI", 
	arguments = list(price = quote(Cl(mktdata)), nCum = nCum, 
	nRSI = nRSI, nStreak = nStreak, 
	nPercentLookBack = nPercentLookBack),
	label = "CRSIcum")

add.indicator(strategy.st, name = "connorsRSI",   
	arguments = list(price= quote(Cl(mktdata)), nRSI = nRSI,
	nStreak = nStreak,
	nPercentLookBack = nPercentLookBack),
	label= "CRSI")

add.indicator(strategy.st, name = "SMA", 
	arguments = list(x = quote(HLC(mktdata)), n = nSMA),
	label = "sma")

add.indicator(strategy.st, name = "lagATR",
	arguments = list(HLC = quote(HLC(mktdata)), n = period),
	label = "atrX")


test <- applyIndicators(strategy.st, mktdata = OHLC(CBA.AX))

#head(round(test, 2 ), 253)
head(round(test, 2 ), 10)

########## SIGNALS ######################

add.signal(strategy.st, name = "sigThreshold",
	arguments = list(column = "cumCRSI.CRSIcum",
	threshold = cumThresh, relationship = "lt", cross = FALSE),
	label = "cumCRSI.lt.thresh")

add.signal(strategy.st, name = "sigComparison",
	arguments = list(columns = c("Close", "SMA.sma"),
	realtionship = "gr"), label = "Cl.gt.SMA")

add.signal(strategy.st, name = "sigAND",
	arguments = list(columns = c("cumCRSI.lt.thresh",
	"Cl.gt.SMA"), cross = TRUE), label = "longEntry")

add.signal(strategy.st, name = "sigThreshold",
	arguments = list(column = "connorsRSI.CRSI",
	threshold = exitThresh, relationship = "gt",
	cross = TRUE), label = "longExit")

########## RULES ################################

add.rule(strategy.st, name = "ruleSignal",
	arguments = list(sigcol = "longEntry", 
	sigval = TRUE, ordertype = "market",
	orderside = "long", TxnFees = txnFees, replace = FALSE,
	prefer = "Open", osFUN = osDollarATR,
	tradeSize = tradeSize, pctATR= pctATR,
	atrMod = "X"), type = "enter", path.dep = TRUE)

add.rule(strategy.st, name = "ruleSignal",
	arguments = list(sigcol = "longExit",
	sigval = TRUE,
	orderqty = "all", delay = 691200,
	ordertype = "market",
	orderside = "long", TxnFees = txnFees,
	replace = FALSE, 
	prefer = "Open"), type= "exit", path.dep = TRUE)



add.rule(strategy.st, name = "ruleSignal",
	arguments = list(sigcol = "longExit",
	sigval = TRUE,
	orderqty = "all",
	ordertype = "market",
	orderside = "long", TxnFees = txnFees,
	replace = FALSE,
	prefer = "Open"), type= "exit", path.dep = TRUE)




######### APPLY #########################################

	t1 <- Sys.time()
	out <- applyStrategy(strategy = strategy.st, 
		portfolio = portfolio.st)
	t2 <- Sys.time()
	print(t2 - t1)

-------------- next part --------------

######### ANALYTICS ######################################

updatePortf(portfolio.st)
dateRange <- time(getPortfolio(portfolio.st)$summary)[-1]
updateAcct(portfolio.st, dateRange)
updateEndEq(account.st)

tStats <- tradeStats(Portfolios = portfolio.st, use = "trades", inclZeroDays = FALSE)
tStats[ ,4:ncol(tStats)] <- round(tStats[ ,4:ncol(tStats)], 2)
print(data.frame(t(tStats[ ,-c(1,2)])))
	

######### EVALUATE #####################################

aggPF <- sum(tStats$Gross.Profits) / - sum(tStats$Gross.Losses)
aggCorrect <- mean(tStats$Percent.Positive)
numTrades <- sum(tStats$Num.Trades)
meanAvgWLR <- mean(tStats$Avg.WinLoss.Ratio[tStats$Avg.WinLoss.Ratio < Inf], na.rm = TRUE)

# win to lose ratio
aggPF

#proportion winning trades
aggCorrect

#number of trades
numTrades

meanAvgWLR


############ GENERAL PERFORMANCE ################################

dStats <- dailyStats(Portfolios = portfolio.st, use = "Equity")
rownames(dStats) <- gsub(".DailyEndEq", "", rownames(dStats))
print(data.frame(t(dStats)))

############ ANALYTICS RELATING TO DURATION ######################

durationStatistics <- function(Portfolio, Symbols, includeOpenTrade = FALSE, ...){

	tmp <- list()
	length(tmp) <- length(Symbols)
	for(Symbol in Symbols){
	
		pts <- perTradeStats(Portfolio = Portfolio, Symbol = Symbol, includeOpenTrade = includeOpenTrade)
		pts$diff <- pts$End - pts$Start
		
		durationSummary <- summary(as.numeric(pts$diff))
		winDurationSummary <- summary(as.numeric(pts$diff[pts$Net.Trading.PL > 0]))
		lossDurationSummary <- summary(as.numeric(pts$diff[pts$Net.Trading.PL <= 0]))
		names(durationSummary) <- c("Min", "Q1", "Med", "Mean", "Q3", "Max")
		names(winDurationSummary) <- c("Min", "Q1", "Med", "Mean", "Q3", "Max")
		names(lossDurationSummary) <- c("Min", "Q1", "Med", "Mean", "Q3", "Max")
		names(winDurationSummary) <- paste0("W", names(winDurationSummary))
		names(lossDurationSummary) <- paste0("L", names(lossDurationSummary))
		
		dataRow <- data.frame(cbind(t(round(durationSummary)),
			t(round(winDurationSummary)),
			t(round(lossDurationSummary))))
		tmp[[Symbol]] <- dataRow
		
	}
	
	out <- do.call(rbind, tmp)
	return(out)
	
}

durStats <- durationStatistics(Portfolio = portfolio.st, Symbols=sort(symbols))
print(t(durStats))

############ MARKET EXPOSURE ##################################

tmp <- list()
length(tmp) <- length(symbols)
for(i in 1:nrow(dStats)){
	totalDays <- nrow(get(rownames(dStats)[i]))
	mktExposure <- dStats$Total.Days[i] / totalDays
	tmp[[i]] <- c(rownames(dStats)[i], round(mktExposure, 3))
}
mktExposure <- data.frame <-(do.call(rbind, tmp))
colnames(mktExposure) <- c("Symbol", "MktExposure")

print(mktExposure)

print(mean(as.numeric(mktExposure[ , 2])))


######### CHART AND STATS ###############################



instRate <- PortfReturns(account.st)

portfRets <- xts(rowMeans(instRate) * ncol(instRate), order.by = index(instRate))
cumPortfRets <- cumprod(1 + portfRets)
firstNonZeroDay <- as.character(index(portfRets)[min(which(portfRets != 0))])

# obtain symbol
getSymbols("^AXJO", from = firstNonZeroDay, to = to)
#getSymbols("SPY", from = from, to = to)
AXJOrets <- diff(log(Cl(AXJO)))[-1]
cumAXJOrets <- cumprod(1 + AXJOrets)
comparison <- cbind(cumPortfRets, cumAXJOrets)
colnames(comparison) <- c("Connors RSI trend following strategy", "SPI")

chart.TimeSeries(comparison, legend.loc = "topleft")



##################### CALCULATE THE RISK METRICS ##########################

SharpeRatio.annualized(portfRets)

Return.annualized(portfRets)

maxDrawdown(portfRets)

# win to lose ratio
aggPF

#proportion winning trades
aggCorrect

#number of trades
numTrades

meanAvgWLR

################### EQUITY CURVE OF XLB ##################################

#chart.Posn(portfolio.st, "XLB")


More information about the R-SIG-Finance mailing list