3

I currently have my backtest set up so that it will invest 25% of my current equity. The problem this is causing is when my capital starts to grow the strategy starts taking on enormous trades.

How do I specify for it do invest Equiuty*0.25 but only up to a limit of say 1000 contracts?

Below is the relevant parts of my code.

Thank you for any help. This has been frustrating me for a few weeks now.

osInvestAll <- function (data, timestamp, orderqty, ordertype, orderside, equity, portfolio, symbol, ruletype, ..., orderprice, MaxPosn) 
{   
datePos <- format(timestamp,"%Y-%m-%d")

  updatePortf(Portfolio=portfolio,Symbol=symbol,Dates=datePos)
updateAcct(portfolio,Dates=datePos)   
updateEndEq(portfolio,Dates=datePos)     
Posn <- getPosQty(portfolio,Symbol=symbol,Date=datePos)
equity <- getEndEq(portfolio,datePos) 
ClosePrice <- getPrice(get(symbol))[datePos]
UnitSize <- as.numeric(trunc(0.25*equity/ClosePrice))



 osMaxPos <-function(data, timestamp, orderqty, ordertype, orderside, portfolio, symbol, ruletype, ...)
 addPosLimit(portfolio = portfolioname,symbol = symbollist,maxpos = 100, minpos = -100,timestamp =  as.POSIXct(init.date))

 if (Posn == 0) { 
    osInvestAll <- UnitSize } else
        {osInvestAll <- 0
         }

This is how I have my rule at the moment but I get an error saying "unitsize not found"

add.rule(strategyname,name='ruleSignal',
arguments = list(sigcol="longentry", sigval=TRUE,
replace=FALSE,
prefer='open',
orderside='long',
ordertype='market',
orderqty=unitsize,
orderset='ocolong',
osFUN = "osMaxPos",
maxSize='PosLimit'
),
type='enter',
label='LE'
)

My original rule (when it was working but taking on huge positions) before trying to change it

add.rule(strategyname,name='ruleSignal',
arguments = list(sigcol="longentry", sigval=TRUE,
replace=FALSE,
prefer='open',
orderside='long',
ordertype='market',
orderqty=1,
orderset='ocolong',
osFUN = "osInvestAll",
maxSize='PosLimit'
),
type='enter',
label='LE'
)
Cameron Giles
  • 72
  • 1
  • 8

1 Answers1

0

Here is a reproducible example that I think does what you seek?

The strategy simply enters a long position if the RSI of the SPY falls below 60, and exits the entire long position if the RSI crosses above 70. The max number of units that the long position can be is 1000 units.

Your code in osInvestAll has some incorrect/redundant code, which I've omitted. This is a clean minimal order sizing function that(I think) does what you want.

Also, just a tip: you don't need to call updateAcct and updateEndEq on each long entry, in order to trade the current equity for the symbol in question, as this will add unnecessary extra computational time in bigger simulations.

osInvestAll <- function (data, timestamp, orderqty, ordertype, orderside, equity, portfolio, symbol, ruletype, ..., initEq) {
    datePos <- format(timestamp,"%Y-%m-%d")

    updatePortf(Portfolio=portfolio,Symbol=symbol,Dates=paste0(start(data), "/", datePos))
    # After updating portfolio profit, we can extract the Net.Trading.PL earned up to datePos.
    trading_pl <- sum(.getPortfolio(portfolio)$summary$Net.Trading.PL)
    # The total equity in the strategy for this symbol (and this symbol only in isolation always, as this is how quantstrat by default works with applyStrategy)
    equity <- initEq + trading_pl
    ClosePrice <- getPrice(data, prefer = "Close")[datePos]
    UnitSize <- as.numeric(trunc(0.25 * equity / ClosePrice))

    UnitSize <- osMaxPos(data, timestamp, UnitSize, ordertype, orderside, portfolio, symbol, ruletype, digits=0)
    UnitSize
}


library(quantstrat)


suppressWarnings(rm("order_book.RSI",pos=.strategy))
suppressWarnings(rm("account.RSI","portfolio.RSI",pos=.blotter))
suppressWarnings(rm("account.st","portfolio.st","stock.str","stratRSI","startDate","initEq",'start_t','end_t'))


strategy.st <- "RSI"

stratRSI <- strategy(strategy.st, store = TRUE)


add.indicator(strategy = strategy.st, name = "RSI", arguments = list(price = quote(getPrice(mktdata))), label="RSI")
add.signal(strategy = strategy.st, name="sigThreshold",arguments = list(threshold=70, column="RSI",relationship="gt", cross=TRUE),label="RSI.gt.70")

add.signal(strategy = strategy.st, name="sigThreshold",arguments = list(threshold=60, column="RSI",relationship="lt",cross=TRUE),label="RSI.lt.60")


add.rule(strategy = strategy.st, name='ruleSignal', arguments = list(sigcol="RSI.lt.60", sigval=TRUE, orderqty= 100, TxnFees=0, ordertype='market', orderside='long', pricemethod='market', replace=FALSE, osFUN=osInvestAll), type='enter', path.dep=TRUE)
add.rule(strategy = strategy.st, name='ruleSignal', arguments = list(sigcol="RSI.gt.70", sigval=TRUE, orderqty='all', TxnFees=0, ordertype='market', orderside='long', pricemethod='market', replace=FALSE), type='exit', path.dep=TRUE)


currency("USD")
symbols = c("SPY")
stock.str = symbols

startDate <- "1987-01-01"



getSymbols(stock.str,from=startDate, to= Sys.Date())



#getSymbols(stock.str,from=startDate, to= Sys.Date())

for(symbol in symbols){
    stock(symbol, currency="USD",multiplier=1)
}

SPY <- SPY["2015/"]


startDate='1999-12-31'
initEq=100000
port.st<-'RSI'

initPortf(port.st, symbols=symbols)
initAcct(port.st, portfolios=port.st, initEq=initEq)
initOrders(portfolio=port.st)

# Must add maxpos:
for(symbol in symbols){ addPosLimit(port.st, symbol, timestamp = startDate, maxpos = 1000) }

applyStrategy(strategy=strategy.st, portfolios=port.st, initEq = initEq)

# > applyStrategy(strategy=strategy.st, portfolios=port.st, initEq = initEq)
# [1] "2015-03-01 19:00:00 SPY 118 @ 211.990005"
# [1] "2015-03-03 19:00:00 SPY 118 @ 210.229996"
# [1] "2015-05-25 20:00:00 SPY 117 @ 210.699997"
# [1] "2015-10-21 20:00:00 SPY 119 @ 205.210007"
# [1] "2015-11-09 19:00:00 SPY 119 @ 208.559998"
# [1] "2015-12-02 19:00:00 SPY 119 @ 205.610001"
# [1] "2016-03-08 19:00:00 SPY 116 @ 199.380005"
# [1] "2016-04-05 20:00:00 SPY 119 @ 206.419998"
# [1] "2016-04-07 20:00:00 SPY 55 @ 204.5"
# [1] "2016-11-27 19:00:00 SPY -1000 @ 220.479996"
# [1] "2016-12-01 19:00:00 SPY 129 @ 219.679993"
# [1] "2016-12-07 19:00:00 SPY -129 @ 225.149994"
# [1] "2016-12-28 19:00:00 SPY 127 @ 224.350006"
# [1] "2017-01-09 19:00:00 SPY 126 @ 226.460007"
# [1] "2017-01-12 19:00:00 SPY 126 @ 227.050003"
# [1] "2017-01-17 19:00:00 SPY 126 @ 226.75"
# [1] "2017-01-30 19:00:00 SPY 126 @ 227.529999"
# [1] "2017-02-13 19:00:00 SPY -631 @ 233.699997"
# [1] "2017-03-14 20:00:00 SPY 125 @ 238.949997"
# [1] "2017-03-19 20:00:00 SPY 124 @ 236.770004"
# [1] "2017-04-30 20:00:00 SPY 124 @ 238.679993"
# [1] "2017-05-14 20:00:00 SPY 124 @ 240.300003"
# [1] "2017-05-17 20:00:00 SPY 124 @ 236.770004"
# [1] "2017-06-04 20:00:00 SPY -621 @ 243.990005"
# [1] "2017-06-18 20:00:00 SPY 125 @ 244.660004"
# [1] "2017-06-20 20:00:00 SPY 125 @ 242.949997"
# [1] "2017-08-10 20:00:00 SPY 125 @ 244.119995"
# [1] "2017-09-05 20:00:00 SPY 124 @ 246.899994"
# [1] "2017-09-21 20:00:00 SPY 124 @ 249.440002"

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



tradeStats(port.st, "SPY")


 # Portfolio Symbol Num.Txns Num.Trades Net.Trading.PL Avg.Trade.PL Med.Trade.PL Largest.Winner Largest.Loser Gross.Profits Gross.Losses Std.Dev.Trade.PL Std.Err.Trade.PL Percent.Positive Percent.Negative Profit.Factor
# SPY       RSI    SPY       29          4       24581.97     5548.314     4063.635       13360.36             0      22193.25            0         5460.273         2730.136              100                0            NA
# Avg.Win.Trade Med.Win.Trade Avg.Losing.Trade Med.Losing.Trade Avg.Daily.PL Med.Daily.PL Std.Dev.Daily.PL Std.Err.Daily.PL Ann.Sharpe Max.Drawdown Profit.To.Max.Draw Avg.WinLoss.Ratio Med.WinLoss.Ratio Max.Equity Min.Equity
# SPY      5548.314      4063.635              NaN               NA     5548.314     4063.635         5460.273         2730.136   16.13047    -19148.87            1.28373                NA                NA   24946.23  -18349.48
# End.Equity
# SPY   24581.97

You can see in the output that at most 1000 units (long) ever exist in the strategy. And each trade is 25% of the current equity, when a long signal is fired.

FXQuantTrader
  • 6,821
  • 3
  • 36
  • 67
  • Quick question @FXQuantTrader ... Why did you extract Net.Trading.PL (in your osInvestAll function) and not Period.Realized.PL? Shouldn't we pick up Period.Realized.PL i.e. trading_pl <- sum(.getPortfolio(portfolio)$summary$Period.Realized.PL)? – Stat Aug 03 '20 at 18:32
  • 1
    net.trading.PL includes both unrealised and realised PL. I don't think it's realistically to consider your equity based on only what trades you've realised PnL on. Most traders/investors hold onto losses hoping they'll eventually at least break even, and these positions are often the ones that they end up margin calling on. i.e. I would never ignore unrealised PnL when determining your "actual" equity/capital – FXQuantTrader Aug 03 '20 at 20:07
  • Thank you so much. Greatly appreciated. – Stat Aug 03 '20 at 20:11
  • Sorry but another quick question @FXQuantTrader. The total cost for the first 5 buys adds up to about $123,712 which is higher than initEq=$100,000. How come that is possible? I mean after the first 4 buys, we shouldn't have enough capital to buy more. Is it possible to tweak osInvestAll function to account for such an adjustment? – Stat Jul 18 '21 at 22:10
  • @Stat Yes you can apply a capital constraint when computing the next order (size). Out of the box quantstrat does not restrict trades from capital (you can be trading on margin for example). It's been a while since your question; if you still want clarity on to do this, post a question with a quantstrat tag – FXQuantTrader Jan 12 '22 at 02:04