Reproducible code:
package <- c("compiler",
"quantmod",
"dygraphs",
"plyr",
"devtools",
"PerformanceAnalytics",
"doParallel")
lapply(X = package,
FUN = function(this.package){
if (!require(package = this.package,
character.only = TRUE))
{
install.packages(pkgs = this.package,
repos = "https://cloud.r-project.org")
library(package = this.package,
character.only = TRUE)
} else {
library(package = this.package,
character.only = TRUE)
}
})
install_github("braverock/FinancialInstrument")
install_github("braverock/blotter")
install_github("braverock/quantstrat")
install_github("braverock/PerformanceAnalytics")
require(quantstrat)
# +------------------------------------------------------------------
# | The registerDoParallel() function is used to register the
# | parallel backend with the foreach package. detectCores() attempts
# | to detect the number of CPU cores on the current host.
# +------------------------------------------------------------------
registerDoParallel(detectCores())
# +------------------------------------------------------------------
# | Get data.
# +------------------------------------------------------------------
symbols <- c('SPY', 'TLT', 'GLD')
getSymbols(Symbols = symbols)
# +------------------------------------------------------------------
# | osTotEq() is an order sizing function which should return the
# | maximum amount of purchasable securities as for current equity
# | available (assuming you're not invested at the moment of
# | calculation).
# +------------------------------------------------------------------
osTotEq <- function(timestamp, orderqty, portfolio, symbol, ruletype, ...)
{
if (orderqty == "all" && !(ruletype %in% c("exit", "risk")) ||
orderqty == "trigger" && ruletype != "chain")
{
stop(paste("orderqty 'all'/'trigger' would produce nonsense, maybe use osMaxPos instead?\n",
"Order Details:\n", "Timestamp:", timestamp, "Qty:",
orderqty, "Symbol:", symbol))
}
endEq <- getEndEq(Account = portfolio,
Date = timestamp)
refPrice <- Cl(mktdata[, 1:4])[timestamp, ]
orderqty <- floor(endEq / refPrice)
return(orderqty)
}
# +------------------------------------------------------------------+ #
# +------------------------------------------------------------------+ #
# | Main: William's %R | #
# +------------------------------------------------------------------+ #
# +------------------------------------------------------------------+ #
# +------------------------------------------------------------------
# | Parameters
# +------------------------------------------------------------------
name <- 'WPR'
currency <- 'USD'
initEq <- 300000
# +------------------------------------------------------------------
# | Initialization
# +------------------------------------------------------------------
rm.strat(name = name)
currency(currency)
for (symbol in symbols)
{
stock(primary_id = symbol,
currency = currency,
multiplier = 1)
}
initPortf(name = name,
symbols = symbols,
currency = currency)
initAcct(name = name,
portfolios = name,
initEq = initEq)
initOrders(portfolio = name)
strategy(name = name,
store = TRUE)
# +------------------------------------------------------------------
# | Indicators
# +------------------------------------------------------------------
add.indicator(strategy = name,
name = 'volatility',
arguments = list(OHLC = quote(OHLC(mktdata)),
n = 5,
calc = 'yang.zhang',
N = 1),
label = 'sigma',
store = TRUE)
add.indicator(strategy = name,
name = 'WPR',
arguments = list(HLC = quote(HLC(mktdata)),
n = 14),
label = 'wpr',
store = TRUE)
# +------------------------------------------------------------------
# | Signals
# +------------------------------------------------------------------
add.signal(strategy = name,
name = 'sigThreshold',
arguments = list(column = 'wpr',
threshold = .2,
relationship = 'gt',
cross = TRUE),
label = 'wpr.buy')
add.signal(strategy = name,
name = 'sigThreshold',
arguments = list(column = 'wpr',
threshold = .8,
relationship = 'lt',
cross = TRUE),
label = 'wpr.sell')
# +------------------------------------------------------------------
# | Rules
# +------------------------------------------------------------------
add.rule(strategy = name,
name = 'ruleSignal',
arguments = list(sigcol = 'wpr.buy',
sigval = TRUE,
orderqty = 1,
ordertype = 'market',
orderside = 'long',
osFUN = osTotEq),
type = 'enter',
label = 'wpr.buy.enter',
store = TRUE)
add.rule(strategy = name,
name = 'ruleSignal',
arguments = list(sigcol = 'wpr.buy',
sigval = TRUE,
orderqty = 'all',
ordertype = 'stoplimit',
orderside = 'long',
tmult = TRUE,
threshold = quote(mktdata[timestamp, 'X1.sigma']),
orderset = 'stop.loss'),
parent = 'wpr.buy.enter',
type = 'chain',
label = 'wpr.buy.chain',
store = TRUE)
# add.rule(strategy = name,
# name = 'ruleSignal',
# arguments = list(sigcol = 'wpr.sell',
# sigval = TRUE,
# orderqty = 'all',
# ordertype = 'market',
# orderside = 'long',
# pricemethod = 'market',
# replace = TRUE,
# osFUN = osNoOp),
# path.dep = TRUE,
# type = 'exit',
# label = 'wpr.buy.exit',
# store = TRUE)
# +------------------------------------------------------------------
# | Strategy backtest
# +------------------------------------------------------------------
try(applyStrategy(strategy = name,
portfolios = name))
updatePortf(Portfolio = name,
Dates = paste('::',as.Date(Sys.time()), sep = ''))
updateAcct(name = name)
updateEndEq(Account = name)
# +------------------------------------------------------------------
# | Performance analysis
# +------------------------------------------------------------------
for (symbol in symbols)
{
dev.new()
chart.Posn(Portfolio = name,
Symbol = symbol)
}
dev.new()
R <- PortfReturns(Account = name)
R$total.DailyEqPl <- rowSums(R)
charts.PerformanceSummary(R = R,
ylog = TRUE,
main = "Smoothing spline performance",
geometric = TRUE)
getOrderBook(portfolio = name)
I've intentionally left the exit rules commented in order to better highlight the usage of stoplimit orders. As for the order book, it seems that they're not working. This is not the first time I cannot make stoplimit orders work properly. Even if I copy demo codes (and slightly amend it for my purposes), sometimes it doesn't work and I'm not getting why. Above there's an example of "dynamic" stoplimit price, but in my order book I see no stoplimit orders even if I use a fixed value (like quote(0.001)
). Nonetheless, orderset
, parent
and type
seem ok.
Could you please explain what I'm missing?
Here's a snippet of my getOrderBook(portfolio = name)
:
Order.Qty Order.Price Order.Type Order.Side Order.Threshold Order.Status Order.StatusTime Prefer Order.Set Txn.Fees Rule Time.In.Force
2002-08-15 "3487" "86.02" "market" "long" NA "closed" "2002-08-16 00:00:00" "" NA "0" "wpr.buy.enter" ""
2002-09-06 "3430" "87.46" "market" "long" NA "closed" "2002-09-09 00:00:00" "" NA "0" "wpr.buy.enter" ""
2002-09-25 "3348" "89.58" "market" "long" NA "closed" "2002-09-26 00:00:00" "" NA "0" "wpr.buy.enter" ""
2002-10-01 "3373" "88.94" "market" "long" NA "closed" "2002-10-02 00:00:00" "" NA "0" "wpr.buy.enter" ""
2002-11-14 "3457" "86.76" "market" "long" NA "closed" "2002-11-15 00:00:00" "" NA "0" "wpr.buy.enter" ""
2002-12-13 "3467" "86.53" "market" "long" NA "closed" "2002-12-16 00:00:00" "" NA "0" "wpr.buy.enter" ""
2002-12-31 "3387" "88.57" "market" "long" NA "closed" "2003-01-02 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-01-27 "3422" "87.65" "market" "long" NA "closed" "2003-01-28 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-02-05 "3428" "87.51" "market" "long" NA "closed" "2003-02-06 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-02-10 "3416" "87.8" "market" "long" NA "closed" "2003-02-11 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-02-21 "3397" "88.3" "market" "long" NA "closed" "2003-02-24 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-03-13 "3351" "89.52" "market" "long" NA "closed" "2003-03-14 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-05-02 "3396" "88.32" "market" "long" NA "closed" "2003-05-05 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-05-27 "3187" "94.12" "market" "long" NA "closed" "2003-05-28 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-06-11 "3132" "95.78" "market" "long" NA "closed" "2003-06-12 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-06-16 "3116" "96.26" "market" "long" NA "closed" "2003-06-17 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-09-02 "3655" "82.06" "market" "long" NA "closed" "2003-09-03 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-09-08 "3605" "83.2" "market" "long" NA "closed" "2003-09-09 00:00:00" "" NA "0" "wpr.buy.enter" ""
2003-09-11 "3567" "84.1" "market" "long" NA "closed" "2003-09-12 00:00:00" "" NA "0" "wpr.buy.enter" ""
As you can see, no stoplimit order is present at all.
I suspect your problem will be solved by simply adding Sys.setenv(TZ ="UTC")
at the top of your script (many of the quantstrat demos on daily data do this). This sometimes happens when you're working with daily data in quantstrat.
What's happening is in ruleSignal
quantstrat isn't correctly getting the chain.price
at the time a market order is filled, and when it can't find a chain price, it ignores creating the stop loss.
If you're curious and want to convince yourself, try setting the debugger in ruleSignal
under a condition where a stoploss is being filled (you could create your own ruleSignal function very similar to ruleSignal
and supply that function's name in add.rule
name
argument for the stoplimit rule.
If you set the debugger to pause on the first order fill (in ruleSignal
or an equivalent when the chain stoplimit rule triggered), you'll see this:
getTxns(portfolio, "SPY")
Txn.Qty Txn.Price Txn.Fees Txn.Value Txn.Avg.Cost Net.Txn.Realized.PL
1950-01-01 00:00:00 0 0.00 0 0.0 0.00 0
2007-01-25 19:00:00 2108 142.13 0 299610.1 142.13 0
For things to work correctly, that first txn timestamp should be 2007-01-26
.
The chain.price
being supplied to ruleSignal
for the stoplimit is an empty xts object, which arises because in quantstrat's function applyRules
, these lines in the switch chain chain
case don't work correctly on the daily data:
txns <- getTxns(Portfolio=portfolio, Symbol=symbol, Dates=timestamp)
txn.price <- last(txns$Txn.Price)
ruleProc(rules[j], timestamp=timestamp, path.dep=path.dep, mktdata=mktdata, portfolio=portfolio, symbol=symbol, ruletype=type, mktinstr=mktinstr, parameters=list(chain.price=txn.price), curIndex=curIndex)
Specifically, the parameter
argument in ruleProc
isn't passing in a valid chain.price
. In applyRules
, getTxns
is supplied Dates = timestamp
and timestamp = "2007-01-26"
, (the transaction is filled at 2007-01-26, and the entry signal was on 2007-01-25) which is after "2007-01-25 19:00:00", so txns
is an empty xts object. Why does the transaction on day "2007-01-26" have a timestamp of "2007-01-25 19:00:00"?. Because in my R system environment, timezone corresponds to UTC midnight in New York time.
In short, it's a bug (if you don't set the time zone to UTC for daily data sets), but since the authors and most serious users of quantstrat probably don't work with daily bars, it's probably not a big priority to fix. Just be sure to set the timezone to UTC
when working with xts objects that have Date
time time indices as opposed to POSIXct
type.