I would like to use indicators of timeframes different to the data I am using. I have seen this asked a few time but no solutions as of yet (at least for me anyway).
The below example uses daily stock data however the actual project uses intraday currency data. I have an easy work around for importing the intraday csv data now so the example and real-world should be interchangeable enough.
library(quantstrat)
initDate="2000-01-01"
from="2003-01-01"
to="2016-12-31"
#set account currency and system timezone
currency('USD')
Sys.setenv(TZ="UTC")
#get data
symbols <- "SPY"
getSymbols(symbols, from=from, to=to, src="yahoo", adjust=TRUE)
stock(symbols, "USD")
#trade sizing and initial equity settings
tradeSize <- 100000
initEq <- tradeSize*length(symbols)
#set up the portfolio, account and strategy
strategy.st <- portfolio.st <- account.st <- "mtf.strat"
rm.strat(strategy.st)
initPortf(portfolio.st, symbols=symbols, initDate=initDate, currency='USD')
initAcct(account.st, portfolios=portfolio.st, initDate=initDate, currency='USD',initEq=initEq)
initOrders(portfolio.st, initDate=initDate)
strategy(strategy.st, store=TRUE)
#SMA length
nSMA <- 14
Adding the SMA as, in this case a daily indicator works a treat
add.indicator(strategy.st, name="SMA",
arguments=list(x=quote(Cl(mktdata)), n=nSMA, maType = "SMA"),
label="SMA")
test <- applyIndicators(strategy.st, mktdata=OHLC(SPY))
Yet trying to add, in this case a weekly SMA
add.indicator(strategy.st, name="SMA",
arguments=list(x=quote(to.period(Cl(mktdata), period = "weeks", k = 1, indexAt = "startof")), n=nSMA, maType = "SMA"),
label="SMAw1")
## Or this
add.indicator(strategy.st, name="SMA",
arguments=list(x=quote(to.weekly(Cl(mktdata))), n=nSMA, maType = "SMA"),
label="SMAw1")
test <- applyIndicators(strategy.st, mktdata=OHLC(SPY))
# Error in runSum(x, n) : ncol(x) > 1. runSum only supports univariate 'x'
Calling the Close column directly without Cl(x) results in the same error. I did this as TTR:::runSum will throw the above error if given more than one column of data.
I'm not entirely sure what the problem is so some assistance would be great.
The problem is that to.period (and therefore to.weekly) return OHLC objects, not a univariate series like TTR::SMA expects. So you need to wrap the output of to.period in Cl.
add.indicator(strategy.st, name="SMA",
arguments=list(x=quote(Cl(to.weekly(Cl(mktdata)))), n=nSMA, maType = "SMA"),
label="SMAw1")
test <- applyIndicators(strategy.st, mktdata=OHLC(SPY))
Now that code runs, but it may still be a problem for your strategy. There will be a lot of NA when that indicator is merged with the daily mktdata.
R> tail(merge(SPY, test$SMA))
SPY.Open SPY.High SPY.Low SPY.Close SPY.Volume SPY.Adjusted SMA.SMAw1
2016-11-25 221.10 221.56 221.01 221.52 37861800 221.52 215.0720
2016-11-28 221.16 221.48 220.36 220.48 70284100 220.48 NA
2016-11-29 220.52 221.44 220.17 220.91 67079400 220.91 NA
2016-11-30 221.63 221.82 220.31 220.38 99783700 220.38 NA
2016-12-01 220.73 220.73 219.15 219.57 77230500 219.57 NA
2016-12-02 219.67 220.25 219.26 219.68 70863400 219.68 215.3207
So it's a good idea to create your own SMA wrapper function to handle all these steps. Then call add.indicator using your wrapper function.
mySMA <- function(x, on = "days", k = 1, n = 10) {
agg <- x[endpoints(x, on, k)]
sma <- SMA(agg, n)
# merge with zero-width xts object w/original index, filling NA
result <- merge(sma, xts(,index(x)), fill = na.locf)
return(result)
}
add.indicator(strategy.st, name = "mySMA",
arguments = list(x = quote(Cl(mktdata)),
on = "weeks",
n = nSMA),
label = "SMAw1")
test <- applyIndicators(strategy.st, mktdata = OHLC(SPY))
Now the indicator will have a value for every observation in mktdata when it's merged.
> tail(merge(SPY, test$SMA))
SPY.Open SPY.High SPY.Low SPY.Close SPY.Volume SPY.Adjusted SMA.SMAw1
2016-11-25 221.10 221.56 221.01 221.52 37861800 221.52 215.0720
2016-11-28 221.16 221.48 220.36 220.48 70284100 220.48 215.0720
2016-11-29 220.52 221.44 220.17 220.91 67079400 220.91 215.0720
2016-11-30 221.63 221.82 220.31 220.38 99783700 220.38 215.0720
2016-12-01 220.73 220.73 219.15 219.57 77230500 219.57 215.0720
2016-12-02 219.67 220.25 219.26 219.68 70863400 219.68 215.3207
Related
What is the correct method to have signals reference other signals? Is it not intended functionality? I cant seem to find a way to do it in my code.
library(PerformanceAnalytics)
library(quantmod)
library(lattice)
startDate <- '2010-01-01' # start of data
endDate <- '2015-05-01' # end of data
.blotter<-new.env()
.strategy<-new.env()
Sys.setenv(TZ="EST") # set time zone
symbols<-c("GOOG")
data<-getSymbols(symbols, from=startDate, to=endDate, index.class="POSIXct",env=NULL)
library(quantstrat)
initDate <- '2009-12-31'
initEq <- 1e6
currency("USD")
stock(symbols, currency="USD", multiplier=1)
rm.strat("multiAsset.bb1") # remove portfolio, account, orderbook if re-run
initPortf(name="multiAsset.bb1", symbols, initDate=initDate)
initAcct(name="multiAsset.bb1", portfolios="multiAsset.bb1",initDate=initDate, initEq=initEq)
initOrders(portfolio="multiAsset.bb1", initDate=initDate)
strategy("bbands", store=TRUE)
#Indicators are applied before signals and rules, and the output of indicators may be used as inputs to construct signals or fire rules
#mktdata is the time series object that holds the current symbols data during evaluation (pg 55)
add.indicator("bbands", name = "BBands",arguments = list(HLC = quote(HLC(mktdata)), maType='SMA'), label='bbInd')
test <- applyIndicators("bbands", mktdata=data)
head(test, 10)
add.signal("bbands", name="sigThreshold", arguments=list(columns=c("pctB.bbInd",".77"),relationship="gt"),label="H.gt.UpperBand")
add.signal("bbands", name="sigThreshold", arguments=list(columns=c("H.gt.UpperBand","0"),relationship="gt"),label="true.upper.band")
test <- applySignals("bbands", mktdata=test)
head(test, 10)
Error
Error in match.names(column, colnames(data)) :
argument "column" is missing, with no default
Note that this is a generalized example. It would be trivial to make the first signal an indicator and avoid this problem in this specific case.
You've passed the wrong arguments to sigThreshold. This corrected code works as expected, with the second signal using the H.gt.UpperBand column (singular) from the first signal. The missing arguments in your code for the sigThreshold function are column (singular) and threshold.
library(quantstrat)
startDate <- '2010-01-01' # start of data
endDate <- '2015-05-01' # end of data
Sys.setenv(TZ="EST") # set time zone
symbols<-c("GOOG")
data<-getSymbols.yahoo(symbols, from=startDate, to=endDate, index.class="POSIXct",auto.assign=FALSE)
initDate <- '2009-12-31'
initEq <- 1e6
currency("USD")
stock(symbols, currency="USD", multiplier=1)
rm.strat("multiAsset.bb1") # remove portfolio, account, orderbook if re-run
initPortf(name="multiAsset.bb1", symbols, initDate=initDate)
initAcct(name="multiAsset.bb1", portfolios="multiAsset.bb1",initDate=initDate, initEq=initEq)
initOrders(portfolio="multiAsset.bb1", initDate=initDate)
strategy("bbands", store=TRUE)
#Indicators are applied before signals and rules, and the output of indicators may be used as inputs to construct signals or fire rules
#mktdata is the time series object that holds the current symbols data during evaluation (pg 55)
add.indicator("bbands"
, name = "BBands"
, arguments = list(HLC = quote(HLC(mktdata))
, maType='SMA')
, label='bbInd')
test <- applyIndicators("bbands", mktdata=data)
head(test, 10)
add.signal("bbands"
, name="sigThreshold"
, arguments=list(column="pctB.bbInd"
, threshold=.77
, relationship="gt")
, label="H.gt.UpperBand")
add.signal("bbands"
, name="sigThreshold"
, arguments=list(column="H.gt.UpperBand"
, threshold=0
, relationship="gt")
,label="true.upper.band")
test <- applySignals("bbands", mktdata=test)
head(test, 10)
I am trying to tell whether a specific column value is greater than .77, but this code returns 1 if the value is greater then 0. Is there any way to use a known function (not a custom one) to do a simple greater then check?
library(PerformanceAnalytics)
library(quantmod)
library(lattice)
startDate <- '2010-01-01' # start of data
endDate <- '2015-05-01' # end of data
.blotter<-new.env()
.strategy<-new.env()
Sys.setenv(TZ="EST") # set time zone
symbols<-c("GOOG")
data<-getSymbols(symbols, from=startDate, to=endDate, index.class="POSIXct",env=NULL)
library(quantstrat)
initDate <- '2009-12-31'
initEq <- 1e6
currency("USD")
stock(symbols, currency="USD", multiplier=1)
rm.strat("multiAsset.bb1") # remove portfolio, account, orderbook if re-run
initPortf(name="multiAsset.bb1", symbols, initDate=initDate)
initAcct(name="multiAsset.bb1", portfolios="multiAsset.bb1",initDate=initDate, initEq=initEq)
initOrders(portfolio="multiAsset.bb1", initDate=initDate)
strategy("bbands", store=TRUE)
#Indicators are applied before signals and rules, and the output of indicators may be used as inputs to construct signals or fire rules
#mktdata is the time series object that holds the current symbols data during evaluation (pg 55)
add.indicator("bbands", name = "BBands",arguments = list(HLC = quote(HLC(mktdata)), maType='SMA'), label='bbInd')
test <- applyIndicators("bbands", mktdata=data)
head(test, 10)
add.signal("bbands", name="sigThreshold", arguments=list(columns=c("pctB.bbInd",".77"),relationship="gt"),label="H.gt.UpperBand")
test <- applySignals("bbands", mktdata=test) #DOESNT WORK
head(test, 10)
Error:
Error in match.names(column, colnames(data)) :
argument "column" is missing, with no default
My solution was to change:
add.signal("bbands", name="sigThreshold", arguments=list(columns=c("pctB.bbInd",".77"),relationship="gt"),label="H.gt.UpperBand")
to:
add.signal("bbands", name="sigThreshold", arguments=list(threshold=.77,column="pctB.bbInd",relationship="gt"),label="H.gt.UpperBand")
I am having this error while running a strategy back-testing in the R, using Quantstrat package. Whenever, I try to use applySignals function to test the signals, it shows the logical error. I tried to remove the NAs by na.omit(FB) command, but when you calculate Simple Moving Average, you will have the NAS in the beginning. Can somebody suggest me the solution?
Thanks,
require(PerformanceAnalytics)
require(quantstrat)
require(quantmod)
require(blotter)
initDate="2015-01-01"
from="2015-01-02"
to="2015-06-30"
options(width=100)
currency('USD')
Sys.setenv(TZ="UTC")
symbols = c("SPY", "FB", "TWTR")
getSymbols(symbols, from=from, to=to, src="yahoo", adjust=TRUE)
stock(symbols, currency="USD", multiplier=1)
suppressWarnings(rm("account.MAC","portfolio.MAC",pos=.blotter))
suppressWarnings(rm("order_book.MAC",pos=.strategy))
tradeSize <- 1000
initEq <- tradeSize
strategy.st <- portfolio.st <- account.st <- "MAC"
rm.strat(strategy.st)
initPortf(portfolio.st, symbols=symbols, initDate=initDate, currency='USD')
initAcct(account.st, portfolios=portfolio.st, initDate=initDate, currency='USD', initEq=initEq)
initOrders(portfolio.st, initDate=initDate)
strategy(strategy.st, store=TRUE)
#parameters
nFast = 10
nSlow = 30
#indicators
add.indicator(strategy.st, name="SMA",
arguments=list(x=quote(Cl(mktdata)[,1]), n=nFast),
label="nFast")
add.indicator(strategy.st, name="SMA",
arguments=list(x=quote(Cl(mktdata)[,1]), n=nSlow),
label="nSlow")
test <- applyIndicators(strategy.st, mktdata=Cl(FB))
head(test, 5)
#signals
add.signal(strategy.st, name="sigCrossover",
arguments=list(columns=c("nFast", "nSlow"), relationship="gt"),
label="longEntry")
add.signal(strategy.st, name="sigCrossover",
arguments=list(columns=c("nFast", "nSlow"), relationship="lt"),
label="longExit")
test2 <- applySignals(strategy.st, mktdata=Cl(FB))
Error: Error in if (length(j) == 0 || (length(j) == 1 && j == 0)) { :
missing value where TRUE/FALSE needed
I was able to solve the issue with my code.
So, if I only use mktdata object instead of mktdata = Cl(FB), Quantstrat is working fine. I am not able to understand fully why it is working that way, but somehow it worked fine.
test2 <- applySignals(strategy.st, mktdata)
I wrote the following codes and got an error message when apply the strategy:
Error in eval(expr, envir, enclos) : object 'Close' not found
sounds like the strategy can not find column "Close" price,
while the last line of code "head(mktdata)" clearly gives XLB.Close as column name of Close.
by the way, I intentionally left out the add.indicator() function which is not needed.
could anyone help? thanks
last line of code output has XLB.Close as column name:
head(mktdata)
XLB.Open XLB.High XLB.Low XLB.Close XLB.Volume XLB.Adjusted
2010-01-04 30.66197 31.06374 30.54327 31.06374 8287681 30.31
the strategy codes using quantstrat:
------------------------------------------------------------------------
library(quantstrat)
startDate <- '2010-01-01' # start of data
endDate <- '2013-07-31' # end of data
symbols = c("XLF", "XLP", "XLE", "XLY", "XLV", "XLI", "XLB", "XLK", "XLU")
Sys.setenv(TZ="UTC") # set time zone
getSymbols(symbols, src='yahoo', index.class=c("POSIXt","POSIXct"),
from=startDate, to=endDate, adjust=TRUE)
initDate <- '2009-12-31'
initEq <- 1e6
currency("USD")
stock(symbols, currency="USD",multiplier=1)
head(XLB)
Lowcut1<-1.001
Lowcut2<-1.002
rm.strat("multiINTRO") # remove portfolio, account, orderbook if re-run
initPortf(name="multiINTRO", symbols, initDate=initDate)
initAcct(name="multiINTRO", portfolios="multiINTRO",
initDate=initDate, initEq=initEq)
initOrders(portfolio="multiINTRO", initDate=initDate)
strategy("multiINTRO", store=TRUE)
summary(getStrategy("multiINTRO"))
add.signal("multiINTRO", name="sigFormula",
arguments=list(columns=c("Close","Low"),
formula="(Close > Lowcut1*Low) & (Close< Lowcut2*Low)",
cross=FALSE),store=TRUE,env=globalenv(),
label="longLowenter") ##Long entry
add.rule("multiINTRO", name="ruleSignal",
arguments=list(sigcol="longLowenter", sigval=TRUE, orderqty=100,
ordertype="market", orderside="long"), type="enter") ## Long enter
out<-try(applyStrategy("multiINTRO",portfolios="multiINTRO"))
head(mktdata)
head(mktdata) clearly gives "XLB.Close" as column name, and "XLB.Close" != "Close". Use quantmod's Cl and Lo column extractors on market data to get the columns you want.
Also, your formula argument is wrong because it's character string, not a formula. Your strategy runs for me if I change your add.signal call to:
add.signal("multiINTRO", name="sigFormula",
arguments=list(formula=longLowenter ~ Cl(mktdata) > Lowcut1*Lo(mktdata) & Cl(mktdata)< Lowcut2*Lo(mktdata), cross=FALSE), store=TRUE, env=globalenv(),
label="longLowenter") ##Long entry
I'm still playing around with Guy Yollins quantstrat example. In this example he buys 1000 shares of the SPY when it crosses its 10 day MA. Since we define an initial equity, is it possible to always buy for the whole portfolio amount and not just 900 shares? 'all' didn't work for the enter, just the exit..
if (!exists('.blotter')) .blotter <- new.env()
if (!exists('.strategy')) .strategy <- new.env()
if (!exists('.instrument')) .instrument <- new.env()
currency("USD")
stock("SPY",currency="USD",multiplier=1)
ls(envir=FinancialInstrument:::.instrument)
initDate <- '1997-12-31'
startDate <- '1998-01-01'
endDate <- '2013-07-31'
initEq <- 1e6
Sys.setenv(TZ="UTC")
getSymbols('SPY', from=startDate, to=endDate, adjust=T)
SPY=to.monthly(SPY, indexAt='endof')
SPY$SMA10m <- SMA(Cl(SPY), 10)
# inz portfolio, account
qs.strategy <- "qsFaber"
rm.strat(qs.strategy) # remove strategy etc. if this is a re-run
initPortf(qs.strategy,'SPY', initDate=initDate)
initAcct(qs.strategy,portfolios=qs.strategy, initDate=initDate, initEq=initEq)
initOrders(portfolio=qs.strategy,initDate=initDate)
# instantiate a new strategy object
strategy(qs.strategy,store=TRUE)
add.indicator(strategy = qs.strategy, name = "SMA",
arguments = list(x = quote(Cl(mktdata)), n=10), label="SMA10")
add.signal(qs.strategy,name="sigCrossover",
arguments = list(columns=c("Close","SMA10"),relationship="gt"),
label="Cl.gt.SMA")
add.signal(qs.strategy,name="sigCrossover",
arguments = list(columns=c("Close","SMA10"),relationship="lt"),
label="Cl.lt.SMA")
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="Cl.gt.SMA", sigval=TRUE, orderqty=900,
ordertype='market', orderside='long', pricemethod='market'),
type='enter', path.dep=TRUE)
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="Cl.lt.SMA", sigval=TRUE, orderqty='all',
ordertype='market', orderside='long', pricemethod='market'),
type='exit', path.dep=TRUE)
out <- applyStrategy(strategy=qs.strategy , portfolios=qs.strategy)
updatePortf(qs.strategy)
updateAcct(qs.strategy)
updateEndEq(qs.strategy)
myTheme<-chart_theme()
myTheme$col$dn.col<-'lightblue'
myTheme$col$dn.border <- 'lightgray'
myTheme$col$up.border <- 'lightgray'
# plot performance
chart.Posn(qs.strategy, Symbol = 'SPY', Dates = '1998::',theme=myTheme)
plot(add_SMA(n=10,col=4, on=1, lwd=2))
You cannot use orderqty="all" on entries because "all" refers to the current position size (i.e., when you want to exit the entire position).
It's possible to purchase an amount equal to the total available portfolio equity, but you have to define a custom order sizing function. And that function would necessarily have to mark the book (using updatePortf) in order to determine the amount of available equity.
Here is a toy example that achieves what you want.
You need to introduce an order sizing function.
Take a look at the arguments to the function ruleSignal (e.g. formals(ruleSignal) and ?ruleSignal).
You'll see there is an argument osFUN, which is where you can write your custom function that will determine how you order size.
You modify the appropriate parameters in add.rule to introduce ordersizing (on entry trades).
osFUN_all_eq <- 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))
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(equity / ClosePrice))
UnitSize <- osMaxPos(data, timestamp, UnitSize, ordertype, orderside, portfolio, symbol, ruletype, digits=0)
UnitSize
}
library(quantstrat)
currency("USD")
stock("SPY",currency="USD",multiplier=1)
initDate <- '1997-12-31'
startDate <- '1998-01-01'
endDate <- '2013-07-31'
initEq <- 1e6
Sys.setenv(TZ="UTC")
getSymbols('SPY', from=startDate, to=endDate, adjust=T)
SPY=to.monthly(SPY, indexAt='endof')
SPY$SMA10m <- SMA(Cl(SPY), 10)
qs.strategy <- "qsFaber"
rm.strat(qs.strategy) # remove strategy etc. if this is a re-run
initPortf(qs.strategy,'SPY', initDate=initDate)
initAcct(qs.strategy,portfolios=qs.strategy, initDate=initDate, initEq=initEq)
initOrders(portfolio=qs.strategy,initDate=initDate)
# instantiate a new strategy object
strategy(qs.strategy,store=TRUE)
# Specify the max quantity you could hold in the SPY instrument. Here we simply assume 1e5 units. You could reduce this number to limit the exposure
max_qty_traded <- 1e5
addPosLimit(qs.strategy, "SPY", timestamp = startDate, maxpos = max_qty_traded)
add.indicator(strategy = qs.strategy, name = "SMA",
arguments = list(x = quote(Cl(mktdata)), n=10), label="SMA10")
add.signal(qs.strategy,name="sigCrossover",
arguments = list(columns=c("Close","SMA10"),relationship="gt"),
label="Cl.gt.SMA")
add.signal(qs.strategy,name="sigCrossover",
arguments = list(columns=c("Close","SMA10"),relationship="lt"),
label="Cl.lt.SMA")
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="Cl.gt.SMA",
sigval=TRUE,
orderqty = 1, # the acutal orderqty size becomes redundant when supplying a function to the argument `osFUN`
osFUN = osFUN_all_eq,
ordertype='market', orderside='long', pricemethod='market'),
type='enter', path.dep=TRUE)
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="Cl.lt.SMA",
sigval=TRUE,
orderqty='all', # flatten all open long positions
ordertype='market',
orderside='long',
pricemethod='market'),
type='exit',
path.dep=TRUE)
# supply initEq parameter and its value, which pass through to `osFUN`
out <- applyStrategy(strategy=qs.strategy , portfolios=qs.strategy, initEq=initEq)
updatePortf(qs.strategy)
updateAcct(qs.strategy)
updateEndEq(qs.strategy)
myTheme<-chart_theme()
myTheme$col$dn.col<-'lightblue'
myTheme$col$dn.border <- 'lightgray'
myTheme$col$up.border <- 'lightgray'
# plot performance
chart.Posn(qs.strategy, Symbol = 'SPY', Dates = '1998::',theme=myTheme)
plot(add_SMA(n=10,col=4, on=1, lwd=2))
There are a few things you should consider in an order sizing function:
1) If you already have a position open, do you want to permit "stacking"/pyramidying of positions on a particular side? For example, if you want to just have one position on, which you don't contribute further to, you could include getPosQty(qs.strategy, "SPY", timestamp) in your osFUN and return 0 if the current position held is not 0.
2) Do you want a max trade size? This can be handled using addPosLimit() as was done in this example above.
I know this question was posted long ago, but check out package "IKTrading," and the order sizing function "osMaxDollar." Here's a blog post on the topic; when you use this order sizing function you can set the dollar value of each trade and the total position.
https://quantstrattrader.wordpress.com/2014/08/29/comparing-atr-order-sizing-to-max-dollar-order-sizing/