==================================================================================================================================

Motivational Buckets

The objective is to duplicate the MTA 2014 Charles Dow Award Winner’s Beta Rotation Strategy (BRS) by Pension Partners (Bilello C.V. & Gayed M.A.). When the price ratio (or relative strength) of the Utilities sector to the broad market is positive over the four(4)-week period, position into Utilities for the following week. When the ratio (or the relative strength) is negative, position into the broad market for the following week. This strategy uses TWO (2) ETFS with the best approximation of the Fama-French data set for the Utilities sector and broad market, namely Utilities Select Sector SPDR Fund (“XLU”) and the Vanguard Total Stock Market ETF (“VTI”), respectively. URL: www.ssrn.com/abstract=2417974

set user.strategyName - strategy name, e.g. “BetaRotation”

set user.initDate - set initDate prior to the first close price given using the format “YYYY-MM-DD”, e.g. “2005-01-01”

set user.endDate - set endDate using the format “YYYY-MM-DD”, e.g. as.character(Sys.Date())

set user.tradeSize - set trade size in units, e.g. 100

set user.initEq - set initial equity or starting capital (default=100000)

set user.sym - set character vector of stock symbols

Example

> install.packages(“FinancialInstrument”, dependencies = TRUE)

> install.packages(“quantstrat”, repos=“http://R-Forge.R-project.org”, dependencies = TRUE)

> install.packages(“devtools”, dependencies = TRUE)

> devtools::install_github(repo=“IKTrading”, username=“IlyaKipnis”)

1.1) (size: 44) Set user inputs and fetch prices of user symbols. Create new symbol RTO = XLU / VTI.

1.2) (size: 7) Create four objects: (i) portfolio, (ii) account, (iii) orders, and (iv) strategy.

1.3) (size: 9) Create an indicator function dbsXts() that takes in some variant of OHLC and returns an Xts object.

1.4) (size: 5) Use the function applyIndicators() to see how the indicators will be appended to the mktdata object.

1.5) (size: 2) Plot a chart of S&P 500 with the DBS indicator.

1.6) (size: 14) Use the function applySignals() to see how the signals will be appended to the mktdata object.

1.7) (size: 23) Use the function applyRules(), instead of applyStrategy(), to backtest a different symbol that is not associated with portfolio.name. Call the three update functions as the final step to creating a backtesting result.

1.8) (size: 4) Displays a chart of SPY, the holding positions, the cumulative P&L and drawdown during backtesting.

==================================================================================================================================

1.1 (size: 44) The initial equity or starting capital is 100000 unless otherwise given. In this case, the initial equity is the number of symbols times 100000. The function stock() from package FinancialInstrument defines each symbol as a stock currency instrument. Create a new symbol by XLU divide by VTI.

if( Sys.info()["sysname"] == "Linux" )
  source("~/100 FxOption/103 FxOptionVerBack/081 Fx Bit/R-source/PlusReg.R", echo=FALSE)
if( Sys.info()["sysname"] == "Windows" )
{
  if( Sys.info()["nodename"] == "GARYLEE-PC" )
  {
    source("D:/denbrige/100 FxOption/103 FxOptionVerBack/081 Fx Bit/R-source/PlusReg.R", echo=FALSE)
    RegHomeDir <- function()
    {
      retDir <- NULL
      if( RegIsLinuxBln() )
        retDir <- paste0("/home/",Sys.info()["user"],"/")
      if( RegIsWindowsBln() )
        retDir <- paste0("D:/",Sys.info()["user"],"/")
      retDir
    }
  }
  else
  {
    source("C:/Users/denbrige/100 FxOption/103 FxOptionVerBack/081 Fx Bit/R-source/PlusReg.R", echo=FALSE)
  }
}
suppressPackageStartupMessages(source(paste0(RegRSourceDir(),"PlusAddThat.R"), echo=FALSE))
suppressPackageStartupMessages(source(paste0(RegRSourceDir(),"PlusDbs.R"), echo=FALSE))
suppressPackageStartupMessages(require( quantmod ))
suppressPackageStartupMessages(require( quantstrat ))
suppressPackageStartupMessages(require( PerformanceAnalytics ))
Sys.setenv(TZ="UTC")
invisible( currency('USD') )
options(width=120)
user.strategyName   <- "BetaRotation"
user.initDate       <- "2005-01-01"
user.endDate        <- as.character(Sys.Date())
user.tradeSize      <- 100
user.sym            <- c("SPY") #S&P500
user.initEq         <- user.tradeSize * length(user.sym)
dbs.sym             <- c("XLU", #Utilities Select Sector SPDR Fund
                         "VTI", #Vanguard Total Stock Market ETF 
                         user.sym)
suppressWarnings(suppressMessages(getSymbols(dbs.sym, from=user.initDate, to=user.endDate, src="yahoo", adjust=TRUE)))
## [1] "XLU" "VTI" "SPY"
RTO <- XLU / VTI
names(RTO) <- c("RTO.Open", "RTO.High", "RTO.Low", "RTO.Close", "RTO.Volume", "RTO.Adjusted")
portfolio.sym       <- c("RTO", user.sym)
stock(portfolio.sym, currency="USD", multiplier=1)
## [1] "RTO" "SPY"
head(RTO)
##             RTO.Open  RTO.High   RTO.Low RTO.Close RTO.Volume RTO.Adjusted
## 2005-01-03 0.3886388 0.3919688 0.3904092 0.3901185   3.859810    0.3901185
## 2005-01-04 0.3886984 0.3908185 0.3933024 0.3926524   5.272727    0.3926524
## 2005-01-05 0.3931402 0.3924621 0.3891692 0.3891692   3.760242    0.3891692
## 2005-01-06 0.3881458 0.3891317 0.3889568 0.3893292   3.826561    0.3893292
## 2005-01-07 0.3914953 0.3926185 0.3910580 0.3905816   1.356195    0.3905816
## 2005-01-10 0.3891558 0.3902384 0.3892234 0.3910392   3.535091    0.3910392

==================================================================================================================================

1.2 (size: 7) Create four objects: (i) portfolio, (ii) account, (iii) orders, and (iv) strategy. The portfolio consists of user-defined symbols to which the strategy will be applied. Set store=TRUE to store the strategy in the .strategy environment, instead of return it. Set indicator parameters.

strategy.name <- portfolio.name <- account.name <- user.strategyName
invisible( initPortf(portfolio.name, symbols=portfolio.sym, initDate=user.initDate, currency='USD') )
invisible( initAcct(account.name, portfolios=portfolio.name, initDate=user.initDate, currency='USD', initEq=user.initEq) )
initOrders(portfolio.name, initDate=user.initDate)
strategy(strategy.name, store=TRUE)
user.dbs.periodNum <- 20
user.dbs.threshold <- 0.0

==================================================================================================================================

1.3 (size: 9) Create an indicator function dbsXts() that takes in some variant of an OHLC object (whether one column-most likely Close, HLC, or whatever else) and a period (default=20). In theory, you could replace the function dbsXts() with TTR::ROC() as both functions perform the same transformation. In the latter case, add.indicator( strategy.name, name=“ROC”, arguments=list( OHLC=quote(OHLC(mktdata)), n=user.dbs.periodNum, type=“discrete”), label=“roc” ).

dbsXts <- function(OHLC, periodNum=20) 
{
  retXts <- merge.xts( ROC(Op(OHLC), periodNum, type="discrete"),
                       ROC(Hi(OHLC), periodNum, type="discrete"),
                       ROC(Lo(OHLC), periodNum, type="discrete"),
                       ROC(Cl(OHLC), periodNum, type="discrete") )
  names(retXts) <- c("Open", "High", "Low", "Close")
  retXts
}

==================================================================================================================================

1.4 (size: 5) The function applyIndicators() allows a user to see how the indicators will be appended to the mktdata object in the backtest. If the function call fails, it means that there most likely is an issue with column naming.

invisible( add.indicator( strategy.name, name="dbsXts", 
                          arguments=list(OHLC=quote(OHLC(mktdata)), n=user.dbs.periodNum), 
                          label="dbs" ) )
test <- applyIndicators( strategy.name, mktdata=OHLC(RTO) )
head(test, 25)
##             RTO.Open  RTO.High   RTO.Low RTO.Close   Open.dbs   High.dbs    Low.dbs  Close.dbs
## 2005-01-03 0.3886388 0.3919688 0.3904092 0.3901185         NA         NA         NA         NA
## 2005-01-04 0.3886984 0.3908185 0.3933024 0.3926524         NA         NA         NA         NA
## 2005-01-05 0.3931402 0.3924621 0.3891692 0.3891692         NA         NA         NA         NA
## 2005-01-06 0.3881458 0.3891317 0.3889568 0.3893292         NA         NA         NA         NA
## 2005-01-07 0.3914953 0.3926185 0.3910580 0.3905816         NA         NA         NA         NA
## 2005-01-10 0.3891558 0.3902384 0.3892234 0.3910392         NA         NA         NA         NA
## 2005-01-11 0.3903533 0.3919377 0.3906917 0.3922665         NA         NA         NA         NA
## 2005-01-12 0.3914231 0.3924039 0.3924738 0.3916837         NA         NA         NA         NA
## 2005-01-13 0.3938684 0.3968288 0.3957823 0.3966528         NA         NA         NA         NA
## 2005-01-14 0.3984767 0.3981543 0.3959336 0.3982167         NA         NA         NA         NA
## 2005-01-18 0.3973556 0.3972042 0.3963261 0.3973628         NA         NA         NA         NA
## 2005-01-19 0.3991075 0.3985925 0.3992216 0.3992216         NA         NA         NA         NA
## 2005-01-20 0.3975794 0.4011823 0.3997392 0.4003577         NA         NA         NA         NA
## 2005-01-21 0.3984474 0.4003745 0.4011049 0.4008279         NA         NA         NA         NA
## 2005-01-24 0.4025037 0.4049460 0.4034509 0.4065085         NA         NA         NA         NA
## 2005-01-25 0.4054375 0.4054533 0.4035745 0.4031286         NA         NA         NA         NA
## 2005-01-26 0.4024419 0.4064128 0.4024549 0.4064748         NA         NA         NA         NA
## 2005-01-27 0.4085707 0.4076290 0.4052445 0.4079392         NA         NA         NA         NA
## 2005-01-28 0.4053252 0.4081251 0.4076980 0.4098802         NA         NA         NA         NA
## 2005-01-31 0.4087238 0.4102954 0.4080662 0.4103668         NA         NA         NA         NA
## 2005-02-01 0.4112325 0.4111180 0.4096789 0.4107884 0.05813533 0.04885375 0.04935765 0.05298384
## 2005-02-02 0.4133393 0.4115653 0.4105047 0.4104872 0.06339345 0.05308543 0.04373826 0.04542123
## 2005-02-03 0.4133393 0.4130189 0.4099221 0.4136933 0.05137887 0.05237902 0.05332633 0.06301673
## 2005-02-04 0.4153796 0.4133237 0.4139472 0.4125424 0.07016389 0.06216923 0.06424995 0.05962356
## 2005-02-07 0.4148465 0.4147766 0.4120039 0.4118676 0.05964624 0.05643667 0.05356218 0.05449819

==================================================================================================================================

1.5) (size: 2) Plot a chart of S&P 500 with the DBS indicator.

barChart(OHLC(SPY), theme="white", name="S&P 500")

addTA(Cl(test)[,2], legend="Price Ratio Utilities/Market (Neg=Long Market; Pos=Long Utilities)")

==================================================================================================================================

1.6) (size: 14) The function add.signal() allows a user to define the relationship between two columns (or between the first column and a threshold value). The cross argument determines whether the signal should return TRUE for the entire duration of the relationship being true (cross=FALSE), or only on the first day (cross=TRUE). In other words, if you set cross=TRUE, the signal should return true only on the first day, otherwise if you set cross=FALSE, the signal should return TRUE for the entire period in which the condition is met. Relationships are specified with a two or three character identifier, such as ‘gt’, ‘lt’, ‘gte’, ‘lte’, ‘eq’. The two signals ‘sigDbsAboveThreshold’ and ‘sigDbsBelowThreshold’ are best used for setting up filters, i.e. we will look for a long entry only when there is a bullish signal and vice-versa. These signals should return TRUE for the entire duration in which the condition is met, and FALSE otherwise. The other two signals ‘sigDbsCrossUp’ and ‘sigDbsCrossDn’ are useful for setting up buy or sell orders. We want to integrate AsiaChart’s RSI Pop strategy here. For example, we want to enter a position only on the second cross signal. The first cross signal setups the entry criteria whether to look for a long/short entry in the future.

invisible( add.signal( strategy.name, name="sigThreshold",
                      arguments=list(column="Close.dbs", threshold=user.dbs.threshold, relationship="gt", cross=FALSE),
                      label="bearish_or_exit_long") )
invisible( add.signal( strategy.name, name="sigThreshold",
                      arguments=list(column="Close.dbs", threshold=-user.dbs.threshold, relationship="lt", cross=FALSE),
                      label="bullish_or_exit_short") )
invisible( add.signal( strategy.name, name="sigThreshold",
                      arguments=list(column="Close.dbs", threshold=user.dbs.threshold, relationship="gt", cross=TRUE),
                      label="short") )
invisible( add.signal( strategy.name, name="sigThreshold",
                      arguments=list(column="Close.dbs", threshold=-user.dbs.threshold, relationship="lt", cross=TRUE),
                      label="long") )
tst2 <- applySignals( strategy.name, mktdata=test )
spyXts <- merge.xts(SPY[,1:4], tst2[,5:12])

==================================================================================================================================

1.7 (size: 21) The function applyRules(), unlike applySignals() and applyIndicators(), does not return the mktdata object. The reason we use the function applyRules(), instead of applyStrategy(), is because we want to use a different symbol that is not associated with portfolio.name. The three update functions are the final step to creating a backtesting result.

invisible( add.rule(strategy = strategy.name,
                    name='ruleSignal',
                    arguments=list(sigcol='long',
                                   sigval=TRUE,
                                   orderqty=user.tradeSize,
                                   ordertype='market',
                                   orderside='long'),
                    type='enter') )
invisible( add.rule(strategy = strategy.name,
                    name='ruleSignal',
                    arguments=list(sigcol='short',
                                   sigval=TRUE,
                                   orderqty='all',
                                   ordertype='market',
                                   orderside='long'),
                    type='exit') )
invisible( applyRules( portfolio.name, symbol=user.sym, strategy.name, mktdata=spyXts, verbose=FALSE ) )
ob <- getOrderBook(portfolio.name)
invisible( updatePortf(portfolio.name) )
invisible( updateAcct(portfolio.name) )
invisible( updateEndEq(portfolio.name) )

==================================================================================================================================

1.8 (size: 6) The function chart.Posn() displays a chart of the user symbol, the holding positions, the cumulative P&L and drawdown. We obtain the equity curve by calling getAccount(), which returns an account object. We plot the equity curve by passing a\(summary\)End.Eq as input to the function plot(). The Sharpe ratio and other performance metrics can be obtained by passing the strategy name as input to the function tradeStats().

chart.Posn(strategy.name, Symbol=user.sym)

a <- getAccount(portfolio.name)
equity <- a$summary$End.Eq
plot(equity, main="Beta Rotation Strategy Equity Curve")

tstats <- t(tradeStats(strategy.name))
tstats
##                    SPY           
## Portfolio          "BetaRotation"
## Symbol             "SPY"         
## Num.Txns           "236"         
## Num.Trades         "117"         
## Net.Trading.PL     "4015.11"     
## Avg.Trade.PL       "34.31718"    
## Med.Trade.PL       "-11.89852"   
## Largest.Winner     "1888.305"    
## Largest.Loser      "-848.6655"   
## Gross.Profits      "16674.01"    
## Gross.Losses       "-12658.9"    
## Std.Dev.Trade.PL   "366.3552"    
## Percent.Positive   "46.15385"    
## Percent.Negative   "53.84615"    
## Profit.Factor      "1.317177"    
## Avg.Win.Trade      "308.7779"    
## Med.Win.Trade      "209.6551"    
## Avg.Losing.Trade   "-200.9348"   
## Med.Losing.Trade   "-151.9314"   
## Avg.Daily.PL       "34.31718"    
## Med.Daily.PL       "-11.89852"   
## Std.Dev.Daily.PL   "366.3552"    
## Ann.Sharpe         "1.486995"    
## Max.Drawdown       "-3911.967"   
## Profit.To.Max.Draw "1.026366"    
## Avg.WinLoss.Ratio  "1.536707"    
## Med.WinLoss.Ratio  "1.379933"    
## Max.Equity         "5415.922"    
## Min.Equity         "-1584.868"   
## End.Equity         "4015.11"

==================================================================================================================================