Search code examples
rquantmodquantstrat

Stop Adding More Positions Once I have A Position On In Quantstrat R


I am working on a basic RSI trading signal. Buy 100 shares when stock goes below 20 RSI and close position when stock goes above 80 RSI.

What's happening is, once the stock goes below 20 I buy 100 shares, if the stock crosses below 20 again without first going to 80 RSI, I end up buying another 100 shares (total 200).

Once I have a position on, I do not want to add to it. Thank you.

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

#setup
Sys.setenv(TZ = "UTC")
stock.str = "AAPL"
currency('USD')
stock("AAPL", currency= "USD", multiplier = 1)

initDate = "2010-01-01"
startDate = "2011-01-01"
to = Sys.Date()
initEq = 100000

portfolio.st = account.st = strategy.st = 'rsi'

getSymbols("AAPL", from = initDate)

initPortf(portfolio.st, symbols = stock.str,
          initDate = initDate)
initAcct(account.st,
         portfolio.st,
         initDate = initDate, initEq = initEq)
initOrders(portfolio.st, initDate = initDate)
strategy(strategy.st, store = T)

add.indicator(strategy.st, 
              name = "RSI",
              arguments = list(
                    price = quote(Cl(mktdata)),
                    n = 14,
                    maType = "EMA"
              ),
              label = "rsi14")
add.signal(strategy.st,
           name = "sigThreshold",
           arguments = list(
                 column = "rsi14",
                 threshold = 20,
                 cross = T,
                 relationship = "lt"

           ),
           label = "crossBelow")
add.signal(strategy.st,
           name = "sigThreshold",
           arguments = list(
                 column = "rsi14",
                 threshold = 80,
                 cross = T,
                 relationship = "gt"
           ),
           label = "crossAbove")

add.rule(strategy.st,
         name = "ruleSignal",
         arguments = list(
               sigcol = "crossBelow",
               sigval = T,
               orderqty = 100,
               ordertype = "market",
               orderside = "long"

         ),
         type = "enter",
         label = "enter")
add.rule(strategy.st,
         name = "ruleSignal",
         arguments = list(
               sigcol = "crossAbove",
               sigval = T, 
               orderqty = "all",
               ordertype = "market",
               orderside = "long"),
         type = "exit",
         label = "exit"
         )
out = applyStrategy(strategy.st,
                    portfolio.st)

Solution

  • One thing you could do is consider writing your own order sizing function. This gets passed to quanstrat in add.rule as a parameter - osFUN.

    Something like this:

    my_sizing_fun <- function(data, timestamp, symbol, portfolio, ...) {
    
    
        equity <- getEndEq(strategy.st, Date = timestamp) 
    
        # Get current Position and return 0 if we already have a position
        pos <- getPosQty(portfolio, symbol, timestamp) 
        if(pos != 0) {
          return(0)
        } else {
    
        return(100)
        }
    

    This will return 0 if you already have a position, or 100 otherwise. You can do some pretty complicated stuff inside the order sizing functions to help enhance your strategy.

    Now just add osFUN=my_sizing_fun as an argument inside add.rule and remove orderqty and you should be all set.

    When you start to want to play around with going short or dealing with your current equity values the following is a helpful example:

    ### Order Size Function ### 
    ## Calculates Order Size as a percent risk of account equity - currently does not account for multiple symbols or a max trade size like we may implement in real life
    ## Takes in arguments passed from 'RuleSignal'
    ## riskPct is the percentage of account equity to risk per trade
    ## Returns the order quantity as a numeric value
    ## to do - round order qty to lot sizes
    osATR <- function(data, orderside, timestamp, symbol, portfolio, riskPct, ...) {
    
      # Update Accounts and get the Ending Equity
      updatePortf(strategy.st)
      updateAcct(strategy.st)
      updateEndEq(strategy.st)
    
      equity <- getEndEq(strategy.st, Date = timestamp) 
    
      # Get current Position and return 0 if we already have a position
      pos <- getPosQty(portfolio, symbol, timestamp) 
      if(pos != 0) {
        return(0)
      }
    
    
      # Calculate Order Quantity
      qty <- # Add your logic here
      qty <-  as.numeric(trunc(qty)) # return a numeric and round the qty
    
      # Long / Short Check & Set
      if(orderside == "short") {
        qty <- -qty
      } 
    
      # Make sure not to end up with net positions on the wrong side
      if (orderside == 'long' && qty < 0 | orderside == 'short' && qty > 0) {
        stop("orderqty is of the wrong sign")
      }
    
      return(qty)
    }