[prev in list] [next in list] [prev in thread] [next in thread] 

List:       r-sig-finance
Subject:    [R-SIG-Finance] Several quantstrat questions
From:       Ilya Kipnis <ilya.kipnis () gmail ! com>
Date:       2013-10-10 16:26:48
Message-ID: CA+oJuEEb-6mPn=YDrUkKyFZXuJo=HhmAGa+41VkY5M3=5-41Bw () mail ! gmail ! com
[Download RAW message or body]

Hello,

I have a couple of quantstrat questions.

1: For threshold type orders, what is the way to pass in thresholds as
parameters as arguments to applyStrategy (or applyParamset, but I'm not
there yet), rather than code them at runtime? Every example I remember has
simply hard-coded the threshold parameters beforehand, or even hardcoded
them as arguments to add.signal. I'd like to be able to optimize over them.
How would I do this? (Please see code below)

2: I am confused about quantstrat's exit behavior. In the Connors RSI demo
I wrote, my entry signals are to go long when CRSI crosses under 20, or
short when it crosses over 80. For my exits, I exit my short position when
said indicator crosses under 25, or over 75, and for my exits, I have
market orders to completely flatten my position. In other words, according
to my paper logic, there should never be a short entry on a net long
position, and vice versa. However, although I do see instances of the exits
working as intended, I also see cases of exits not triggering when they
should have, because I'm seeing hill-like formations (that is, my position
decreasing by my entry order quantity), rather than the steep drop-offs I
expect.

For example, here is an example of a parameter set I ran.


######################### FUNCTIONS FOR RUNNING DEMO ####################


# compute Connor's RSI, depends on RSI TTR function
connorsRSI <- function(HLC, nRSI=3, nStreak=2, nPercentLookBack=100){
  price=Cl(HLC)
  priceRSI <- RSI(price, nRSI)
  streakRSI <- RSI(computeStreak(price), nStreak)
  percents <- rollapply(price, nPercentLookBack, lastPercentRank)
  ret <- (priceRSI+streakRSI+percents)/3
  ret <- xts(c(rep(NA,nPercentLookBack-1),ret), order.by=index(HLC))
  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
  negDiffs[signs==-1] <- -1

  #create vector of cumulative sums and cum 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
  return (streak)
}

# ranks percentage returns from one point to the next
lastPercentRank <- function(priceSeries) {
  ranks <- rank(as.numeric(diff(log(priceSeries)))[-1])
  percentRanks <- ranks/length(ranks)
  lastVal <- round(percentRanks[length(percentRanks)]*100)
  return (lastVal)
}

############################# CONNORS RSI DEMO ###########################

require(quantstrat)

currency("USD")

symbols <- c("SPY","QQQ")
getSymbols(symbols,
           index.class=c("POSIXt","POSIXct"),
           from = startDate, to = endDate, adjust=TRUE)
stock(symbols, currency='USD', multiplier=1)

initDate <- '2006-12-31'
startDate <- '2007-01-01'
endDate <- '2011-08-31'
initEq = 100000
Sys.setenv(TZ="UTC") #Brian, why is this needed?


.orderqty=100
.txnFees=-5

# I thought you could specify these in the parameters...
# tells me object not found when I try that
# can I do the quote trick with threshold?
longEntryRSI <- 20
longExitRSI <- 75

myTheme <- chart_theme()
myTheme$col$dn.col <- 'lightblue'
myTheme$col$dn.border <- 'lightgray'
myTheme$col$up.border <- 'lightgray'

rm.strat("ConnorRSI")
rm.strat("ConnRSI")
initPortf(name="ConnorRSI",symbols=symbols,initDate=initDate)
initAcct(name="ConnorRSI",portfolios="ConnorRSI",initDate=initDate,initEq=initEq)
initOrders(portfolio="ConnorRSI",symbols=symbols,initDate=initDate)

strategy(name="ConnRSI",store=TRUE)

#indicator MUST be the same length as time series, or else we get a cryptic
error
add.indicator(strategy="ConnRSI", name="connorsRSI",
              arguments=list(HLC=quote(HLC(mktdata))),
              label="conRSI")

#entry signals
add.signal(strategy="ConnRSI",name="sigThreshold",
           arguments=list(threshold=longEntryRSI, column="conRSI",
relationship="lt", cross=TRUE),
           label="RSI.lt.lower")

add.signal(strategy="ConnRSI",name="sigThreshold",
           arguments=list(threshold=(100-longEntryRSI), column="conRSI",
relationship="gt", cross=TRUE),
           label="RSI.gt.upper")

#exit signals
add.signal(strategy="ConnRSI",name="sigThreshold",
           arguments=list(threshold=longExitRSI, column="conRSI",
relationship="gt", cross=TRUE),
           label="RSI.gt.exit.long")

add.signal(strategy="ConnRSI",name="sigThreshold",
           arguments=list(threshold=(100-longExitRSI), column="conRSI",
relationship="lt", cross=TRUE),
           label="RSI.lt.exit.short")


#long rules
add.rule(strategy="ConnRSI",name="ruleSignal",
         arguments=list(
           sigcol="RSI.lt.lower",
           sigval=TRUE,
           orderside='long',
           ordertype="stoplimit",
           prefer="Low",
           orderqty=.orderqty,
           TxnFees=.txnFees),
         type='enter',
         label='enter.long'
)

add.rule(strategy="ConnRSI",name='ruleSignal',
         arguments=list(
           sigcol="RSI.gt.exit.long",
           sigval=TRUE,
           orderside='long',
           ordertype='market',
           orderqty='all',
           TxnFees=.txnFees,
           replace=TRUE),
         type='exit',
         label='exit.long'
)

#short rules
add.rule(strategy='ConnRSI',name='ruleSignal',
         arguments=list(
           sigcol="RSI.gt.upper",
           sigval=TRUE,
           orderside='short',
           ordertype='stoplimit',
           prefer="High",
           orderqty=-.orderqty,
           TxnFees=.txnFees),
         type='enter',
         label='enter.short'
)

add.rule(strategy='ConnRSI',name='ruleSignal',
         arguments=list(
           sigcol="RSI.lt.exit.short",
           sigval=TRUE,
           orderside='short',
           ordertype='market',
           orderqty='all',
           TxnFees=.txnFees,
           replace=TRUE),
         type='exit',
         label='exit.short'
)

out <- applyStrategy(strategy='ConnRSI',portfolios="ConnorRSI",
                     parameters=list(
                       nPrice=3,
                       nStreak=2,
                       nPercentLookBack=100)
                     )

updatePortf(Portfolio="ConnorRSI",Dates=paste(startDate,endDate,sep="::"))
dateRange <- time(getPortfolio("ConnorRSI")$summary)[-1]
updateAcct(name="ConnorRSI",Dates=dateRange)
updateEndEq("ConnorRSI")

# I should put this function from Guy Yollin's slides elsewhere.
checkBlotterUpdate <- function(port.st,account.st,verbose=TRUE)
{
  ok <- TRUE
  p <- getPortfolio(port.st)
  a <- getAccount(account.st)
  syms <- names(p$symbols)
  port.tot <- sum(sapply(syms,FUN = function(x) eval(parse(
    text=paste("sum(p$symbols",x,"posPL.USD$Net.Trading.PL)",sep="$")))))
  initEq <- as.numeric(first(a$summary$End.Eq))
  endEq <- as.numeric(last(a$summary$End.Eq))
  if( !isTRUE(all.equal(port.tot,endEq-initEq)) ) {
    ok <- FALSE
    if( verbose )
      print("portfolio P&L doesn't match account P&L")
  }
  if( sum(duplicated(index(p$summary))) ) {
    ok <- FALSE
    if( verbose )
      print("duplicate timestamps in portfolio summary")
  }
  if( sum(duplicated(index(a$summary))) ) {
    ok <- FALSE
    if( verbose )
      print("duplicate timestamps in account summary")
  }
  return(ok)
}

checkBlotterUpdate("ConnorRSI","ConnorRSI")

acct <- getAccount(Account="ConnorRSI")
EndEq <- getEndEq(Account="ConnorRSI",endDate)
maxEq <- max(acct$summary$End.Eq)
(tStats <- tradeStats(Portfolios="ConnorRSI"))

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

chart.ME(Portfolio="ConnorRSI",Symbol="SPY",type="MAE", scale='percent')
chart.ME(Portfolio="ConnorRSI",Symbol="SPY",type="MFE", scale='percent')
chart.ME(Portfolio="ConnorRSI",Symbol="QQQ",type="MAE", scale='percent')
chart.ME(Portfolio="ConnorRSI",Symbol="QQQ",type="MFE")

#mostly intended behavior
chart.Posn(Portfolio="ConnorRSI",Symbol="SPY")

#very visible hill formation patterns
chart.Posn(Portfolio="ConnorRSI",Symbol="QQQ")

#unintended hilling behavior
chart.Posn("ConnorRSI","QQQ","2008-07-01::2009-07-15",theme=myTheme)

#hilling behavior and intended exits (drops)
chart.Posn("ConnorRSI","QQQ","2010-06-01::2011-07-01",theme=myTheme)

####################### END DEMO################

Long story short, I do not understand what causes this hill type behavior
to arise, which makes me hesitant to ever implement any strategy that has
potential for both long and short orders (AKA just about any remotely
sensible strategy). I'd like to understand what's going on here a little
bit better.

Thanks for any help.

	[[alternative HTML version deleted]]

_______________________________________________
R-SIG-Finance@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-sig-finance
-- Subscriber-posting only. If you want to post, subscribe first.
-- Also note that this is not the r-help list where general R questions should go.
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic